From 6ab4c7f1fb5be3b29dc3533bd0d46132a8ccee08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 13 Mar 2024 18:34:52 +0100 Subject: [PATCH] [Workflow] Add support for executing custom workflow definition validators during the container compilation --- .../Bundle/FrameworkBundle/CHANGELOG.md | 1 + .../DependencyInjection/Configuration.php | 35 +++++++-- .../FrameworkExtension.php | 35 +++++---- .../FrameworkBundle/FrameworkBundle.php | 2 + .../Resources/config/schema/symfony-1.0.xsd | 1 + .../Validator/DefinitionValidator.php | 16 ++++ .../Fixtures/php/workflows.php | 3 + .../Fixtures/xml/workflows.xml | 1 + .../Fixtures/yml/workflows.yml | 2 + .../FrameworkExtensionTestCase.php | 12 ++- .../PhpFrameworkExtensionTest.php | 61 ++++++++++++++- .../Bundle/FrameworkBundle/composer.json | 4 +- .../WorkflowValidatorPass.php | 37 ++++++++++ .../WorkflowValidatorPassTest.php | 74 +++++++++++++++++++ src/Symfony/Component/Workflow/composer.json | 1 + 15 files changed, 256 insertions(+), 29 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/Workflow/Validator/DefinitionValidator.php create mode 100644 src/Symfony/Component/Workflow/DependencyInjection/WorkflowValidatorPass.php create mode 100644 src/Symfony/Component/Workflow/Tests/DependencyInjection/WorkflowValidatorPassTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index f7a3766d66cb7..ce62c9cdf836b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -55,6 +55,7 @@ CHANGELOG * Allow configuring compound rate limiters * Make `ValidatorCacheWarmer` use `kernel.build_dir` instead of `cache_dir` * Make `SerializeCacheWarmer` use `kernel.build_dir` instead of `cache_dir` + * Support executing custom workflow validators during container compilation 7.2 --- diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 11dc781babd3d..4c40455526e57 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -52,6 +52,7 @@ use Symfony\Component\Validator\Validation; use Symfony\Component\Webhook\Controller\WebhookController; use Symfony\Component\WebLink\HttpHeaderSerializer; +use Symfony\Component\Workflow\Validator\DefinitionValidatorInterface; use Symfony\Component\Workflow\WorkflowEvents; /** @@ -403,6 +404,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->useAttributeAsKey('name') ->prototype('array') ->fixXmlConfig('support') + ->fixXmlConfig('definition_validator') ->fixXmlConfig('place') ->fixXmlConfig('transition') ->fixXmlConfig('event_to_dispatch', 'events_to_dispatch') @@ -432,11 +434,28 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->prototype('scalar') ->cannotBeEmpty() ->validate() - ->ifTrue(fn ($v) => !class_exists($v) && !interface_exists($v, false)) + ->ifTrue(static fn ($v) => !class_exists($v) && !interface_exists($v, false)) ->thenInvalid('The supported class or interface "%s" does not exist.') ->end() ->end() ->end() + ->arrayNode('definition_validators') + ->prototype('scalar') + ->cannotBeEmpty() + ->validate() + ->ifTrue(static fn ($v) => !class_exists($v)) + ->thenInvalid('The validation class %s does not exist.') + ->end() + ->validate() + ->ifTrue(static fn ($v) => !is_a($v, DefinitionValidatorInterface::class, true)) + ->thenInvalid(\sprintf('The validation class %%s is not an instance of "%s".', DefinitionValidatorInterface::class)) + ->end() + ->validate() + ->ifTrue(static fn ($v) => 1 <= (new \ReflectionClass($v))->getConstructor()?->getNumberOfRequiredParameters()) + ->thenInvalid('The %s validation class constructor must not have any arguments.') + ->end() + ->end() + ->end() ->scalarNode('support_strategy') ->cannotBeEmpty() ->end() @@ -448,7 +467,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->variableNode('events_to_dispatch') ->defaultValue(null) ->validate() - ->ifTrue(function ($v) { + ->ifTrue(static function ($v) { if (null === $v) { return false; } @@ -475,14 +494,14 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->arrayNode('places') ->beforeNormalization() ->always() - ->then(function ($places) { + ->then(static function ($places) { if (!\is_array($places)) { throw new InvalidConfigurationException('The "places" option must be an array in workflow configuration.'); } // It's an indexed array of shape ['place1', 'place2'] if (isset($places[0]) && \is_string($places[0])) { - return array_map(function (string $place) { + return array_map(static function (string $place) { return ['name' => $place]; }, $places); } @@ -522,7 +541,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->arrayNode('transitions') ->beforeNormalization() ->always() - ->then(function ($transitions) { + ->then(static function ($transitions) { if (!\is_array($transitions)) { throw new InvalidConfigurationException('The "transitions" option must be an array in workflow configuration.'); } @@ -589,20 +608,20 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->end() ->end() ->validate() - ->ifTrue(function ($v) { + ->ifTrue(static function ($v) { return $v['supports'] && isset($v['support_strategy']); }) ->thenInvalid('"supports" and "support_strategy" cannot be used together.') ->end() ->validate() - ->ifTrue(function ($v) { + ->ifTrue(static function ($v) { return !$v['supports'] && !isset($v['support_strategy']); }) ->thenInvalid('"supports" or "support_strategy" should be configured.') ->end() ->beforeNormalization() ->always() - ->then(function ($values) { + ->then(static function ($values) { // Special case to deal with XML when the user wants an empty array if (\array_key_exists('event_to_dispatch', $values) && null === $values['event_to_dispatch']) { $values['events_to_dispatch'] = []; diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 4b18b38177047..6df4d21df25ce 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1123,7 +1123,8 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ } } $metadataStoreDefinition->replaceArgument(2, $transitionsMetadataDefinition); - $container->setDefinition(\sprintf('%s.metadata_store', $workflowId), $metadataStoreDefinition); + $metadataStoreId = \sprintf('%s.metadata_store', $workflowId); + $container->setDefinition($metadataStoreId, $metadataStoreDefinition); // Create places $places = array_column($workflow['places'], 'name'); @@ -1134,7 +1135,8 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $definitionDefinition->addArgument($places); $definitionDefinition->addArgument($transitions); $definitionDefinition->addArgument($initialMarking); - $definitionDefinition->addArgument(new Reference(\sprintf('%s.metadata_store', $workflowId))); + $definitionDefinition->addArgument(new Reference($metadataStoreId)); + $definitionDefinitionId = \sprintf('%s.definition', $workflowId); // Create MarkingStore $markingStoreDefinition = null; @@ -1148,14 +1150,26 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $markingStoreDefinition = new Reference($workflow['marking_store']['service']); } + // Validation + $workflow['definition_validators'][] = match ($workflow['type']) { + 'state_machine' => Workflow\Validator\StateMachineValidator::class, + 'workflow' => Workflow\Validator\WorkflowValidator::class, + default => throw new \LogicException(\sprintf('Invalid workflow type "%s".', $workflow['type'])), + }; + // Create Workflow $workflowDefinition = new ChildDefinition(\sprintf('%s.abstract', $type)); - $workflowDefinition->replaceArgument(0, new Reference(\sprintf('%s.definition', $workflowId))); + $workflowDefinition->replaceArgument(0, new Reference($definitionDefinitionId)); $workflowDefinition->replaceArgument(1, $markingStoreDefinition); $workflowDefinition->replaceArgument(3, $name); $workflowDefinition->replaceArgument(4, $workflow['events_to_dispatch']); - $workflowDefinition->addTag('workflow', ['name' => $name, 'metadata' => $workflow['metadata']]); + $workflowDefinition->addTag('workflow', [ + 'name' => $name, + 'metadata' => $workflow['metadata'], + 'definition_validators' => $workflow['definition_validators'], + 'definition_id' => $definitionDefinitionId, + ]); if ('workflow' === $type) { $workflowDefinition->addTag('workflow.workflow', ['name' => $name]); } elseif ('state_machine' === $type) { @@ -1164,21 +1178,10 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ // Store to container $container->setDefinition($workflowId, $workflowDefinition); - $container->setDefinition(\sprintf('%s.definition', $workflowId), $definitionDefinition); + $container->setDefinition($definitionDefinitionId, $definitionDefinition); $container->registerAliasForArgument($workflowId, WorkflowInterface::class, $name.'.'.$type); $container->registerAliasForArgument($workflowId, WorkflowInterface::class, $name); - // Validate Workflow - if ('state_machine' === $workflow['type']) { - $validator = new Workflow\Validator\StateMachineValidator(); - } else { - $validator = new Workflow\Validator\WorkflowValidator(); - } - - $trs = array_map(fn (Reference $ref): Workflow\Transition => $container->get((string) $ref), $transitions); - $realDefinition = new Workflow\Definition($places, $trs, $initialMarking); - $validator->validate($realDefinition, $name); - // Add workflow to Registry if ($workflow['supports']) { foreach ($workflow['supports'] as $supportedClassName) { diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index faf2841f40105..7c5ba6e39e121 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -77,6 +77,7 @@ use Symfony\Component\VarExporter\Internal\Registry; use Symfony\Component\Workflow\DependencyInjection\WorkflowDebugPass; use Symfony\Component\Workflow\DependencyInjection\WorkflowGuardListenerPass; +use Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass; // Help opcache.preload discover always-needed symbols class_exists(ApcuAdapter::class); @@ -173,6 +174,7 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new CachePoolPrunerPass(), PassConfig::TYPE_AFTER_REMOVING); $this->addCompilerPassIfExists($container, FormPass::class); $this->addCompilerPassIfExists($container, WorkflowGuardListenerPass::class); + $this->addCompilerPassIfExists($container, WorkflowValidatorPass::class); $container->addCompilerPass(new ResettableServicePass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); $container->addCompilerPass(new RegisterLocaleAwareServicesPass()); $container->addCompilerPass(new TestServiceContainerWeakRefPass(), PassConfig::TYPE_BEFORE_REMOVING, -32); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index c4ee3486dae87..3a6242b837dd3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -449,6 +449,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/Workflow/Validator/DefinitionValidator.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/Workflow/Validator/DefinitionValidator.php new file mode 100644 index 0000000000000..7244e927ca763 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/Workflow/Validator/DefinitionValidator.php @@ -0,0 +1,16 @@ + [ FrameworkExtensionTestCase::class, ], + 'definition_validators' => [ + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator\DefinitionValidator::class, + ], 'initial_marking' => ['draft'], 'metadata' => [ 'title' => 'article workflow', diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml index 76b4f07a87a44..c5dae479d3d63 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml @@ -13,6 +13,7 @@ draft Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator\DefinitionValidator diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml index a9b427d89408a..cac5f6f230f92 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml @@ -9,6 +9,8 @@ framework: type: workflow supports: - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase + definition_validators: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator\DefinitionValidator initial_marking: [draft] metadata: title: article workflow diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php index d942c122c826a..1899d5239eb4d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -15,6 +15,7 @@ use Psr\Log\LoggerAwareInterface; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator\DefinitionValidator; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DummyMessage; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Bundle\FullStack; @@ -287,7 +288,11 @@ public function testProfilerCollectSerializerDataEnabled() public function testWorkflows() { - $container = $this->createContainerFromFile('workflows'); + DefinitionValidator::$called = false; + + $container = $this->createContainerFromFile('workflows', compile: false); + $container->addCompilerPass(new \Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass()); + $container->compile(); $this->assertTrue($container->hasDefinition('workflow.article'), 'Workflow is registered as a service'); $this->assertSame('workflow.abstract', $container->getDefinition('workflow.article')->getParent()); @@ -310,6 +315,7 @@ public function testWorkflows() ], $tags['workflow'][0]['metadata'] ?? null); $this->assertTrue($container->hasDefinition('workflow.article.definition'), 'Workflow definition is registered as a service'); + $this->assertTrue(DefinitionValidator::$called, 'DefinitionValidator is called'); $workflowDefinition = $container->getDefinition('workflow.article.definition'); @@ -403,7 +409,9 @@ public function testWorkflowAreValidated() { $this->expectException(InvalidDefinitionException::class); $this->expectExceptionMessage('A transition from a place/state must have an unique name. Multiple transitions named "go" from place/state "first" were found on StateMachine "my_workflow".'); - $this->createContainerFromFile('workflow_not_valid'); + $container = $this->createContainerFromFile('workflow_not_valid', compile: false); + $container->addCompilerPass(new \Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass()); + $container->compile(); } public function testWorkflowCannotHaveBothSupportsAndSupportStrategy() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index dbadcc468a5b9..d2bd2b38eb313 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -102,7 +102,7 @@ public function testWorkflowValidationStateMachine() { $this->expectException(InvalidDefinitionException::class); $this->expectExceptionMessage('A transition from a place/state must have an unique name. Multiple transitions named "a_to_b" from place/state "a" were found on StateMachine "article".'); - $this->createContainerFromClosure(function ($container) { + $this->createContainerFromClosure(function (ContainerBuilder $container) { $container->loadFromExtension('framework', [ 'annotations' => false, 'http_method_override' => false, @@ -128,9 +128,57 @@ public function testWorkflowValidationStateMachine() ], ], ]); + $container->addCompilerPass(new \Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass()); + }); + } + + /** + * @dataProvider provideWorkflowValidationCustomTests + */ + public function testWorkflowValidationCustomBroken(string $class, string $message) + { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage($message); + $this->createContainerFromClosure(function ($container) use ($class) { + $container->loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'workflows' => [ + 'article' => [ + 'type' => 'state_machine', + 'supports' => [ + __CLASS__, + ], + 'places' => [ + 'a', + 'b', + ], + 'transitions' => [ + 'a_to_b' => [ + 'from' => ['a'], + 'to' => ['b'], + ], + ], + 'definition_validators' => [ + $class, + ], + ], + ], + ]); }); } + public static function provideWorkflowValidationCustomTests() + { + yield ['classDoesNotExist', 'Invalid configuration for path "framework.workflows.workflows.article.definition_validators.0": The validation class "classDoesNotExist" does not exist.']; + + yield [\DateTime::class, 'Invalid configuration for path "framework.workflows.workflows.article.definition_validators.0": The validation class "DateTime" is not an instance of "Symfony\Component\Workflow\Validator\DefinitionValidatorInterface".']; + + yield [WorkflowValidatorWithConstructor::class, 'Invalid configuration for path "framework.workflows.workflows.article.definition_validators.0": The "Symfony\\\\Bundle\\\\FrameworkBundle\\\\Tests\\\\DependencyInjection\\\\WorkflowValidatorWithConstructor" validation class constructor must not have any arguments.']; + } + public function testWorkflowDefaultMarkingStoreDefinition() { $container = $this->createContainerFromClosure(function ($container) { @@ -407,3 +455,14 @@ public static function emailValidationModeProvider() } } } + +class WorkflowValidatorWithConstructor implements \Symfony\Component\Workflow\Validator\DefinitionValidatorInterface +{ + public function __construct(bool $enabled) + { + } + + public function validate(\Symfony\Component\Workflow\Definition $definition, string $name): void + { + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index bc312827ffa14..b3c81b28700a3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -67,7 +67,7 @@ "symfony/twig-bundle": "^6.4|^7.0", "symfony/type-info": "^7.1", "symfony/validator": "^6.4|^7.0", - "symfony/workflow": "^6.4|^7.0", + "symfony/workflow": "^7.3", "symfony/yaml": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", "symfony/json-streamer": "7.3.*", @@ -108,7 +108,7 @@ "symfony/validator": "<6.4", "symfony/web-profiler-bundle": "<6.4", "symfony/webhook": "<7.2", - "symfony/workflow": "<6.4" + "symfony/workflow": "<7.3" }, "autoload": { "psr-4": { "Symfony\\Bundle\\FrameworkBundle\\": "" }, diff --git a/src/Symfony/Component/Workflow/DependencyInjection/WorkflowValidatorPass.php b/src/Symfony/Component/Workflow/DependencyInjection/WorkflowValidatorPass.php new file mode 100644 index 0000000000000..60072ef0ca612 --- /dev/null +++ b/src/Symfony/Component/Workflow/DependencyInjection/WorkflowValidatorPass.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\DependencyInjection; + +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\LogicException; + +/** + * @author Grégoire Pineau + */ +class WorkflowValidatorPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + foreach ($container->findTaggedServiceIds('workflow') as $attributes) { + foreach ($attributes as $attribute) { + foreach ($attribute['definition_validators'] ?? [] as $validatorClass) { + $container->addResource(new FileResource($container->getReflectionClass($validatorClass)->getFileName())); + + $realDefinition = $container->get($attribute['definition_id'] ?? throw new \LogicException('The "definition_id" attribute is required.')); + (new $validatorClass())->validate($realDefinition, $attribute['name'] ?? throw new \LogicException('The "name" attribute is required.')); + } + } + } + } +} diff --git a/src/Symfony/Component/Workflow/Tests/DependencyInjection/WorkflowValidatorPassTest.php b/src/Symfony/Component/Workflow/Tests/DependencyInjection/WorkflowValidatorPassTest.php new file mode 100644 index 0000000000000..213e0d4d94cc3 --- /dev/null +++ b/src/Symfony/Component/Workflow/Tests/DependencyInjection/WorkflowValidatorPassTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Workflow\Definition; +use Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass; +use Symfony\Component\Workflow\Validator\DefinitionValidatorInterface; +use Symfony\Component\Workflow\WorkflowInterface; + +class WorkflowValidatorPassTest extends TestCase +{ + private ContainerBuilder $container; + private WorkflowValidatorPass $compilerPass; + + protected function setUp(): void + { + $this->container = new ContainerBuilder(); + $this->compilerPass = new WorkflowValidatorPass(); + } + + public function testNothingToDo() + { + $this->compilerPass->process($this->container); + + $this->assertFalse(DefinitionValidator::$called); + } + + public function testValidate() + { + $this + ->container + ->register('my.workflow', WorkflowInterface::class) + ->addTag('workflow', [ + 'definition_id' => 'my.workflow.definition', + 'name' => 'my.workflow', + 'definition_validators' => [DefinitionValidator::class], + ]) + ; + + $this + ->container + ->register('my.workflow.definition', Definition::class) + ->setArguments([ + '$places' => [], + '$transitions' => [], + ]) + ; + + $this->compilerPass->process($this->container); + + $this->assertTrue(DefinitionValidator::$called); + } +} + +class DefinitionValidator implements DefinitionValidatorInterface +{ + public static bool $called = false; + + public function validate(Definition $definition, string $name): void + { + self::$called = true; + } +} diff --git a/src/Symfony/Component/Workflow/composer.json b/src/Symfony/Component/Workflow/composer.json index ef6779c6de142..3e2c50a38cffd 100644 --- a/src/Symfony/Component/Workflow/composer.json +++ b/src/Symfony/Component/Workflow/composer.json @@ -25,6 +25,7 @@ }, "require-dev": { "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", "symfony/error-handler": "^6.4|^7.0", "symfony/event-dispatcher": "^6.4|^7.0",