Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6e0b6ea

Browse files
committed
bug #20583 [Workflow] Fixed graphviz dumper for state machine (lyrixx)
This PR was merged into the 3.3-dev branch. Discussion ---------- [Workflow] Fixed graphviz dumper for state machine | Q | A | ------------- | --- | Branch? | master | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #20497 | License | MIT | Doc PR | ~ --- Before: ![before](https://cloud.githubusercontent.com/assets/408368/20490801/77cc0abe-b00f-11e6-8094-f4d428d5acde.png) After: ![after](https://cloud.githubusercontent.com/assets/408368/20490809/7d21a4b0-b00f-11e6-8de2-c5021fe2d4e0.png) --- Script: ```php <?php require __DIR__.'/vendor/autoload.php'; use Symfony\Component\Workflow\Definition; use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper; use Symfony\Component\Workflow\Marking; use Symfony\Component\Workflow\StateMachine; use Symfony\Component\Workflow\Transition; $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); $marking = new Marking(['d' => 1]); echo (new StateMachineGraphvizDumper())->dump($definition, $marking); ``` Commits ------- 1e92e02 [Workflow] Fixed graphviz dumper for state machine
2 parents 9361c5e + 1e92e02 commit 6e0b6ea

File tree

9 files changed

+260
-86
lines changed

9 files changed

+260
-86
lines changed

src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Console\Input\InputInterface;
1616
use Symfony\Component\Console\Output\OutputInterface;
1717
use Symfony\Component\Workflow\Dumper\GraphvizDumper;
18+
use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper;
1819
use Symfony\Component\Workflow\Marking;
1920
use Symfony\Component\Workflow\Workflow;
2021

@@ -60,13 +61,14 @@ protected function execute(InputInterface $input, OutputInterface $output)
6061
$serviceId = $input->getArgument('name');
6162
if ($container->has('workflow.'.$serviceId)) {
6263
$workflow = $container->get('workflow.'.$serviceId);
64+
$dumper = new GraphvizDumper();
6365
} elseif ($container->has('state_machine.'.$serviceId)) {
6466
$workflow = $container->get('state_machine.'.$serviceId);
67+
$dumper = new StateMachineGraphvizDumper();
6568
} else {
6669
throw new \InvalidArgumentException(sprintf('No service found for "workflow.%1$s" nor "state_machine.%1$s".', $serviceId));
6770
}
6871

69-
$dumper = new GraphvizDumper();
7072
$marking = new Marking();
7173

7274
foreach ($input->getArgument('marking') as $place) {

src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
*/
2727
class GraphvizDumper implements DumperInterface
2828
{
29-
private static $defaultOptions = array(
29+
protected static $defaultOptions = array(
3030
'graph' => array('ratio' => 'compress', 'rankdir' => 'LR'),
3131
'node' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => '#333333', 'fillcolor' => 'lightblue', 'fixedsize' => true, 'width' => 1),
3232
'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
5858
.$this->endDot();
5959
}
6060

61-
private function findPlaces(Definition $definition, Marking $marking = null)
61+
/**
62+
* @internal
63+
*/
64+
protected function findPlaces(Definition $definition, Marking $marking = null)
6265
{
6366
$places = array();
6467

@@ -79,7 +82,10 @@ private function findPlaces(Definition $definition, Marking $marking = null)
7982
return $places;
8083
}
8184

82-
private function findTransitions(Definition $definition)
85+
/**
86+
* @internal
87+
*/
88+
protected function findTransitions(Definition $definition)
8389
{
8490
$transitions = array();
8591

@@ -93,37 +99,38 @@ private function findTransitions(Definition $definition)
9399
return $transitions;
94100
}
95101

96-
private function addPlaces(array $places)
102+
/**
103+
* @internal
104+
*/
105+
protected function addPlaces(array $places)
97106
{
98107
$code = '';
99108

100109
foreach ($places as $id => $place) {
101-
$code .= sprintf(" place_%s [label=\"%s\", shape=circle%s];\n",
102-
$this->dotize($id),
103-
$id,
104-
$this->addAttributes($place['attributes'])
105-
);
110+
$code .= sprintf(" place_%s [label=\"%s\", shape=circle%s];\n", $this->dotize($id), $id, $this->addAttributes($place['attributes']));
106111
}
107112

108113
return $code;
109114
}
110115

111-
private function addTransitions(array $transitions)
116+
/**
117+
* @internal
118+
*/
119+
protected function addTransitions(array $transitions)
112120
{
113121
$code = '';
114122

115123
foreach ($transitions as $place) {
116-
$code .= sprintf(" transition_%s [label=\"%s\", shape=box%s];\n",
117-
$this->dotize($place['name']),
118-
$place['name'],
119-
$this->addAttributes($place['attributes'])
120-
);
124+
$code .= sprintf(" transition_%s [label=\"%s\", shape=box%s];\n", $this->dotize($place['name']), $place['name'], $this->addAttributes($place['attributes']));
121125
}
122126

123127
return $code;
124128
}
125129

126-
private function findEdges(Definition $definition)
130+
/**
131+
* @internal
132+
*/
133+
protected function findEdges(Definition $definition)
127134
{
128135
$dotEdges = array();
129136

@@ -147,7 +154,10 @@ private function findEdges(Definition $definition)
147154
return $dotEdges;
148155
}
149156

150-
private function addEdges($edges)
157+
/**
158+
* @internal
159+
*/
160+
protected function addEdges(array $edges)
151161
{
152162
$code = '';
153163

@@ -163,7 +173,10 @@ private function addEdges($edges)
163173
return $code;
164174
}
165175

166-
private function startDot(array $options)
176+
/**
177+
* @internal
178+
*/
179+
protected function startDot(array $options)
167180
{
168181
return sprintf("digraph workflow {\n %s\n node [%s];\n edge [%s];\n\n",
169182
$this->addOptions($options['graph']),
@@ -172,12 +185,23 @@ private function startDot(array $options)
172185
);
173186
}
174187

175-
private function endDot()
188+
/**
189+
* @internal
190+
*/
191+
protected function endDot()
176192
{
177193
return "}\n";
178194
}
179195

180-
private function addAttributes($attributes)
196+
/**
197+
* @internal
198+
*/
199+
protected function dotize($id)
200+
{
201+
return strtolower(preg_replace('/[^\w]/i', '_', $id));
202+
}
203+
204+
private function addAttributes(array $attributes)
181205
{
182206
$code = array();
183207

@@ -188,7 +212,7 @@ private function addAttributes($attributes)
188212
return $code ? ', '.implode(', ', $code) : '';
189213
}
190214

191-
private function addOptions($options)
215+
private function addOptions(array $options)
192216
{
193217
$code = array();
194218

@@ -198,9 +222,4 @@ private function addOptions($options)
198222

199223
return implode(' ', $code);
200224
}
201-
202-
private function dotize($id)
203-
{
204-
return strtolower(preg_replace('/[^\w]/i', '_', $id));
205-
}
206225
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Workflow\Dumper;
13+
14+
use Symfony\Component\Workflow\Definition;
15+
use Symfony\Component\Workflow\Marking;
16+
17+
class StateMachineGraphvizDumper extends GraphvizDumper
18+
{
19+
/**
20+
* {@inheritdoc}
21+
*
22+
* Dumps the workflow as a graphviz graph.
23+
*
24+
* Available options:
25+
*
26+
* * graph: The default options for the whole graph
27+
* * node: The default options for nodes (places)
28+
* * edge: The default options for edges
29+
*/
30+
public function dump(Definition $definition, Marking $marking = null, array $options = array())
31+
{
32+
$places = $this->findPlaces($definition, $marking);
33+
$edges = $this->findEdges($definition);
34+
35+
$options = array_replace_recursive(self::$defaultOptions, $options);
36+
37+
return $this->startDot($options)
38+
.$this->addPlaces($places)
39+
.$this->addEdges($edges)
40+
.$this->endDot()
41+
;
42+
}
43+
44+
/**
45+
* @internal
46+
*/
47+
protected function findEdges(Definition $definition)
48+
{
49+
$edges = array();
50+
51+
foreach ($definition->getTransitions() as $transition) {
52+
foreach ($transition->getFroms() as $from) {
53+
foreach ($transition->getTos() as $to) {
54+
$edges[$from][] = array(
55+
'name' => $transition->getName(),
56+
'to' => $to,
57+
);
58+
}
59+
}
60+
}
61+
62+
return $edges;
63+
}
64+
65+
/**
66+
* @internal
67+
*/
68+
protected function addEdges(array $edges)
69+
{
70+
$code = '';
71+
72+
foreach ($edges as $id => $edges) {
73+
foreach ($edges as $edge) {
74+
$code .= sprintf(" place_%s -> place_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], 'solid');
75+
}
76+
}
77+
78+
return $code;
79+
}
80+
}

src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public function setUp()
2020
/**
2121
* @dataProvider provideWorkflowDefinitionWithoutMarking
2222
*/
23-
public function testGraphvizDumperWithoutMarking($definition, $expected)
23+
public function testDumpWithoutMarking($definition, $expected)
2424
{
2525
$dump = $this->dumper->dump($definition);
2626

@@ -30,7 +30,7 @@ public function testGraphvizDumperWithoutMarking($definition, $expected)
3030
/**
3131
* @dataProvider provideWorkflowDefinitionWithMarking
3232
*/
33-
public function testWorkflowWithMarking($definition, $marking, $expected)
33+
public function testDumpWithMarking($definition, $marking, $expected)
3434
{
3535
$dump = $this->dumper->dump($definition, $marking);
3636

@@ -40,9 +40,9 @@ public function testWorkflowWithMarking($definition, $marking, $expected)
4040
public function provideWorkflowDefinitionWithMarking()
4141
{
4242
yield array(
43-
$this->createComplexWorkflow(),
43+
$this->createComplexWorkflowDefinition(),
4444
new Marking(array('b' => 1)),
45-
$this->createComplexWorkflowDumpWithMarking(),
45+
$this->createComplexWorkflowDefinitionDumpWithMarking(),
4646
);
4747

4848
yield array(
@@ -54,11 +54,11 @@ public function provideWorkflowDefinitionWithMarking()
5454

5555
public function provideWorkflowDefinitionWithoutMarking()
5656
{
57-
yield array($this->createComplexWorkflow(), $this->provideComplexWorkflowDumpWithoutMarking());
57+
yield array($this->createComplexWorkflowDefinition(), $this->provideComplexWorkflowDumpWithoutMarking());
5858
yield array($this->createSimpleWorkflowDefinition(), $this->provideSimpleWorkflowDumpWithoutMarking());
5959
}
6060

61-
public function createComplexWorkflowDumpWithMarking()
61+
public function createComplexWorkflowDefinitionDumpWithMarking()
6262
{
6363
return 'digraph workflow {
6464
ratio="compress" rankdir="LR"
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace Symfony\Component\Workflow\Tests\Dumper;
4+
5+
use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper;
6+
use Symfony\Component\Workflow\Marking;
7+
use Symfony\Component\Workflow\Tests\WorkflowBuilderTrait;
8+
9+
class StateMachineGraphvizDumperTest extends \PHPUnit_Framework_TestCase
10+
{
11+
use WorkflowBuilderTrait;
12+
13+
private $dumper;
14+
15+
public function setUp()
16+
{
17+
$this->dumper = new StateMachineGraphvizDumper();
18+
}
19+
20+
public function testDumpWithoutMarking()
21+
{
22+
$definition = $this->createComplexStateMachineDefinition();
23+
24+
$dump = $this->dumper->dump($definition);
25+
26+
$expected = <<<'EOGRAPH'
27+
digraph workflow {
28+
ratio="compress" rankdir="LR"
29+
node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="1" width="1"];
30+
edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"];
31+
32+
place_a [label="a", shape=circle, style="filled"];
33+
place_b [label="b", shape=circle];
34+
place_c [label="c", shape=circle];
35+
place_d [label="d", shape=circle];
36+
place_a -> place_b [label="t1" style="solid"];
37+
place_d -> place_b [label="t1" style="solid"];
38+
place_b -> place_c [label="t2" style="solid"];
39+
place_b -> place_d [label="t3" style="solid"];
40+
}
41+
42+
EOGRAPH;
43+
44+
$this->assertEquals($expected, $dump);
45+
}
46+
47+
public function testDumpWithMarking()
48+
{
49+
$definition = $this->createComplexStateMachineDefinition();
50+
$marking = new Marking(array('b' => 1));
51+
52+
$expected = <<<'EOGRAPH'
53+
digraph workflow {
54+
ratio="compress" rankdir="LR"
55+
node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="1" width="1"];
56+
edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"];
57+
58+
place_a [label="a", shape=circle, style="filled"];
59+
place_b [label="b", shape=circle, color="#FF0000", shape="doublecircle"];
60+
place_c [label="c", shape=circle];
61+
place_d [label="d", shape=circle];
62+
place_a -> place_b [label="t1" style="solid"];
63+
place_d -> place_b [label="t1" style="solid"];
64+
place_b -> place_c [label="t2" style="solid"];
65+
place_b -> place_d [label="t3" style="solid"];
66+
}
67+
68+
EOGRAPH;
69+
70+
$dump = $this->dumper->dump($definition, $marking);
71+
72+
$this->assertEquals($expected, $dump);
73+
}
74+
}

0 commit comments

Comments
 (0)