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

Skip to content

[Workflow] Fixed graphviz dumper for state machine #20583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 24, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we have a way to dump the state machine in workflow mode ? It is a subset after all.

But anyway, I agree it should not be the default dumping for state machines.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this of course means that it should be fixed to work properly, but I know how to do this (stop assuming that transition names are unique identifiers of the transition, as we stopped doing this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we have a way to dump the state machine in workflow mode ?

For now, it's not really readable to dump a state machine with the workflow dumper. So IMHO, it's not useful to add a way to do it.

} 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) {
Expand Down
71 changes: 45 additions & 26 deletions src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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();

Expand All @@ -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();

Expand All @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, this should not be protected extension points. This seems like the wrong way to reuse part of the GraphvizDumper (fixing the dumping of transitions to stop assuming unique names might require refactoring private methods, but making them protected forbids this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand what you mean. Could you elaborate?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are talking about composition, it's not really possible.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added @internal everywhere.

{
$dotEdges = array();

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

private function addEdges($edges)
/**
* @internal
*/
protected function addEdges(array $edges)
{
$code = '';

Expand All @@ -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']),
Expand All @@ -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();

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

private function addOptions($options)
private function addOptions(array $options)
{
$code = array();

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

return implode(' ', $code);
}

private function dotize($id)
{
return strtolower(preg_replace('/[^\w]/i', '_', $id));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function setUp()
/**
* @dataProvider provideWorkflowDefinitionWithoutMarking
*/
public function testGraphvizDumperWithoutMarking($definition, $expected)
public function testDumpWithoutMarking($definition, $expected)
{
$dump = $this->dumper->dump($definition);

Expand All @@ -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);

Expand All @@ -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(
Expand All @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace Symfony\Component\Workflow\Tests\Dumper;

use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper;
use Symfony\Component\Workflow\Marking;
use Symfony\Component\Workflow\Tests\WorkflowBuilderTrait;

class StateMachineGraphvizDumperTest extends \PHPUnit_Framework_TestCase
{
use WorkflowBuilderTrait;

private $dumper;

public function setUp()
{
$this->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);
}
}
Loading