/
githubmirror
/
symfony
Обзор
Документация
Войти
/
githubmirror
/
symfony
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
8.2
src/Symfony/Component/Workflow/Command/WorkflowDumpCommand.php
147 строк
6 KB
Nicolas Grekas
[Workflow] Various fixes and hardenings
19 май 2026, 14:46
19 май 2026, 14:46
a2f435b
Код
Авторство
О чём код?
<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Workflow\Command; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Workflow\Debug\ListenerExtractor; use Symfony\Component\Workflow\Debug\TraceableWorkflow; 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; use Symfony\Contracts\Service\ServiceProviderInterface; /** * @author Grégoire Pineau <lyrixx@lyrixx.info> * * @final */ #[AsCommand(name: 'workflow:dump', description: 'Dump a workflow')] class WorkflowDumpCommand extends Command { private readonly ?ListenerExtractor $listenerExtractor; private const DUMP_FORMAT_OPTIONS = [ 'puml', 'mermaid', 'dot', ]; public function __construct( private ServiceProviderInterface $workflows, ?EventDispatcherInterface $dispatcher = null, ) { parent::__construct(); $this->listenerExtractor = $dispatcher ? new ListenerExtractor($dispatcher) : null; } protected function configure(): void { $this ->setDefinition([ new InputArgument('name', InputArgument::REQUIRED, 'A workflow name'), new InputArgument('marking', InputArgument::IS_ARRAY, 'A marking (a list of places)'), new InputOption('label', 'l', InputOption::VALUE_REQUIRED, 'Label a graph'), new InputOption('with-metadata', null, InputOption::VALUE_NONE, 'Include the workflow\'s metadata in the dumped graph', null), new InputOption('with-listeners', null, InputOption::VALUE_NONE, 'Include the workflow\'s listeners in the dumped graph', null), new InputOption('dump-format', null, InputOption::VALUE_REQUIRED, 'The dump format ['.implode('|', self::DUMP_FORMAT_OPTIONS).']', 'dot'), ]) ->setHelp(<<<'EOF' The <info>%command.name%</info> command dumps the graphical representation of a workflow in different formats <info>DOT</info>: %command.full_name% <workflow name> | dot -Tpng > workflow.png <info>PUML</info>: %command.full_name% <workflow name> --dump-format=puml | java -jar plantuml.jar -p > workflow.png <info>MERMAID</info>: %command.full_name% <workflow name> --dump-format=mermaid | mmdc -o workflow.svg EOF ) ; } protected function execute(InputInterface $input, OutputInterface $output): int { $workflowName = $input->getArgument('name'); if (!$this->workflows->has($workflowName)) { throw new InvalidArgumentException(\sprintf('The workflow named "%s" cannot be found.', $workflowName)); } $workflow = $this->workflows->get($workflowName); if ($workflow instanceof TraceableWorkflow) { $workflow = $workflow->getInner(); } $type = $workflow instanceof StateMachine ? 'state_machine' : 'workflow'; $definition = $workflow->getDefinition(); switch ($input->getOption('dump-format')) { case 'puml': $transitionType = 'workflow' === $type ? PlantUmlDumper::WORKFLOW_TRANSITION : PlantUmlDumper::STATEMACHINE_TRANSITION; $dumper = new PlantUmlDumper($transitionType); break; case 'mermaid': $transitionType = 'workflow' === $type ? MermaidDumper::TRANSITION_TYPE_WORKFLOW : MermaidDumper::TRANSITION_TYPE_STATEMACHINE; $dumper = new MermaidDumper($transitionType); break; case 'dot': default: $dumper = ('workflow' === $type) ? new GraphvizDumper() : new StateMachineGraphvizDumper(); } $marking = new Marking(); foreach ($input->getArgument('marking') as $place) { $marking->mark($place); } $listeners = []; if ($input->getOption('with-listeners')) { if (!$this->listenerExtractor) { throw new InvalidArgumentException('You cannot use the "--with-listeners" option if a dispatcher is not injected in the constructor.'); } $listeners = $this->listenerExtractor->extractListeners($workflowName, $definition); } $options = [ 'name' => $workflowName, 'with-metadata' => $input->getOption('with-metadata'), 'listeners' => $listeners, 'nofooter' => true, 'label' => $input->getOption('label'), ]; $output->writeln($dumper->dump($definition, $marking, $options)); return 0; } public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('name')) { $suggestions->suggestValues(array_keys($this->workflows->getProvidedServices())); } if ($input->mustSuggestOptionValuesFor('dump-format')) { $suggestions->suggestValues(self::DUMP_FORMAT_OPTIONS); } } }