diff --git a/UPGRADE-6.2.md b/UPGRADE-6.2.md index 015dde43b155f..8271d0843738a 100644 --- a/UPGRADE-6.2.md +++ b/UPGRADE-6.2.md @@ -37,3 +37,13 @@ Validator --------- * Deprecate the `loose` e-mail validation mode, use `html5` instead + +Workflow +-------- + + * The `Registry` is marked as internal and should not be used directly. use a tagged locator instead + ``` + tagged_locator('workflow', 'name') + ``` + * The first argument of `WorkflowDumpCommand` should be a `ServiceLocator` of + all workflows indexed by names diff --git a/UPGRADE-7.0.md b/UPGRADE-7.0.md new file mode 100644 index 0000000000000..587796bbb6e9f --- /dev/null +++ b/UPGRADE-7.0.md @@ -0,0 +1,8 @@ +UPGRADE FROM 6.4 to 7.0 +======================= + +Workflow +-------- + + * The first argument of `WorkflowDumpCommand` must be a `ServiceLocator` of all + workflows indexed by names diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 6766dc5c57992..7c503eb35d52b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -12,6 +12,9 @@ CHANGELOG `Symfony\Component\Serializer\Normalizer\NormalizerInterface` or implement `NormalizerAwareInterface` instead * Add service usages list to the `debug:container` command output * Add service and alias deprecation message to `debug:container []` output + * Tag all workflows services with `workflow`, those with type=workflow are + tagged with `workflow.workflow`, and those with type=state_machine with + `workflow.state_machine` 6.1 --- diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php index eb96e65470ebf..14ea912a86bd9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php @@ -20,12 +20,14 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\Workflow\Definition; use Symfony\Component\Workflow\Dumper\GraphvizDumper; use Symfony\Component\Workflow\Dumper\MermaidDumper; use Symfony\Component\Workflow\Dumper\PlantUmlDumper; use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper; use Symfony\Component\Workflow\Marking; +use Symfony\Component\Workflow\StateMachine; /** * @author Grégoire Pineau @@ -40,7 +42,9 @@ class WorkflowDumpCommand extends Command * * @var array */ - private array $workflows = []; + private array $definitions = []; + + private ServiceLocator $workflows; private const DUMP_FORMAT_OPTIONS = [ 'puml', @@ -48,11 +52,18 @@ class WorkflowDumpCommand extends Command 'dot', ]; - public function __construct(array $workflows) + public function __construct($workflows) { parent::__construct(); - $this->workflows = $workflows; + if ($workflows instanceof ServiceLocator) { + $this->workflows = $workflows; + } elseif (\is_array($workflows)) { + $this->definitions = $workflows; + trigger_deprecation('symfony/framework-bundle', '6.2', 'Passing an array of definitions in "%s()" is deprecated. Inject a ServiceLocator filled with all workflows instead.', __METHOD__); + } else { + throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be an array or a ServiceLocator, "%s" given.', __METHOD__, \gettype($workflows))); + } } /** @@ -88,15 +99,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int $workflow = null; - if (isset($this->workflows['workflow.'.$workflowName])) { - $workflow = $this->workflows['workflow.'.$workflowName]; + if (isset($this->workflows)) { + if (!$this->workflows->has($workflowName)) { + throw new InvalidArgumentException(sprintf('The workflow named "%s" cannot be found.', $workflowName)); + } + $workflow = $this->workflows->get($workflowName); + $type = $workflow instanceof StateMachine ? 'state_machine' : 'workflow'; + $definition = $workflow->getDefinition(); + } elseif (isset($this->definitions['workflow.'.$workflowName])) { + $definition = $this->definitions['workflow.'.$workflowName]; $type = 'workflow'; - } elseif (isset($this->workflows['state_machine.'.$workflowName])) { - $workflow = $this->workflows['state_machine.'.$workflowName]; + } elseif (isset($this->definitions['state_machine.'.$workflowName])) { + $definition = $this->definitions['state_machine.'.$workflowName]; $type = 'state_machine'; } - if (null === $workflow) { + if (null === $definition) { throw new InvalidArgumentException(sprintf('No service found for "workflow.%1$s" nor "state_machine.%1$s".', $workflowName)); } @@ -129,7 +147,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'label' => $input->getOption('label'), ], ]; - $output->writeln($dumper->dump($workflow, $marking, $options)); + $output->writeln($dumper->dump($definition, $marking, $options)); return 0; } @@ -137,7 +155,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('name')) { - $suggestions->suggestValues(array_keys($this->workflows)); + if (isset($this->workflows)) { + $suggestions->suggestValues(array_keys($this->workflows->getProvidedServices())); + } else { + $suggestions->suggestValues(array_keys($this->definitions)); + } } if ($input->mustSuggestOptionValuesFor('dump-format')) { diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php index 42edc57ffc816..02b59afad5e61 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php @@ -97,6 +97,7 @@ class UnusedTagsPass implements CompilerPassInterface 'validator.auto_mapper', 'validator.constraint_validator', 'validator.initializer', + 'workflow', ]; public function process(ContainerBuilder $container) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 706773fa60522..c5855b04cd992 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -883,9 +883,9 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $loader->load('workflow.php'); - $registryDefinition = $container->getDefinition('workflow.registry'); + $registryDefinition = $container->getDefinition('.workflow.registry'); - $workflows = []; + $workflow = []; foreach ($config['workflows'] as $name => $workflow) { $type = $workflow['type']; @@ -994,6 +994,13 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $workflowDefinition->replaceArgument(3, $name); $workflowDefinition->replaceArgument(4, $workflow['events_to_dispatch']); + $workflowDefinition->addTag('workflow', ['name' => $name]); + if ('workflow' === $type) { + $workflowDefinition->addTag('workflow.workflow', ['name' => $name]); + } elseif ('state_machine' === $type) { + $workflowDefinition->addTag('workflow.state_machine', ['name' => $name]); + } + // Store to container $container->setDefinition($workflowId, $workflowDefinition); $container->setDefinition(sprintf('%s.definition', $workflowId), $definitionDefinition); @@ -1063,9 +1070,6 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $container->setParameter('workflow.has_guard_listeners', true); } } - - $commandDumpDefinition = $container->getDefinition('console.command.workflow_dump'); - $commandDumpDefinition->setArgument(0, $workflows); } private function registerDebugConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php index cc51b12a0bf2c..9ca75d099e5f9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php @@ -282,6 +282,9 @@ ->tag('console.command', ['command' => 'translation:push']) ->set('console.command.workflow_dump', WorkflowDumpCommand::class) + ->args([ + tagged_locator('workflow', 'name'), + ]) ->tag('console.command') ->set('console.command.xliff_lint', XliffLintCommand::class) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.php index b6c784bdbeaa9..4b8875da73424 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.php @@ -39,8 +39,11 @@ ->abstract() ->set('workflow.marking_store.method', MethodMarkingStore::class) ->abstract() - ->set('workflow.registry', Registry::class) - ->alias(Registry::class, 'workflow.registry') + ->set('.workflow.registry', Registry::class) + ->alias(Registry::class, '.workflow.registry') + ->deprecate('symfony/workflow', '6.2', 'The "%alias_id%" alias is deprecated since Symfony 6.2 and will be removed in Symfony 7.0. Inject the workflow directly.') + ->alias('workflow.registry', '.workflow.registry') + ->deprecate('symfony/workflow', '6.2', 'The "%alias_id%" service is deprecated since Symfony 6.2 and will be removed in Symfony 7.0. Inject the workflow directly.') ->set('workflow.security.expression_language', ExpressionLanguage::class) ; }; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/WorkflowDumpCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/WorkflowDumpCommandTest.php index 13a63b40d97fa..cefc5dacd8e50 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/WorkflowDumpCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/WorkflowDumpCommandTest.php @@ -15,6 +15,7 @@ use Symfony\Bundle\FrameworkBundle\Command\WorkflowDumpCommand; use Symfony\Component\Console\Application; use Symfony\Component\Console\Tester\CommandCompletionTester; +use Symfony\Component\DependencyInjection\ServiceLocator; class WorkflowDumpCommandTest extends TestCase { @@ -24,7 +25,7 @@ class WorkflowDumpCommandTest extends TestCase public function testComplete(array $input, array $expectedSuggestions) { $application = new Application(); - $application->add(new WorkflowDumpCommand([])); + $application->add(new WorkflowDumpCommand(new ServiceLocator([]))); $tester = new CommandCompletionTester($application->find('workflow:dump')); $suggestions = $tester->complete($input, 2); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 85ceec55ac8ca..320483fb0dafc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -295,6 +295,8 @@ public function testWorkflows() $this->assertArrayHasKey('index_4', $args); $this->assertNull($args['index_4'], 'Workflows has eventsToDispatch=null'); + $this->assertSame(['workflow' => [['name' => 'article']], 'workflow.workflow' => [['name' => 'article']]], $container->getDefinition('workflow.article')->getTags()); + $this->assertTrue($container->hasDefinition('workflow.article.definition'), 'Workflow definition is registered as a service'); $workflowDefinition = $container->getDefinition('workflow.article.definition'); @@ -324,6 +326,8 @@ public function testWorkflows() $this->assertSame('state_machine.abstract', $container->getDefinition('state_machine.pull_request')->getParent()); $this->assertTrue($container->hasDefinition('state_machine.pull_request.definition'), 'State machine definition is registered as a service'); + $this->assertSame(['workflow' => [['name' => 'pull_request']], 'workflow.state_machine' => [['name' => 'pull_request']]], $container->getDefinition('state_machine.pull_request')->getTags()); + $stateMachineDefinition = $container->getDefinition('state_machine.pull_request.definition'); $this->assertSame( @@ -371,8 +375,8 @@ public function testWorkflows() $this->assertInstanceOf(Reference::class, $markingStoreRef); $this->assertEquals('workflow_service', (string) $markingStoreRef); - $this->assertTrue($container->hasDefinition('workflow.registry'), 'Workflow registry is registered as a service'); - $registryDefinition = $container->getDefinition('workflow.registry'); + $this->assertTrue($container->hasDefinition('.workflow.registry'), 'Workflow registry is registered as a service'); + $registryDefinition = $container->getDefinition('.workflow.registry'); $this->assertGreaterThan(0, \count($registryDefinition->getMethodCalls())); } diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.php b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.php index cf8540764c7c4..aa5c543b30f40 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.php +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.php @@ -141,7 +141,7 @@ ->tag('translation.extractor', ['alias' => 'twig']) ->set('workflow.twig_extension', WorkflowExtension::class) - ->args([service('workflow.registry')]) + ->args([service('.workflow.registry')]) ->set('twig.configurator.environment', EnvironmentConfigurator::class) ->args([ diff --git a/src/Symfony/Component/Workflow/CHANGELOG.md b/src/Symfony/Component/Workflow/CHANGELOG.md index b533107a8f9b8..758606b3eb68f 100644 --- a/src/Symfony/Component/Workflow/CHANGELOG.md +++ b/src/Symfony/Component/Workflow/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.2 +--- + + * Mark `Symfony\Component\Workflow\Registry` as internal + 6.0 --- diff --git a/src/Symfony/Component/Workflow/Registry.php b/src/Symfony/Component/Workflow/Registry.php index 1fbc76be487ac..a9c21af18a906 100644 --- a/src/Symfony/Component/Workflow/Registry.php +++ b/src/Symfony/Component/Workflow/Registry.php @@ -17,6 +17,8 @@ /** * @author Fabien Potencier * @author Grégoire Pineau + * + * @internal since Symfony 6.2. Inject the workflow where you need it. */ class Registry {