8000 added the first more-or-less working version of the Workflow component · symfony/symfony@17d59a7 · GitHub
[go: up one dir, main page]

Skip to content

Commit 17d59a7

Browse files
committed
added the first more-or-less working version of the Workflow component
1 parent 9af416d commit 17d59a7

File tree

11 files changed

+803
-0
lines changed

11 files changed

+803
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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;
13+
14+
/**
15+
* @author Fabien Potencier <fabien@symfony.com>
16+
*/
17+
class Definition
18+
{
19+
private $class;
20+
private $states = array();
21+
private $transitions = array();
22+
private $initialState;
23+
24+
public function __construct($class)
25+
{
26+
$this->class = $class;
27+
}
28+
29+
public function getClass()
30+
{
31+
return $this->class;
32+
}
33+
34+
public function getStates()
35+
{
36+
return $this->states;
37+
}
38+
39+
public function getTransitions()
40+
{
41+
return $this->transitions;
42+
}
43+
44+
public function getInitialState()
45+
{
46+
return $this->initialState;
47+
}
48+
49+
public function setInitialState($name)
50+
{
51+
if (!isset($this->states[$name])) {
52+
throw new \LogicException(sprintf('State "%s" cannot be the initial state as it does not exist.', $name));
53+
}
54+
55+
$this->initialState = $name;
56+
}
57+
58+
public function addState($name)
59+
{
60+
if (!count($this->states)) {
61+
$this->initialState = $name;
62+
}
63+
64+
$this->states[$name] = $name;
65+
}
66+
67+
public function addTransition(Transition $transition)
68+
{
69+
if (isset($this->transitions[$transition->getName()])) {
70+
throw new \LogicException(sprintf('Transition "%s" is already defined.', $transition->getName()));
71+
}
72+
73+
foreach ($transition->getFroms() as $from) {
74+
if (!isset($this->states[$from])) {
75+
throw new \LogicException(sprintf('State "%s" referenced in transition "%s" does not exist.', $from, $name));
76+
}
77+
}
78+
79+
foreach ($transition->getTos() as $to) {
80+
if (!isset($this->states[$to])) {
81+
throw new \LogicException(sprintf('State "%s" referenced in transition "%s" does not exist.', $to, $name));
82+
}
83+
}
84+
85+
$this->transitions[$transition->getName()] = $transition;
86+
}
87+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
16+
/**
17+
* DumperInterface is the interface implemented by workflow dumper classes.
18+
*
19+
* @author Fabien Potencier <fabien@symfony.com>
20+
*/
21+
interface DumperInterface
22+
{
23+
/**
24+
* Dumps a workflow definition.
25+
*
26+
* @param Definition $definition A Definition instance
27+
* @param array $options An array of options
28+
*
29+
* @return string The representation of the workflow
30+
*/
31+
public function dump(Definition $definition, array $options = array());
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
16+
/**
17+
* GraphvizDumper dumps a workflow as a graphviz file.
18+
*
19+
* You can convert the generated dot file with the dot utility (http://www.graphviz.org/):
20+
*
21+
* dot -Tpng workflow.dot > workflow.png
22+
*
23+
* @author Fabien Potencier <fabien@symfony.com>
24+
*/
25+
class GraphvizDumper implements DumperInterface
26+
{
27+
private $nodes;
28+
private $edges;
29+
private $options = array(
30+
'graph' => array('ratio' => 'compress', 'rankdir' => 'LR'),
31+
'node' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => '#333', 'shape' => 'circle', 'fillcolor' => 'lightblue', 'fixedsize' => true, 'width' => 1),
32+
'edge' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => '#333', 'arrowhead' => 'normal', 'arrowsize' => 0.5),
33+
);
34+
35+
/**
36+
* Dumps the workflow as a graphviz graph.
37+
*
38+
* Available options:
39+
*
40+
* * graph: The default options for the whole graph
41+
* * node: The default options for nodes
42+
* * edge: The default options for edges
43+
*
44+
* @param Definition $definition A Definition instance
45+
* @param array $options An array of options
46+
*
47+
* @return string The dot representation of the workflow
48+
*/
49+
public function dump(Definition $definition, array $options = array())
50+
{
51+
foreach (array('graph', 'node', 'edge') as $key) {
52+
if (isset($options[$key])) {
53+
$this->options[$key] = array_merge($this->options[$key], $options[$key]);
54+
}
55+
}
56+
57+
$this->nodes = $this->findNodes($definition);
58+
$this->edges = $this->findEdges($definition);
59+
60+
return $this->startDot().$this->addNodes().$this->addEdges().$this->endDot();
61+
}
62+
63+
/**
64+
* Finds all nodes.
65+
*
66+
* @return array An array of all nodes
67+
*/
68+
private function findNodes(Definition $definition)
69+
{
70+
$nodes = array();
71+
foreach ($definition->getStates() as $state) {
72+
$nodes[$state] = array(
73+
'attributes' => array_merge($this->options['node'], array('style' => $state == $definition->getInitialState() ? 'filled' : 'solid'))
74+
);
75+
}
76+
77+
return $nodes;
78+
}
79+
80+
/**
81+
* Returns all nodes.
82+
*
83+
* @return string A string representation of all nodes
84+
*/
85+
private function addNodes()
86+
{
87+
$code = '';
88+
foreach ($this->nodes as $id => $node) {
89+
$code .= sprintf(" node_%s [label=\"%s\", shape=%s%s];\n", $this->dotize($id), $id, $this->options['node']['shape'], $this->addAttributes($node['attributes']));
90+
}
91+
92+
return $code;
93+
}
94+
95+
private function findEdges(Definition $definition)
96+
{
97+
$edges = array();
98+
foreach ($definition->getTransitions() as $transition) {
99+
foreach ($transition->getFroms() as $from) {
100+
foreach ($transition->getTos() as $to) {
101+
$edges[$from][] = array(
102+
'name' => $transition->getName(),
103+
'to' => $to,
104+
);
105+
}
106+
}
107+
}
108+
109+
return $edges;
110+
}
111+
112+
/**
113+
* Returns all edges.
114+
*
115+
* @return string A string representation of all edges
116+
*/
117+
private function addEdges()
118+
{
119+
$code = '';
120+
foreach ($this->edges as $id => $edges) {
121+
foreach ($edges as $edge) {
122+
$code .= sprintf(" node_%s -> node_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], 'solid');
123+
}
124+
}
125+
126+
return $code;
127+
}
128+
129+
/**
130+
* Returns the start dot.
131+
*
132+
* @return string The string representation of a start dot
133+
*/
134+
private function startDot()
135+
{
136+
return sprintf("digraph workflow {\n %s\n node [%s];\n edge [%s];\n\n",
137+
$this->addOptions($this->options['graph']),
138+
$this->addOptions($this->options['node']),
139+
$this->addOptions($this->options['edge'])
140+
);
141+
}
142+
143+
/**
144+
* Returns the end dot.
145+
*
146+
* @return string
147+
*/
148+
private function endDot()
149+
{
150+
return "}\n";
151+
}
152+
153+
/**
154+
* Adds attributes
155+
*
156+
* @param array $attributes An array of attributes
157+
*
158+
* @return string A comma separated list of attributes
159+
*/
160+
private function addAttributes($attributes)
161+
{
162+
$code = array();
163+
foreach ($attributes as $k => $v) {
164+
$code[] = sprintf('%s="%s"', $k, $v);
165+
}
166+
167+
return $code ? ', '.implode(', ', $code) : '';
168+
}
169+
170+
/**
171+
* Adds options
172+
*
173+
* @param array $options An array of options
174+
*
175+
* @return string A space separated list of options
176+
*/
177+
private function addOptions($options)
178+
{
179+
$code = array();
180+
foreach ($options as $k => $v) {
181+
$code[] = sprintf('%s="%s"', $k, $v);
182+
}
183+
184+
return implode(' ', $code);
185+
}
186+
187+
/**
188+
* Dotizes an identifier.
189+
*
190+
* @param string $id The identifier to dotize
191+
*
192+
* @return string A dotized string
193+
*/
194+
private function dotize($id)
195+
{
196+
return strtolower(preg_replace('/[^\w]/i', '_', $id));
197+
}
198+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Event;
13+
14+
use Symfony\Component\EventDispatcher\Event as BaseEvent;
15+
16+
/**
17+
* @author Fabien Potencier <fabien@symfony.com>
18+
*/
19+
class Event extends BaseEvent
20+
{
21+
private $object;
22+
private $state;
23+
private $attributes;
24+
25+
public function __construct($object, $state, array $attributes = array())
26+
{
27+
$this->object = $object;
28+
$this->state = $state;
29+
$this->attributes = $attributes;
30+
}
31+
32+
public function getState()
33+
{
34+
return $this->state;
35+
}
36+
37+
public function getObject()
38+
{
39+
return $this->object;
40+
}
41+
42+
public function getAttribute($key)
43+
{
44+
return isset($this->attributes[$key]) ? $this->attributes[$key] : null;
45+
}
46+
47+
public function hastAttribute($key)
48+
{
49+
return isset($this->attributes[$key]);
50+
}
51+
}

0 commit comments

Comments
 (0)
0