diff --git a/framework/core/src/Extend/Conditional.php b/framework/core/src/Extend/Conditional.php index b9bbc764af..26202666c2 100644 --- a/framework/core/src/Extend/Conditional.php +++ b/framework/core/src/Extend/Conditional.php @@ -13,17 +13,36 @@ use Flarum\Extension\ExtensionManager; use Illuminate\Contracts\Container\Container; +/** + * The Conditional extender allows developers to conditionally apply other extenders + * based on either boolean values or results from callable functions. + * + * This is useful for applying extenders only if certain conditions are met, + * such as the presence of an enabled extension or a specific configuration setting. + * + * @package Flarum\Extend + */ class Conditional implements ExtenderInterface { /** + * An array of conditions and their associated extenders. + * + * Each entry should have: + * - 'condition': a boolean or callable that should return a boolean. + * - 'extenders': an array of extenders, a callable returning an array of extenders, or an invokable class string. + * * @var array */ protected $conditions = []; /** - * @param ExtenderInterface[] $extenders + * Apply extenders only if a specific extension is enabled. + * + * @param string $extensionId The ID of the extension. + * @param ExtenderInterface[]|callable|string $extenders An array of extenders, a callable returning an array of extenders, or an invokable class string. + * @return self */ - public function whenExtensionEnabled(string $extensionId, array $extenders): self + public function whenExtensionEnabled(string $extensionId, $extenders): self { return $this->when(function (ExtensionManager $extensions) use ($extensionId) { return $extensions->isEnabled($extensionId); @@ -31,10 +50,14 @@ public function whenExtensionEnabled(string $extensionId, array $extenders): sel } /** - * @param bool|callable $condition - * @param ExtenderInterface[] $extenders + * Apply extenders based on a condition. + * + * @param bool|callable $condition A boolean or callable that should return a boolean. + * If this evaluates to true, the extenders will be applied. + * @param ExtenderInterface[]|callable|string $extenders An array of extenders, a callable returning an array of extenders, or an invokable class string. + * @return self */ - public function when($condition, array $extenders): self + public function when($condition, $extenders): self { $this->conditions[] = [ 'condition' => $condition, @@ -44,6 +67,13 @@ public function when($condition, array $extenders): self return $this; } + /** + * Iterates over the conditions and applies the associated extenders if the conditions are met. + * + * @param Container $container + * @param Extension|null $extension + * @return void + */ public function extend(Container $container, Extension $extension = null) { foreach ($this->conditions as $condition) { @@ -52,7 +82,15 @@ public function extend(Container $container, Extension $extension = null) } if ($condition['condition']) { - foreach ($condition['extenders'] as $extender) { + $extenders = $condition['extenders']; + + if (is_string($extenders)) { + $extenders = $container->call($extenders); + } elseif (is_callable($extenders)) { + $extenders = $container->call($extenders); + } + + foreach ($extenders as $extender) { $extender->extend($container, $extension); } } diff --git a/framework/core/tests/integration/extenders/ConditionalTest.php b/framework/core/tests/integration/extenders/ConditionalTest.php index e68472d2c5..ce5291b80b 100644 --- a/framework/core/tests/integration/extenders/ConditionalTest.php +++ b/framework/core/tests/integration/extenders/ConditionalTest.php @@ -12,12 +12,9 @@ use Exception; use Flarum\Api\Serializer\ForumSerializer; use Flarum\Extend; -use Flarum\Extend\ExtenderInterface; -use Flarum\Extension\Extension; use Flarum\Extension\ExtensionManager; use Flarum\Testing\integration\RetrievesAuthorizedUsers; use Flarum\Testing\integration\TestCase; -use Illuminate\Contracts\Container\Container; class ConditionalTest extends TestCase { @@ -147,7 +144,7 @@ public function conditional_injects_dependencies_to_condition_callable() $this->extend( (new Extend\Conditional()) ->when(function (?ExtensionManager $extensions) { - if (! $extensions) { + if (!$extensions) { throw new Exception('ExtensionManager not injected'); } }, [ @@ -164,44 +161,138 @@ public function conditional_injects_dependencies_to_condition_callable() } /** @test */ - public function conditional_does_not_instantiate_extender_if_condition_is_false() + public function conditional_does_not_instantiate_extender_if_condition_is_false_using_callable() { $this->extend( (new Extend\Conditional()) - ->when(false, [ - new TestExtender() - ]) + ->when(false, TestExtender::class) ); $this->app(); + + $response = $this->send( + $this->request('GET', '/api', [ + 'authenticatedAs' => 1, + ]) + ); + + $payload = json_decode($response->getBody()->getContents(), true); + + $this->assertArrayNotHasKey('customConditionalAttribute', $payload['data']['attributes']); } /** @test */ - public function conditional_does_instantiate_extender_if_condition_is_true() + public function conditional_does_instantiate_extender_if_condition_is_true_using_callable() { - $this->expectException(Exception::class); - $this->expectExceptionMessage('TestExtender was instantiated!'); + $this->extend( + (new Extend\Conditional()) + ->when(true, TestExtender::class) + ); + + $this->app(); + + $response = $this->send( + $this->request('GET', '/api', [ + 'authenticatedAs' => 1, + ]) + ); + + $payload = json_decode($response->getBody()->getContents(), true); + $this->assertArrayHasKey('customConditionalAttribute', $payload['data']['attributes']); + } + + /** @test */ + public function conditional_does_not_instantiate_extender_if_condition_is_false_using_callback() + { $this->extend( (new Extend\Conditional()) - ->when(true, [ - new TestExtender() - ]) + ->when(false, function (): array { + return [ + (new Extend\ApiSerializer(ForumSerializer::class)) + ->attributes(function () { + return [ + 'customConditionalAttribute' => true + ]; + }) + ]; + }) ); $this->app(); + + $response = $this->send( + $this->request('GET', '/api', [ + 'authenticatedAs' => 1, + ]) + ); + + $payload = json_decode($response->getBody()->getContents(), true); + + $this->assertArrayNotHasKey('customConditionalAttribute', $payload['data']['attributes']); } -} -class TestExtender implements ExtenderInterface -{ - public function __construct() + /** @test */ + public function conditional_does_instantiate_extender_if_condition_is_true_using_callback() { - throw new Exception('TestExtender was instantiated!'); + $this->extend( + (new Extend\Conditional()) + ->when(true, function (): array { + return [ + (new Extend\ApiSerializer(ForumSerializer::class)) + ->attributes(function () { + return [ + 'customConditionalAttribute' => true + ]; + }) + ]; + }) + ); + + $this->app(); + + $response = $this->send( + $this->request('GET', '/api', [ + 'authenticatedAs' => 1, + ]) + ); + + $payload = json_decode($response->getBody()->getContents(), true); + + $this->assertArrayHasKey('customConditionalAttribute', $payload['data']['attributes']); } - public function extend(Container $container, Extension $extension = null) + /** @test */ + public function conditional_does_not_work_if_extension_is_disabled() + { + $this->extend( + (new Extend\Conditional()) + ->whenExtensionEnabled('dummy-extension-id', TestExtender::class) + ); + + $response = $this->send( + $this->request('GET', '/api', [ + 'authenticatedAs' => 1, + ]) + ); + + $payload = json_decode($response->getBody()->getContents(), true); + + $this->assertArrayNotHasKey('customConditionalAttribute', $payload['data']['attributes']); + } +} + +class TestExtender +{ + public function __invoke(): array { - // This method can be left empty for this test. + return [ + (new Extend\ApiSerializer(ForumSerializer::class)) + ->attributes(function () { + return [ + 'customConditionalAttribute' => true + ]; + }) + ]; } }