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",