Skip to content

Commit e93497b

Browse files
Nyholmlyrixx
authored andcommitted
Make the Workflow support State Machines
1 parent c36cc44 commit e93497b

17 files changed

+509
-101
lines changed

Definition.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ public function getPlaces()
4646
return $this->places;
4747
}
4848

49+
/**
50+
* @return Transition[]
51+
*/
4952
public function getTransitions()
5053
{
5154
return $this->transitions;
@@ -103,6 +106,6 @@ public function addTransition(Transition $transition)
103106
}
104107
}
105108

106-
$this->transitions[$name] = $transition;
109+
$this->transitions[] = $transition;
107110
}
108111
}

Dumper/GraphvizDumper.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ private function findTransitions(Definition $definition)
8383
{
8484
$transitions = array();
8585

86-
foreach ($definition->getTransitions() as $name => $transition) {
87-
$transitions[$name] = array(
86+
foreach ($definition->getTransitions() as $transition) {
87+
$transitions[] = array(
8888
'attributes' => array('shape' => 'box', 'regular' => true),
89+
'name' => $transition->getName(),
8990
);
9091
}
9192

@@ -111,10 +112,10 @@ private function addTransitions(array $transitions)
111112
{
112113
$code = '';
113114

114-
foreach ($transitions as $id => $place) {
115+
foreach ($transitions as $place) {
115116
$code .= sprintf(" transition_%s [label=\"%s\", shape=box%s];\n",
116-
$this->dotize($id),
117-
$id,
117+
$this->dotize($place['name']),
118+
$place['name'],
118119
$this->addAttributes($place['attributes'])
119120
);
120121
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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\Exception;
13+
14+
/**
15+
* Thrown by the DefinitionValidatorInterface when the definition is invalid.
16+
*
17+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
18+
*/
19+
class InvalidDefinitionException extends \LogicException implements ExceptionInterface
20+
{
21+
}

MarkingStore/ScalarMarkingStore.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*
2121
* @author Grégoire Pineau <lyrixx@lyrixx.info>
2222
*/
23-
class ScalarMarkingStore implements MarkingStoreInterface, UniqueTransitionOutputInterface
23+
class ScalarMarkingStore implements MarkingStoreInterface
2424
{
2525
private $property;
2626
private $propertyAccessor;

MarkingStore/UniqueTransitionOutputInterface.php

Lines changed: 0 additions & 21 deletions
This file was deleted.

StateMachine.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Symfony\Component\Workflow;
4+
5+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
6+
use Symfony\Component\Workflow\MarkingStore\MarkingStoreInterface;
7+
use Symfony\Component\Workflow\MarkingStore\ScalarMarkingStore;
8+
9+
/**
10+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
11+
*/
12+
class StateMachine extends Workflow
13+
{
14+
public function __construct(Definition $definition, MarkingStoreInterface $markingStore = null, EventDispatcherInterface $dispatcher = null, $name = 'unnamed')
15+
{
16+
parent::__construct($definition, $markingStore ?: new ScalarMarkingStore(), $dispatcher, $name);
17+
}
18+
}

Tests/DefinitionTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public function testAddTransition()
5555
$definition = new Definition($places, array($transition));
5656

5757
$this->assertCount(1, $definition->getTransitions());
58-
$this->assertSame($transition, $definition->getTransitions()['name']);
58+
$this->assertSame($transition, $definition->getTransitions()[0]);
5959
}
6060

6161
/**

Tests/RegistryTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ protected function setUp()
1818

1919
$this->registry = new Registry();
2020

21-
$this->registry->add(new Workflow(new Definition(), $this->getMock(MarkingStoreInterface::class), $this->getMock(EventDispatcherInterface::class), 'workflow1'), Subject1::class);
22-
$this->registry->add(new Workflow(new Definition(), $this->getMock(MarkingStoreInterface::class), $this->getMock(EventDispatcherInterface::class), 'workflow2'), Subject2::class);
23-
$this->registry->add(new Workflow(new Definition(), $this->getMock(MarkingStoreInterface::class), $this->getMock(EventDispatcherInterface::class), 'workflow3'), Subject2::class);
21+
$this->registry->add(new Workflow(new Definition(), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow1'), Subject1::class);
22+
$this->registry->add(new Workflow(new Definition(), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow2'), Subject2::class);
23+
$this->registry->add(new Workflow(new Definition(), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow3'), Subject2::class);
2424
}
2525

2626
protected function tearDown()
@@ -55,7 +55,7 @@ public function testGetWithMultipleMatch()
5555
}
5656

5757
/**
58-
* @expectedException Symfony\Component\Workflow\Exception\InvalidArgumentException
58+
* @expectedException \Symfony\Component\Workflow\Exception\InvalidArgumentException
5959
* @expectedExceptionMessage Unable to find a workflow for class "stdClass".
6060
*/
6161
public function testGetWithNoMatch()

Tests/StateMachineTest.php

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
namespace Symfony\Component\Workflow\Tests;
4+
5+
use Symfony\Component\Workflow\Definition;
6+
use Symfony\Component\Workflow\Marking;
7+
use Symfony\Component\Workflow\StateMachine;
8+
use Symfony\Component\Workflow\Transition;
9+
10+
class StateMachineTest extends \PHPUnit_Framework_TestCase
11+
{
12+
public function testCan()
13+
{
14+
$places = array('a', 'b', 'c', 'd');
15+
$transitions[] = new Transition('t1', 'a', 'b');
16+
$transitions[] = new Transition('t1', 'd', 'b');
17+
$transitions[] = new Transition('t2', 'b', 'c');
18+
$transitions[] = new Transition('t3', 'b', 'd');
19+
$definition = new Definition($places, $transitions);
20+
21+
$net = new StateMachine($definition);
22+
$subject = new \stdClass();
23+
24+
// If you are in place "a" you should be able to apply "t1"
25+
$subject->marking = 'a';
26+
$this->assertTrue($net->can($subject, 't1'));
27+
$subject->marking = 'd';
28+
$this->assertTrue($net->can($subject, 't1'));
29+
30+
$subject->marking = 'b';
31+
$this->assertFalse($net->can($subject, 't1'));
32+
33+
// The graph looks like:
34+
//
35+
// +-------------------------------+
36+
// v |
37+
// +---+ +----+ +----+ +----+ +---+ +----+
38+
// | a | --> | t1 | --> | b | --> | t3 | --> | d | --> | t1 |
39+
// +---+ +----+ +----+ +----+ +---+ +----+
40+
// |
41+
// |
42+
// v
43+
// +----+ +----+
44+
// | t2 | --> | c |
45+
// +----+ +----+
46+
}
47+
48+
public function testCanWithMultipleTransition()
49+
{
50+
$places = array('a', 'b', 'c');
51+
$transitions[] = new Transition('t1', 'a', 'b');
52+
$transitions[] = new Transition('t2', 'a', 'c');
53+
$definition = new Definition($places, $transitions);
54+
55+
$net = new StateMachine($definition);
56+
$subject = new \stdClass();
57+
58+
// If you are in place "a" you should be able to apply "t1" and "t2"
59+
$subject->marking = 'a';
60+
$this->assertTrue($net->can($subject, 't1'));
61+
$this->assertTrue($net->can($subject, 't2'));
62+
63+
// The graph looks like:
64+
//
65+
// +----+ +----+ +---+
66+
// | a | --> | t1 | --> | b |
67+
// +----+ +----+ +---+
68+
// |
69+
// |
70+
// v
71+
// +----+ +----+
72+
// | t2 | --> | c |
73+
// +----+ +----+
74+
}
75+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Symfony\Component\Workflow\Tests\Validator;
4+
5+
use Symfony\Component\Workflow\Definition;
6+
use Symfony\Component\Workflow\Marking;
7+
use Symfony\Component\Workflow\Tests\WorkflowTest;
8+
use Symfony\Component\Workflow\Transition;
9+
use Symfony\Component\Workflow\Validator\SinglePlaceWorkflowValidator;
10+
use Symfony\Component\Workflow\Workflow;
11+
12+
class SinglePlaceWorkflowValidatorTest extends WorkflowTest
13+
{
14+
/**
15+
* @expectedException \Symfony\Component\Workflow\Exception\InvalidDefinitionException
16+
* @expectedExceptionMessage The marking store of workflow "foo" can not store many places.
17+
*/
18+
public function testSinglePlaceWorkflowValidatorAndComplexWorkflow()
19+
{
20+
$definition = $this->createComplexWorkflow();
21+
22+
(new SinglePlaceWorkflowValidator())->validate($definition, 'foo');
23+
}
24+
25+
public function testSinglePlaceWorkflowValidatorAndSimpleWorkflow()
26+
{
27+
$places = array('a', 'b');
28+
$transition = new Transition('t1', 'a', 'b');
29+
$definition = new Definition($places, array($transition));
30+
31+
(new SinglePlaceWorkflowValidator())->validate($definition, 'foo');
32+
}
33+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
3+
namespace Symfony\Component\Workflow\Tests\Validator;
4+
5+
use Symfony\Component\Workflow\Definition;
6+
use Symfony\Component\Workflow\Transition;
7+
use Symfony\Component\Workflow\Validator\StateMachineValidator;
8+
9+
class StateMachineValidatorTest extends \PHPUnit_Framework_TestCase
10+
{
11+
/**
12+
* @expectedException \Symfony\Component\Workflow\Exception\InvalidDefinitionException
13+
* @expectedExceptionMessage A transition from a place/state must have an unique name.
14+
*/
15+
public function testWithMultipleTransitionWithSameNameShareInput()
16+
{
17+
$places = array('a', 'b', 'c');
18+
$transitions[] = new Transition('t1', 'a', 'b');
19+
$transitions[] = new Transition('t1', 'a', 'c');
20+
$definition = new Definition($places, $transitions);
21+
22+
(new StateMachineValidator())->validate($definition, 'foo');
23+
24+
// The graph looks like:
25+
//
26+
// +----+ +----+ +---+
27+
// | a | --> | t1 | --> | b |
28+
// +----+ +----+ +---+
29+
// |
30+
// |
31+
// v
32+
// +----+ +----+
33+
// | t1 | --> | c |
34+
// +----+ +----+
35+
}
36+
37+
/**
38+
* @expectedException \Symfony\Component\Workflow\Exception\InvalidDefinitionException
39+
* @expectedExceptionMessage A transition in StateMachine can only have one output.
40+
*/
41+
public function testWithMultipleTos()
42+
{
43+
$places = array('a', 'b', 'c');
44+
$transitions[] = new Transition('t1', 'a', array('b', 'c'));
45+
$definition = new Definition($places, $transitions);
46+
47+
(new StateMachineValidator())->validate($definition, 'foo');
48+
49+
// The graph looks like:
50+
//
51+
// +---+ +----+ +---+
52+
// | a | --> | t1 | --> | b |
53+
// +---+ +----+ +---+
54+
// |
55+
// |
56+
// v
57+
// +----+
58+
// | c |
59+
// +----+
60+
}
61+
62+
/**
63+
* @expectedException \Symfony\Component\Workflow\Exception\InvalidDefinitionException
64+
* @expectedExceptionMessage A transition in StateMachine can only have one input.
65+
*/
66+
public function testWithMultipleFroms()
67+
{
68+
$places = array('a', 'b', 'c');
69+
$transitions[] = new Transition('t1', array('a', 'b'), 'c');
70+
$definition = new Definition($places, $transitions);
71+
72+
(new StateMachineValidator())->validate($definition, 'foo');
73+
74+
// The graph looks like:
75+
//
76+
// +---+ +----+ +---+
77+
// | a | --> | t1 | --> | c |
78+
// +---+ +----+ +---+
79+
// ^
80+
// |
81+
// |
82+
// +----+
83+
// | b |
84+
// +----+
85+
}
86+
87+
public function testValid()
88+
{
89+
$places = array('a', 'b', 'c');
90+
$transitions[] = new Transition('t1', 'a', 'b');
91+
$transitions[] = new Transition('t2', 'a', 'c');
92+
$definition = new Definition($places, $transitions);
93+
94+
(new StateMachineValidator())->validate($definition, 'foo');
95+
96+
// The graph looks like:
97+
//
98+
// +----+ +----+ +---+
99+
// | a | --> | t1 | --> | b |
100+
// +----+ +----+ +---+
101+
// |
102+
// |
103+
// v
104+
// +----+ +----+
105+
// | t2 | --> | c |
106+
// +----+ +----+
107+
}
108+
}

0 commit comments

Comments
 (0)