diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php index ddfb987ff1937..40604e3d809db 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php @@ -15,6 +15,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Workflow\Dumper\GraphvizDumper; +use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper; use Symfony\Component\Workflow\Marking; use Symfony\Component\Workflow\Workflow; @@ -60,13 +61,14 @@ protected function execute(InputInterface $input, OutputInterface $output) $serviceId = $input->getArgument('name'); if ($container->has('workflow.'.$serviceId)) { $workflow = $container->get('workflow.'.$serviceId); + $dumper = new GraphvizDumper(); } elseif ($container->has('state_machine.'.$serviceId)) { $workflow = $container->get('state_machine.'.$serviceId); + $dumper = new StateMachineGraphvizDumper(); } else { throw new \InvalidArgumentException(sprintf('No service found for "workflow.%1$s" nor "state_machine.%1$s".', $serviceId)); } - $dumper = new GraphvizDumper(); $marking = new Marking(); foreach ($input->getArgument('marking') as $place) { diff --git a/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php b/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php index 916c4bd460622..3681b6f1391a8 100644 --- a/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php +++ b/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php @@ -26,7 +26,7 @@ */ class GraphvizDumper implements DumperInterface { - private static $defaultOptions = array( + protected static $defaultOptions = array( 'graph' => array('ratio' => 'compress', 'rankdir' => 'LR'), 'node' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => '#333333', 'fillcolor' => 'lightblue', 'fixedsize' => true, 'width' => 1), 'edge' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => '#333333', 'arrowhead' => 'normal', 'arrowsize' => 0.5), @@ -58,7 +58,10 @@ public function dump(Definition $definition, Marking $marking = null, array $opt .$this->endDot(); } - private function findPlaces(Definition $definition, Marking $marking = null) + /** + * @internal + */ + protected function findPlaces(Definition $definition, Marking $marking = null) { $places = array(); @@ -79,7 +82,10 @@ private function findPlaces(Definition $definition, Marking $marking = null) return $places; } - private function findTransitions(Definition $definition) + /** + * @internal + */ + protected function findTransitions(Definition $definition) { $transitions = array(); @@ -93,37 +99,38 @@ private function findTransitions(Definition $definition) return $transitions; } - private function addPlaces(array $places) + /** + * @internal + */ + protected function addPlaces(array $places) { $code = ''; foreach ($places as $id => $place) { - $code .= sprintf(" place_%s [label=\"%s\", shape=circle%s];\n", - $this->dotize($id), - $id, - $this->addAttributes($place['attributes']) - ); + $code .= sprintf(" place_%s [label=\"%s\", shape=circle%s];\n", $this->dotize($id), $id, $this->addAttributes($place['attributes'])); } return $code; } - private function addTransitions(array $transitions) + /** + * @internal + */ + protected function addTransitions(array $transitions) { $code = ''; foreach ($transitions as $place) { - $code .= sprintf(" transition_%s [label=\"%s\", shape=box%s];\n", - $this->dotize($place['name']), - $place['name'], - $this->addAttributes($place['attributes']) - ); + $code .= sprintf(" transition_%s [label=\"%s\", shape=box%s];\n", $this->dotize($place['name']), $place['name'], $this->addAttributes($place['attributes'])); } return $code; } - private function findEdges(Definition $definition) + /** + * @internal + */ + protected function findEdges(Definition $definition) { $dotEdges = array(); @@ -147,7 +154,10 @@ private function findEdges(Definition $definition) return $dotEdges; } - private function addEdges($edges) + /** + * @internal + */ + protected function addEdges(array $edges) { $code = ''; @@ -163,7 +173,10 @@ private function addEdges($edges) return $code; } - private function startDot(array $options) + /** + * @internal + */ + protected function startDot(array $options) { return sprintf("digraph workflow {\n %s\n node [%s];\n edge [%s];\n\n", $this->addOptions($options['graph']), @@ -172,12 +185,23 @@ private function startDot(array $options) ); } - private function endDot() + /** + * @internal + */ + protected function endDot() { return "}\n"; } - private function addAttributes($attributes) + /** + * @internal + */ + protected function dotize($id) + { + return strtolower(preg_replace('/[^\w]/i', '_', $id)); + } + + private function addAttributes(array $attributes) { $code = array(); @@ -188,7 +212,7 @@ private function addAttributes($attributes) return $code ? ', '.implode(', ', $code) : ''; } - private function addOptions($options) + private function addOptions(array $options) { $code = array(); @@ -198,9 +222,4 @@ private function addOptions($options) return implode(' ', $code); } - - private function dotize($id) - { - return strtolower(preg_replace('/[^\w]/i', '_', $id)); - } } diff --git a/src/Symfony/Component/Workflow/Dumper/StateMachineGraphvizDumper.php b/src/Symfony/Component/Workflow/Dumper/StateMachineGraphvizDumper.php new file mode 100644 index 0000000000000..9f68e1daf72f3 --- /dev/null +++ b/src/Symfony/Component/Workflow/Dumper/StateMachineGraphvizDumper.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\Dumper; + +use Symfony\Component\Workflow\Definition; +use Symfony\Component\Workflow\Marking; + +class StateMachineGraphvizDumper extends GraphvizDumper +{ + /** + * {@inheritdoc} + * + * Dumps the workflow as a graphviz graph. + * + * Available options: + * + * * graph: The default options for the whole graph + * * node: The default options for nodes (places) + * * edge: The default options for edges + */ + public function dump(Definition $definition, Marking $marking = null, array $options = array()) + { + $places = $this->findPlaces($definition, $marking); + $edges = $this->findEdges($definition); + + $options = array_replace_recursive(self::$defaultOptions, $options); + + return $this->startDot($options) + .$this->addPlaces($places) + .$this->addEdges($edges) + .$this->endDot() + ; + } + + /** + * @internal + */ + protected function findEdges(Definition $definition) + { + $edges = array(); + + foreach ($definition->getTransitions() as $transition) { + foreach ($transition->getFroms() as $from) { + foreach ($transition->getTos() as $to) { + $edges[$from][] = array( + 'name' => $transition->getName(), + 'to' => $to, + ); + } + } + } + + return $edges; + } + + /** + * @internal + */ + protected function addEdges(array $edges) + { + $code = ''; + + foreach ($edges as $id => $edges) { + foreach ($edges as $edge) { + $code .= sprintf(" place_%s -> place_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], 'solid'); + } + } + + return $code; + } +} diff --git a/src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php b/src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php index 2b90ea8d5f197..01927b209c2ff 100644 --- a/src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php +++ b/src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php @@ -20,7 +20,7 @@ public function setUp() /** * @dataProvider provideWorkflowDefinitionWithoutMarking */ - public function testGraphvizDumperWithoutMarking($definition, $expected) + public function testDumpWithoutMarking($definition, $expected) { $dump = $this->dumper->dump($definition); @@ -30,7 +30,7 @@ public function testGraphvizDumperWithoutMarking($definition, $expected) /** * @dataProvider provideWorkflowDefinitionWithMarking */ - public function testWorkflowWithMarking($definition, $marking, $expected) + public function testDumpWithMarking($definition, $marking, $expected) { $dump = $this->dumper->dump($definition, $marking); @@ -40,9 +40,9 @@ public function testWorkflowWithMarking($definition, $marking, $expected) public function provideWorkflowDefinitionWithMarking() { yield array( - $this->createComplexWorkflow(), + $this->createComplexWorkflowDefinition(), new Marking(array('b' => 1)), - $this->createComplexWorkflowDumpWithMarking(), + $this->createComplexWorkflowDefinitionDumpWithMarking(), ); yield array( @@ -54,11 +54,11 @@ public function provideWorkflowDefinitionWithMarking() public function provideWorkflowDefinitionWithoutMarking() { - yield array($this->createComplexWorkflow(), $this->provideComplexWorkflowDumpWithoutMarking()); + yield array($this->createComplexWorkflowDefinition(), $this->provideComplexWorkflowDumpWithoutMarking()); yield array($this->createSimpleWorkflowDefinition(), $this->provideSimpleWorkflowDumpWithoutMarking()); } - public function createComplexWorkflowDumpWithMarking() + public function createComplexWorkflowDefinitionDumpWithMarking() { return 'digraph workflow { ratio="compress" rankdir="LR" diff --git a/src/Symfony/Component/Workflow/Tests/Dumper/StateMachineGraphvizDumperTest.php b/src/Symfony/Component/Workflow/Tests/Dumper/StateMachineGraphvizDumperTest.php new file mode 100644 index 0000000000000..c9a49b36f71e1 --- /dev/null +++ b/src/Symfony/Component/Workflow/Tests/Dumper/StateMachineGraphvizDumperTest.php @@ -0,0 +1,74 @@ +dumper = new StateMachineGraphvizDumper(); + } + + public function testDumpWithoutMarking() + { + $definition = $this->createComplexStateMachineDefinition(); + + $dump = $this->dumper->dump($definition); + + $expected = <<<'EOGRAPH' +digraph workflow { + ratio="compress" rankdir="LR" + node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="1" width="1"]; + edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"]; + + place_a [label="a", shape=circle, style="filled"]; + place_b [label="b", shape=circle]; + place_c [label="c", shape=circle]; + place_d [label="d", shape=circle]; + place_a -> place_b [label="t1" style="solid"]; + place_d -> place_b [label="t1" style="solid"]; + place_b -> place_c [label="t2" style="solid"]; + place_b -> place_d [label="t3" style="solid"]; +} + +EOGRAPH; + + $this->assertEquals($expected, $dump); + } + + public function testDumpWithMarking() + { + $definition = $this->createComplexStateMachineDefinition(); + $marking = new Marking(array('b' => 1)); + + $expected = <<<'EOGRAPH' +digraph workflow { + ratio="compress" rankdir="LR" + node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="1" width="1"]; + edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"]; + + place_a [label="a", shape=circle, style="filled"]; + place_b [label="b", shape=circle, color="#FF0000", shape="doublecircle"]; + place_c [label="c", shape=circle]; + place_d [label="d", shape=circle]; + place_a -> place_b [label="t1" style="solid"]; + place_d -> place_b [label="t1" style="solid"]; + place_b -> place_c [label="t2" style="solid"]; + place_b -> place_d [label="t3" style="solid"]; +} + +EOGRAPH; + + $dump = $this->dumper->dump($definition, $marking); + + $this->assertEquals($expected, $dump); + } +} diff --git a/src/Symfony/Component/Workflow/Tests/StateMachineTest.php b/src/Symfony/Component/Workflow/Tests/StateMachineTest.php index 6aa3c60fc1cc6..7dca9a824d7c1 100644 --- a/src/Symfony/Component/Workflow/Tests/StateMachineTest.php +++ b/src/Symfony/Component/Workflow/Tests/StateMachineTest.php @@ -2,21 +2,16 @@ namespace Symfony\Component\Workflow\Tests; -use Symfony\Component\Workflow\Definition; use Symfony\Component\Workflow\Marking; use Symfony\Component\Workflow\StateMachine; -use Symfony\Component\Workflow\Transition; class StateMachineTest extends \PHPUnit_Framework_TestCase { + use WorkflowBuilderTrait; + public function testCan() { - $places = array('a', 'b', 'c', 'd'); - $transitions[] = new Transition('t1', 'a', 'b'); - $transitions[] = new Transition('t1', 'd', 'b'); - $transitions[] = new Transition('t2', 'b', 'c'); - $transitions[] = new Transition('t3', 'b', 'd'); - $definition = new Definition($places, $transitions); + $definition = $this->createComplexStateMachineDefinition(); $net = new StateMachine($definition); $subject = new \stdClass(); @@ -29,47 +24,18 @@ public function testCan() $subject->marking = 'b'; $this->assertFalse($net->can($subject, 't1')); - - // The graph looks like: - // - // +-------------------------------+ - // v | - // +---+ +----+ +----+ +----+ +---+ +----+ - // | a | --> | t1 | --> | b | --> | t3 | --> | d | --> | t1 | - // +---+ +----+ +----+ +----+ +---+ +----+ - // | - // | - // v - // +----+ +----+ - // | t2 | --> | c | - // +----+ +----+ } public function testCanWithMultipleTransition() { - $places = array('a', 'b', 'c'); - $transitions[] = new Transition('t1', 'a', 'b'); - $transitions[] = new Transition('t2', 'a', 'c'); - $definition = new Definition($places, $transitions); + $definition = $this->createComplexStateMachineDefinition(); $net = new StateMachine($definition); $subject = new \stdClass(); - // If you are in place "a" you should be able to apply "t1" and "t2" - $subject->marking = 'a'; - $this->assertTrue($net->can($subject, 't1')); + // If you are in place "b" you should be able to apply "t1" and "t2" + $subject->marking = 'b'; $this->assertTrue($net->can($subject, 't2')); - - // The graph looks like: - // - // +----+ +----+ +---+ - // | a | --> | t1 | --> | b | - // +----+ +----+ +---+ - // | - // | - // v - // +----+ +----+ - // | t2 | --> | c | - // +----+ +----+ + $this->assertTrue($net->can($subject, 't3')); } } diff --git a/src/Symfony/Component/Workflow/Tests/Validator/WorkflowValidatorTest.php b/src/Symfony/Component/Workflow/Tests/Validator/WorkflowValidatorTest.php index 1b5fa67fdb083..30d2551fa1b0f 100644 --- a/src/Symfony/Component/Workflow/Tests/Validator/WorkflowValidatorTest.php +++ b/src/Symfony/Component/Workflow/Tests/Validator/WorkflowValidatorTest.php @@ -15,7 +15,7 @@ class WorkflowValidatorTest extends \PHPUnit_Framework_TestCase */ public function testSinglePlaceWorkflowValidatorAndComplexWorkflow() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); (new WorkflowValidator(true))->validate($definition, 'foo'); } diff --git a/src/Symfony/Component/Workflow/Tests/WorkflowBuilderTrait.php b/src/Symfony/Component/Workflow/Tests/WorkflowBuilderTrait.php index d7b8de530445c..5e8db29061295 100644 --- a/src/Symfony/Component/Workflow/Tests/WorkflowBuilderTrait.php +++ b/src/Symfony/Component/Workflow/Tests/WorkflowBuilderTrait.php @@ -7,7 +7,7 @@ trait WorkflowBuilderTrait { - private function createComplexWorkflow() + private function createComplexWorkflowDefinition() { $places = range('a', 'g'); @@ -33,7 +33,7 @@ private function createComplexWorkflow() // +----+ +----+ +----+ +----+ } - public function createSimpleWorkflowDefinition() + private function createSimpleWorkflowDefinition() { $places = range('a', 'c'); @@ -42,5 +42,38 @@ public function createSimpleWorkflowDefinition() $transitions[] = new Transition('t2', 'b', 'c'); return new Definition($places, $transitions); + + // The graph looks like: + // +---+ +----+ +---+ +----+ +---+ + // | a | --> | t1 | --> | b | --> | t2 | --> | c | + // +---+ +----+ +---+ +----+ +---+ + } + + private function createComplexStateMachineDefinition() + { + $places = array('a', 'b', 'c', 'd'); + + $transitions[] = new Transition('t1', 'a', 'b'); + $transitions[] = new Transition('t1', 'd', 'b'); + $transitions[] = new Transition('t2', 'b', 'c'); + $transitions[] = new Transition('t3', 'b', 'd'); + + $definition = new Definition($places, $transitions); + + return $definition; + + // The graph looks like: + // t1 + // +------------------+ + // v | + // +---+ t1 +-----+ t2 +---+ | + // | a | ----> | b | ----> | c | | + // +---+ +-----+ +---+ | + // | | + // | t3 | + // v | + // +-----+ | + // | d | -------------+ + // +-----+ } } diff --git a/src/Symfony/Component/Workflow/Tests/WorkflowTest.php b/src/Symfony/Component/Workflow/Tests/WorkflowTest.php index 0a886752d5992..cb5256f412933 100644 --- a/src/Symfony/Component/Workflow/Tests/WorkflowTest.php +++ b/src/Symfony/Component/Workflow/Tests/WorkflowTest.php @@ -56,7 +56,7 @@ public function testGetMarkingWithImpossiblePlace() public function testGetMarkingWithEmptyInitialMarking() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $workflow = new Workflow($definition, new MultipleStateMarkingStore()); @@ -70,7 +70,7 @@ public function testGetMarkingWithEmptyInitialMarking() public function testGetMarkingWithExistingMarking() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $subject->marking = array('b' => 1, 'c' => 1); @@ -89,7 +89,7 @@ public function testGetMarkingWithExistingMarking() */ public function testCanWithUnexistingTransition() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $workflow = new Workflow($definition, new MultipleStateMarkingStore()); @@ -99,7 +99,7 @@ public function testCanWithUnexistingTransition() public function testCan() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $workflow = new Workflow($definition, new MultipleStateMarkingStore()); @@ -110,7 +110,7 @@ public function testCan() public function testCanWithGuard() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $eventDispatcher = new EventDispatcher(); @@ -128,7 +128,7 @@ public function testCanWithGuard() */ public function testApplyWithImpossibleTransition() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $workflow = new Workflow($definition, new MultipleStateMarkingStore()); @@ -138,7 +138,7 @@ public function testApplyWithImpossibleTransition() public function testApply() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $workflow = new Workflow($definition, new MultipleStateMarkingStore()); @@ -153,7 +153,7 @@ public function testApply() public function testApplyWithEventDispatcher() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $eventDispatcher = new EventDispatcherMock(); @@ -187,7 +187,7 @@ public function testApplyWithEventDispatcher() public function testGetEnabledTransitions() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $eventDispatcher = new EventDispatcher(); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy