├── FUNDING.yml ├── .gitignore ├── docs ├── images │ ├── graphviz.png │ └── petrinet_builder.png └── documentation.md ├── .travis.yml ├── src └── Petrinet │ ├── Model │ ├── InputArc.php │ ├── OutputArc.php │ ├── InputArcInterface.php │ ├── OutputArcInterface.php │ ├── TokenInterface.php │ ├── Token.php │ ├── PlaceInterface.php │ ├── TransitionInterface.php │ ├── Place.php │ ├── Transition.php │ ├── NodeInterface.php │ ├── MarkingInterface.php │ ├── PetrinetInterface.php │ ├── PlaceMarkingInterface.php │ ├── ArcInterface.php │ ├── FactoryInterface.php │ ├── AbstractArc.php │ ├── PlaceMarking.php │ ├── AbstractNode.php │ ├── Marking.php │ ├── Petrinet.php │ └── Factory.php │ ├── Service │ ├── Exception │ │ └── TransitionNotEnabledException.php │ ├── TransitionServiceInterface.php │ └── TransitionService.php │ ├── Dumper │ ├── DumperInterface.php │ └── GraphvizDumper.php │ └── Builder │ ├── MarkingBuilder.php │ └── PetrinetBuilder.php ├── spec └── Petrinet │ ├── Model │ ├── InputArcSpec.php │ ├── OutputArcSpec.php │ ├── MarkingSpec.php │ └── FactorySpec.php │ ├── Builder │ ├── MarkingBuilderSpec.php │ └── PetrinetBuilderSpec.php │ ├── Dumper │ └── GraphvizDumperSpec.php │ └── Service │ └── TransitionServiceSpec.php ├── composer.json ├── LICENSE └── README.md /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: florianv 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | vendor 3 | composer.lock 4 | -------------------------------------------------------------------------------- /docs/images/graphviz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florianv/petrinet/HEAD/docs/images/graphviz.png -------------------------------------------------------------------------------- /docs/images/petrinet_builder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florianv/petrinet/HEAD/docs/images/petrinet_builder.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.3 5 | - 7.4 6 | - 8.0 7 | - nightly 8 | 9 | matrix: 10 | fast_finish: true 11 | allow_failures: 12 | - php: nightly 13 | 14 | before_script: 15 | - composer --prefer-source --dev install 16 | 17 | script: 18 | - php bin/phpspec run 19 | -------------------------------------------------------------------------------- /src/Petrinet/Model/InputArc.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Implementation of InputArcInterface. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | class InputArc extends AbstractArc implements InputArcInterface 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /src/Petrinet/Model/OutputArc.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Implementation of OutputArcInterface. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | class OutputArc extends AbstractArc implements OutputArcInterface 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /src/Petrinet/Model/InputArcInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Interface for input arcs (from a place to a transition). 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | interface InputArcInterface extends ArcInterface 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /src/Petrinet/Model/OutputArcInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Interface for output arcs (from a transition to a place). 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | interface OutputArcInterface extends ArcInterface 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /src/Petrinet/Service/Exception/TransitionNotEnabledException.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Service\Exception; 13 | 14 | /** 15 | * Exception thrown when trying to fire a disabled transition. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | class TransitionNotEnabledException extends \RuntimeException 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /src/Petrinet/Model/TokenInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Interface for tokens. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | interface TokenInterface 20 | { 21 | /** 22 | * Gets the id. 23 | * 24 | * @return integer 25 | */ 26 | public function getId(); 27 | } 28 | -------------------------------------------------------------------------------- /spec/Petrinet/Model/InputArcSpec.php: -------------------------------------------------------------------------------- 1 | shouldHaveType('Petrinet\Model\InputArc'); 13 | } 14 | 15 | function it_sets_the_weight() 16 | { 17 | $this->setWeight(3); 18 | $this->getWeight()->shouldReturn(3); 19 | } 20 | 21 | function it_throws_an_exception_when_setting_a_negative_weight() 22 | { 23 | $this 24 | ->shouldThrow(new \InvalidArgumentException('The weight must be a positive integer.')) 25 | ->duringSetWeight(-1) 26 | ; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /spec/Petrinet/Model/OutputArcSpec.php: -------------------------------------------------------------------------------- 1 | shouldHaveType('Petrinet\Model\OutputArc'); 13 | } 14 | 15 | function it_sets_the_weight() 16 | { 17 | $this->setWeight(3); 18 | $this->getWeight()->shouldReturn(3); 19 | } 20 | 21 | function it_throws_an_exception_when_setting_a_negative_weight() 22 | { 23 | $this 24 | ->shouldThrow(new \InvalidArgumentException('The weight must be a positive integer.')) 25 | ->duringSetWeight(-1) 26 | ; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Petrinet/Model/Token.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Implementation of TokenInterface. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | class Token implements TokenInterface 20 | { 21 | /** 22 | * The id. 23 | * 24 | * @var integer 25 | */ 26 | protected $id; 27 | 28 | /** 29 | * Gets the id. 30 | * 31 | * @return integer 32 | */ 33 | public function getId() 34 | { 35 | return $this->id; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Petrinet/Model/PlaceInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Interface for places. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | interface PlaceInterface extends NodeInterface 20 | { 21 | /** 22 | * Adds an input arc. 23 | * 24 | * @param OutputArcInterface $arc 25 | */ 26 | public function addInputArc(OutputArcInterface $arc); 27 | 28 | /** 29 | * Adds an output arc. 30 | * 31 | * @param InputArcInterface $arc 32 | */ 33 | public function addOutputArc(InputArcInterface $arc); 34 | } 35 | -------------------------------------------------------------------------------- /src/Petrinet/Model/TransitionInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Interface for transitions. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | interface TransitionInterface extends NodeInterface 20 | { 21 | /** 22 | * Adds an input arc. 23 | * 24 | * @param InputArcInterface $arc 25 | */ 26 | public function addInputArc(InputArcInterface $arc); 27 | 28 | /** 29 | * Adds an output arc. 30 | * 31 | * @param OutputArcInterface $arc 32 | */ 33 | public function addOutputArc(OutputArcInterface $arc); 34 | } 35 | -------------------------------------------------------------------------------- /src/Petrinet/Dumper/DumperInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Dumper; 13 | 14 | use Petrinet\Model\MarkingInterface; 15 | use Petrinet\Model\PetrinetInterface; 16 | 17 | /** 18 | * Interface for Petrinet dumpers. 19 | * 20 | * @author Florian Voutzinos 21 | */ 22 | interface DumperInterface 23 | { 24 | /** 25 | * Dumps a Petrinet. 26 | * 27 | * @param PetrinetInterface $petrinet 28 | * @param MarkingInterface $marking 29 | * 30 | * @return string 31 | */ 32 | public function dump(PetrinetInterface $petrinet, MarkingInterface $marking = null); 33 | } 34 | -------------------------------------------------------------------------------- /src/Petrinet/Model/Place.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Implementation of PlaceInterface. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | class Place extends AbstractNode implements PlaceInterface 20 | { 21 | /** 22 | * {@inheritdoc} 23 | */ 24 | public function addInputArc(OutputArcInterface $arc) 25 | { 26 | $this->inputArcs[] = $arc; 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function addOutputArc(InputArcInterface $arc) 33 | { 34 | $this->outputArcs[] = $arc; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Petrinet/Model/Transition.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Implementation of TransitionInterface. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | class Transition extends AbstractNode implements TransitionInterface 20 | { 21 | /** 22 | * {@inheritdoc} 23 | */ 24 | public function addInputArc(InputArcInterface $arc) 25 | { 26 | $this->inputArcs[] = $arc; 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function addOutputArc(OutputArcInterface $arc) 33 | { 34 | $this->outputArcs[] = $arc; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "florianv/petrinet", 3 | "type": "library", 4 | "description": "Petrinet framework for PHP 7.2+", 5 | "keywords": ["petrinet", "workflow"], 6 | "homepage": "https://github.com/florianv/petrinet", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Florian Voutzinos", 11 | "email": "florian@voutzinos.com", 12 | "homepage": "http://voutzinos.com" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=7.3", 17 | "doctrine/collections": "^1.6" 18 | }, 19 | "require-dev": { 20 | "phpspec/phpspec": "^6.3 || ^7.0" 21 | }, 22 | "autoload": { 23 | "psr-0": { 24 | "Petrinet\\": "src/" 25 | } 26 | }, 27 | "config": { 28 | "bin-dir": "bin" 29 | }, 30 | "extra": { 31 | "branch-alias": { 32 | "dev-master": "2.0-dev" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Petrinet/Model/NodeInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Interface for nodes (places or transitions). 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | interface NodeInterface 20 | { 21 | /** 22 | * Gets the id. 23 | * 24 | * @return integer 25 | */ 26 | public function getId(); 27 | 28 | /** 29 | * Gets the output arcs. 30 | * 31 | * @return ArcInterface[] 32 | */ 33 | public function getOutputArcs(); 34 | 35 | /** 36 | * Gets the input arcs. 37 | * 38 | * @return ArcInterface[] 39 | */ 40 | public function getInputArcs(); 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 - 2016 Florian Voutzinos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Petrinet/Model/MarkingInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Interface for markings. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | interface MarkingInterface 20 | { 21 | /** 22 | * Gets the id. 23 | * 24 | * @return integer 25 | */ 26 | public function getId(); 27 | 28 | /** 29 | * Gets the marking for a place. 30 | * 31 | * @param PlaceInterface $place 32 | * 33 | * @return PlaceMarkingInterface|null 34 | */ 35 | public function getPlaceMarking(PlaceInterface $place); 36 | 37 | /** 38 | * Adds a place marking. 39 | * 40 | * @param PlaceMarkingInterface 41 | */ 42 | public function addPlaceMarking(PlaceMarkingInterface $marking); 43 | 44 | /** 45 | * Sets the place markings. 46 | * 47 | * @param PlaceMarkingInterface[] 48 | */ 49 | public function setPlaceMarkings($placeMarkings); 50 | } 51 | -------------------------------------------------------------------------------- /src/Petrinet/Model/PetrinetInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Interface for Petrinets. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | interface PetrinetInterface 20 | { 21 | /** 22 | * Gets the id. 23 | * 24 | * @return integer 25 | */ 26 | public function getId(); 27 | 28 | /** 29 | * Sets the transitions. 30 | * 31 | * @param TransitionInterface[] $transitions 32 | */ 33 | public function setTransitions($transitions); 34 | 35 | /** 36 | * Gets the transitions. 37 | * 38 | * @return TransitionInterface[] 39 | */ 40 | public function getTransitions(); 41 | 42 | /** 43 | * Sets the places. 44 | * 45 | * @param PlaceInterface[] $places 46 | */ 47 | public function setPlaces($places); 48 | 49 | /** 50 | * Gets the places. 51 | * 52 | * @return PlaceInterface[] 53 | */ 54 | public function getPlaces(); 55 | } 56 | -------------------------------------------------------------------------------- /src/Petrinet/Service/TransitionServiceInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Service; 13 | 14 | use Petrinet\Model\TransitionInterface; 15 | use Petrinet\Model\MarkingInterface; 16 | 17 | /** 18 | * Contract for transition services. 19 | * 20 | * @author Florian Voutzinos 21 | */ 22 | interface TransitionServiceInterface 23 | { 24 | /** 25 | * Tells if the transition is enabled in the given marking. 26 | * 27 | * @param TransitionInterface $transition 28 | * @param MarkingInterface $marking 29 | * 30 | * @return boolean 31 | */ 32 | public function isEnabled(TransitionInterface $transition, MarkingInterface $marking); 33 | 34 | /** 35 | * Fires a transition in a given marking. 36 | * 37 | * @param TransitionInterface $transition 38 | * @param MarkingInterface $marking 39 | * 40 | * @throws Exception\TransitionNotEnabledException 41 | */ 42 | public function fire(TransitionInterface $transition, MarkingInterface $marking); 43 | } 44 | -------------------------------------------------------------------------------- /src/Petrinet/Model/PlaceMarkingInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Interface for place markings. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | interface PlaceMarkingInterface 20 | { 21 | /** 22 | * Gets the id. 23 | * 24 | * @return integer 25 | */ 26 | public function getId(); 27 | 28 | /** 29 | * Sets the place. 30 | * 31 | * @param PlaceInterface $place 32 | */ 33 | public function setPlace(PlaceInterface $place); 34 | 35 | /** 36 | * Gets the place. 37 | * 38 | * @return PlaceInterface 39 | */ 40 | public function getPlace(); 41 | 42 | /** 43 | * Removes the given token. 44 | * 45 | * @param TokenInterface $token 46 | */ 47 | public function removeToken(TokenInterface $token); 48 | 49 | /** 50 | * Sets the tokens. 51 | * 52 | * @param TokenInterface[] $tokens 53 | */ 54 | public function setTokens($tokens); 55 | 56 | /** 57 | * Gets the tokens. 58 | * 59 | * @return TokenInterface[] 60 | */ 61 | public function getTokens(); 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Petrinet [![Build status][travis-image]][travis-url] [![Version][version-image]][version-url] [![PHP Version][php-version-image]][php-version-url] 2 | 3 | This framework allows to build, vizualize and execute [Petrinets](http://en.wikipedia.org/wiki/Petri_net) 4 | which can be used to build workflow engines. It provides the core domain model of basic Petrinets that can be persisted 5 | using your favorite ORM as well as services to manage its execution. 6 | 7 | ## Installation 8 | 9 | ```bash 10 | $ composer require florianv/petrinet 11 | ``` 12 | 13 | ## Documentation 14 | 15 | [Read the documentation for master](https://github.com/florianv/petrinet/blob/master/docs/documentation.md) 16 | 17 | [Read the documentation for the 1.0 version](https://github.com/florianv/petrinet/blob/1.0/docs/documentation.md) 18 | 19 | ## License 20 | 21 | [MIT](https://github.com/florianv/petrinet/blob/master/LICENSE) 22 | 23 | [travis-url]: https://travis-ci.org/florianv/petrinet 24 | [travis-image]: http://img.shields.io/travis/florianv/petrinet.svg?style=flat 25 | 26 | [license-url]: https://packagist.org/packages/florianv/petrinet 27 | [license-image]: http://img.shields.io/packagist/l/florianv/petrinet.svg?style=flat 28 | 29 | [version-url]: https://packagist.org/packages/florianv/petrinet 30 | [version-image]: http://img.shields.io/packagist/v/florianv/petrinet.svg?style=flat 31 | 32 | [php-version-url]: https://packagist.org/packages/florianv/petrinet 33 | [php-version-image]: http://img.shields.io/badge/php-7.3.0+-ff69b4.svg 34 | -------------------------------------------------------------------------------- /src/Petrinet/Model/ArcInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Interface for arcs. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | interface ArcInterface 20 | { 21 | /** 22 | * Gets the id. 23 | * 24 | * @return integer 25 | */ 26 | public function getId(); 27 | 28 | /** 29 | * Sets the place. 30 | * 31 | * @param PlaceInterface $place 32 | */ 33 | public function setPlace(PlaceInterface $place); 34 | 35 | /** 36 | * Gets the place. 37 | * 38 | * @return PlaceInterface 39 | */ 40 | public function getPlace(); 41 | 42 | /** 43 | * Sets the transition. 44 | * 45 | * @param TransitionInterface $transition 46 | */ 47 | public function setTransition(TransitionInterface $transition); 48 | 49 | /** 50 | * Gets the transition. 51 | * 52 | * @return TransitionInterface 53 | */ 54 | public function getTransition(); 55 | 56 | /** 57 | * Sets the weight. 58 | * 59 | * @param integer $weight 60 | */ 61 | public function setWeight($weight); 62 | 63 | /** 64 | * Gets the weight. 65 | * 66 | * @return integer 67 | */ 68 | public function getWeight(); 69 | } 70 | -------------------------------------------------------------------------------- /src/Petrinet/Model/FactoryInterface.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * The factory is the place where model instances are created. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | interface FactoryInterface 20 | { 21 | /** 22 | * Creates a new Petrinet instance. 23 | * 24 | * @return PetrinetInterface 25 | */ 26 | public function createPetrinet(); 27 | 28 | /** 29 | * Creates a new place instance. 30 | * 31 | * @return PlaceInterface 32 | */ 33 | public function createPlace(); 34 | 35 | /** 36 | * Creates a new transition instance. 37 | * 38 | * @return TransitionInterface 39 | */ 40 | public function createTransition(); 41 | 42 | /** 43 | * Creates a new input arc instance. 44 | * 45 | * @return InputArcInterface 46 | */ 47 | public function createInputArc(); 48 | 49 | /** 50 | * Creates a new output arc instance. 51 | * 52 | * @return OutputArcInterface 53 | */ 54 | public function createOutputArc(); 55 | 56 | /** 57 | * Creates a new place marking instance. 58 | * 59 | * @return PlaceMarkingInterface 60 | */ 61 | public function createPlaceMarking(); 62 | 63 | /** 64 | * Creates a new token instance. 65 | * 66 | * @return TokenInterface 67 | */ 68 | public function createToken(); 69 | 70 | /** 71 | * Creates a new marking instance. 72 | * 73 | * @return MarkingInterface 74 | */ 75 | public function createMarking(); 76 | } 77 | -------------------------------------------------------------------------------- /src/Petrinet/Model/AbstractArc.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Base class for the arcs. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | abstract class AbstractArc implements ArcInterface 20 | { 21 | /** 22 | * The id. 23 | * 24 | * @var integer 25 | */ 26 | protected $id; 27 | 28 | /** 29 | * The place. 30 | * 31 | * @var PlaceInterface 32 | */ 33 | protected $place; 34 | 35 | /** 36 | * The transition. 37 | * 38 | * @var TransitionInterface 39 | */ 40 | protected $transition; 41 | 42 | /** 43 | * The weight. 44 | * 45 | * @var integer 46 | */ 47 | protected $weight; 48 | 49 | /** 50 | * Gets the id. 51 | * 52 | * @return integer 53 | */ 54 | public function getId() 55 | { 56 | return $this->id; 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | public function setPlace(PlaceInterface $place) 63 | { 64 | $this->place = $place; 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | */ 70 | public function getPlace() 71 | { 72 | return $this->place; 73 | } 74 | 75 | /** 76 | * {@inheritdoc} 77 | */ 78 | public function setTransition(TransitionInterface $transition) 79 | { 80 | $this->transition = $transition; 81 | } 82 | 83 | /** 84 | * {@inheritdoc} 85 | */ 86 | public function getTransition() 87 | { 88 | return $this->transition; 89 | } 90 | 91 | /** 92 | * {@inheritdoc} 93 | */ 94 | public function setWeight($weight) 95 | { 96 | if ($weight < 0) { 97 | throw new \InvalidArgumentException('The weight must be a positive integer.'); 98 | } 99 | 100 | $this->weight = $weight; 101 | } 102 | 103 | /** 104 | * {@inheritdoc} 105 | */ 106 | public function getWeight() 107 | { 108 | return $this->weight; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Petrinet/Builder/MarkingBuilder.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Builder; 13 | 14 | use Petrinet\Model\FactoryInterface; 15 | use Petrinet\Model\PlaceInterface; 16 | use Petrinet\Model\TokenInterface; 17 | 18 | /** 19 | * Helps building markings. 20 | * 21 | * @author Florian Voutzinos 22 | */ 23 | class MarkingBuilder 24 | { 25 | /** 26 | * The created place markings. 27 | * 28 | * @var \Petrinet\Model\PlaceMarkingInterface[] 29 | */ 30 | private $placeMarkings = array(); 31 | 32 | /** 33 | * The factory. 34 | * 35 | * @var FactoryInterface 36 | */ 37 | private $factory; 38 | 39 | /** 40 | * Creates a new marking builder. 41 | * 42 | * @param FactoryInterface $factory 43 | */ 44 | public function __construct(FactoryInterface $factory) 45 | { 46 | $this->factory = $factory; 47 | } 48 | 49 | /** 50 | * Marks the place with the specified tokens. 51 | * 52 | * @param PlaceInterface $place 53 | * @param \Petrinet\Model\TokenInterface|array|integer $tokens 54 | * 55 | * @return MarkingBuilder 56 | * 57 | * @throws \InvalidArgumentException 58 | */ 59 | public function mark(PlaceInterface $place, $tokens) 60 | { 61 | if (is_int($tokens)) { 62 | $tokensCount = $tokens; 63 | $tokens = array(); 64 | 65 | for ($i = 0; $i < $tokensCount; $i++) { 66 | $tokens[] = $this->factory->createToken(); 67 | } 68 | } elseif ($tokens instanceof TokenInterface) { 69 | $tokens = array($tokens); 70 | } elseif (!is_array($tokens)) { 71 | throw new \InvalidArgumentException( 72 | 'The $tokens argument must be an array, integer or a Petrinet\Model\TokenInterface instance.' 73 | ); 74 | } 75 | 76 | $placeMarking = $this->factory->createPlaceMarking(); 77 | $placeMarking->setPlace($place); 78 | $placeMarking->setTokens($tokens); 79 | 80 | $this->placeMarkings[] = $placeMarking; 81 | 82 | return $this; 83 | } 84 | 85 | /** 86 | * Gets the created marking. 87 | * 88 | * @return \Petrinet\Model\MarkingInterface 89 | */ 90 | public function getMarking() 91 | { 92 | $marking = $this->factory->createMarking(); 93 | $marking->setPlaceMarkings($this->placeMarkings); 94 | 95 | return $marking; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Petrinet/Model/PlaceMarking.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | use Doctrine\Common\Collections\ArrayCollection; 15 | 16 | /** 17 | * Implementation of PlaceMarkingInterface. 18 | * 19 | * @author Florian Voutzinos 20 | */ 21 | class PlaceMarking implements PlaceMarkingInterface 22 | { 23 | /** 24 | * The id. 25 | * 26 | * @var integer 27 | */ 28 | protected $id; 29 | 30 | /** 31 | * The place. 32 | * 33 | * @var PlaceInterface 34 | */ 35 | protected $place; 36 | 37 | /** 38 | * The tokens. 39 | * 40 | * @var ArrayCollection 41 | */ 42 | protected $tokens; 43 | 44 | /** 45 | * Creates a new place marking. 46 | */ 47 | public function __construct() 48 | { 49 | $this->tokens = new ArrayCollection(); 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | */ 55 | public function getId() 56 | { 57 | return $this->id; 58 | } 59 | 60 | /** 61 | * Adds a token. 62 | * 63 | * @param TokenInterface $token 64 | */ 65 | public function addToken(TokenInterface $token) 66 | { 67 | $this->tokens[] = $token; 68 | } 69 | 70 | /** 71 | * Tells if the place marking has the given token. 72 | * 73 | * @param TokenInterface $token 74 | * 75 | * @return boolean 76 | */ 77 | public function hasToken(TokenInterface $token) 78 | { 79 | return $this->tokens->contains($token); 80 | } 81 | 82 | /** 83 | * {@inheritdoc} 84 | */ 85 | public function removeToken(TokenInterface $token) 86 | { 87 | $this->tokens->removeElement($token); 88 | } 89 | 90 | /** 91 | * {@inheritdoc} 92 | */ 93 | public function setTokens($tokens) 94 | { 95 | $this->tokens = new ArrayCollection(); 96 | 97 | foreach ($tokens as $token) { 98 | $this->addToken($token); 99 | } 100 | } 101 | 102 | /** 103 | * {@inheritdoc} 104 | */ 105 | public function getTokens() 106 | { 107 | return $this->tokens; 108 | } 109 | 110 | /** 111 | * {@inheritdoc} 112 | */ 113 | public function setPlace(PlaceInterface $place) 114 | { 115 | $this->place = $place; 116 | } 117 | 118 | /** 119 | * {@inheritdoc} 120 | */ 121 | public function getPlace() 122 | { 123 | return $this->place; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Petrinet/Model/AbstractNode.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | use Doctrine\Common\Collections\ArrayCollection; 15 | 16 | /** 17 | * Base class for the nodes. 18 | * 19 | * @author Florian Voutzinos 20 | */ 21 | abstract class AbstractNode implements NodeInterface 22 | { 23 | /** 24 | * The id. 25 | * 26 | * @var integer 27 | */ 28 | protected $id; 29 | 30 | /** 31 | * The input arcs. 32 | * 33 | * @var ArrayCollection 34 | */ 35 | protected $inputArcs; 36 | 37 | /** 38 | * The output arcs. 39 | * 40 | * @var ArrayCollection 41 | */ 42 | protected $outputArcs; 43 | 44 | /** 45 | * Creates a new abstract node. 46 | */ 47 | public function __construct() 48 | { 49 | $this->inputArcs = new ArrayCollection(); 50 | $this->outputArcs = new ArrayCollection(); 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function getId() 57 | { 58 | return $this->id; 59 | } 60 | 61 | /** 62 | * Tells if the node has the input arc. 63 | * 64 | * @param ArcInterface $inputArc 65 | * 66 | * @return boolean 67 | */ 68 | public function hasInputArc(ArcInterface $inputArc) 69 | { 70 | return $this->inputArcs->contains($inputArc); 71 | } 72 | 73 | /** 74 | * Removes the input arc. 75 | * 76 | * @param ArcInterface $inputArc 77 | */ 78 | public function removeInputArc(ArcInterface $inputArc) 79 | { 80 | $this->inputArcs->removeElement($inputArc); 81 | } 82 | 83 | /** 84 | * {@inheritdoc} 85 | */ 86 | public function getInputArcs() 87 | { 88 | return $this->inputArcs; 89 | } 90 | 91 | /** 92 | * Tells if the node has the output arc. 93 | * 94 | * @param ArcInterface $outputArc 95 | * 96 | * @return boolean 97 | */ 98 | public function hasOutputArc(ArcInterface $outputArc) 99 | { 100 | return $this->outputArcs->contains($outputArc); 101 | } 102 | 103 | /** 104 | * Removes the output arc. 105 | * 106 | * @param ArcInterface $outputArc 107 | */ 108 | public function removeOutputArc(ArcInterface $outputArc) 109 | { 110 | $this->outputArcs->removeElement($outputArc); 111 | } 112 | 113 | /** 114 | * {@inheritdoc} 115 | */ 116 | public function getOutputArcs() 117 | { 118 | return $this->outputArcs; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/Petrinet/Model/Marking.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | use Doctrine\Common\Collections\ArrayCollection; 15 | 16 | /** 17 | * Implementation of MarkingInterface. 18 | * 19 | * @author Florian Voutzinos 20 | */ 21 | class Marking implements MarkingInterface 22 | { 23 | /** 24 | * The id. 25 | * 26 | * @var integer 27 | */ 28 | protected $id; 29 | 30 | /** 31 | * The place markings. 32 | * 33 | * @var PlaceMarkingInterface[] 34 | */ 35 | protected $placeMarkings; 36 | 37 | /** 38 | * Creates a new marking. 39 | */ 40 | public function __construct() 41 | { 42 | $this->placeMarkings = new ArrayCollection(); 43 | } 44 | 45 | /** 46 | * Gets the id. 47 | * 48 | * @return integer 49 | */ 50 | public function getId() 51 | { 52 | return $this->id; 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | */ 58 | public function getPlaceMarking(PlaceInterface $place) 59 | { 60 | foreach ($this->placeMarkings as $placeMarking) { 61 | if ($placeMarking->getPlace() === $place 62 | || null !== $place->getId() && $placeMarking->getPlace()->getId() === $place->getId()) { 63 | return $placeMarking; 64 | } 65 | } 66 | 67 | return null; 68 | } 69 | 70 | /** 71 | * {@inheritdoc} 72 | */ 73 | public function addPlaceMarking(PlaceMarkingInterface $marking) 74 | { 75 | foreach ($this->placeMarkings as $placeMarking) { 76 | $place = $placeMarking->getPlace(); 77 | 78 | if ($place === $marking->getPlace() 79 | || null !== $place->getId() && $place->getId() === $marking->getPlace()->getId()) { 80 | throw new \InvalidArgumentException('Cannot add two markings for the same place.'); 81 | } 82 | } 83 | 84 | $this->placeMarkings->add($marking); 85 | } 86 | 87 | /** 88 | * {@inheritdoc} 89 | */ 90 | public function setPlaceMarkings($placeMarkings) 91 | { 92 | $this->placeMarkings = new ArrayCollection(); 93 | 94 | foreach ($placeMarkings as $placeMarking) { 95 | $this->addPlaceMarking($placeMarking); 96 | } 97 | } 98 | 99 | /** 100 | * Removes a place marking. 101 | * 102 | * @param PlaceMarkingInterface $placeMarking 103 | */ 104 | public function removePlaceMarking(PlaceMarkingInterface $placeMarking) 105 | { 106 | $this->placeMarkings->removeElement($placeMarking); 107 | } 108 | 109 | /** 110 | * Gets the place markings. 111 | * 112 | * @return PlaceMarkingInterface[] 113 | */ 114 | public function getPlaceMarkings() 115 | { 116 | return $this->placeMarkings; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Petrinet/Service/TransitionService.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Service; 13 | 14 | use Petrinet\Model\FactoryInterface; 15 | use Petrinet\Model\MarkingInterface; 16 | use Petrinet\Model\TransitionInterface; 17 | use Petrinet\Service\Exception\TransitionNotEnabledException; 18 | 19 | /** 20 | * Implementation of the TransitionServiceInterface. 21 | * 22 | * @author Florian Voutzinos 23 | */ 24 | class TransitionService implements TransitionServiceInterface 25 | { 26 | /** 27 | * The factory. 28 | * 29 | * @var FactoryInterface 30 | */ 31 | private $factory; 32 | 33 | /** 34 | * Creates a new transition service. 35 | * 36 | * @param FactoryInterface $factory 37 | */ 38 | public function __construct(FactoryInterface $factory) 39 | { 40 | $this->factory = $factory; 41 | } 42 | 43 | /** 44 | * {@inheritdoc} 45 | */ 46 | public function isEnabled(TransitionInterface $transition, MarkingInterface $marking) 47 | { 48 | $inputArcs = $transition->getInputArcs(); 49 | 50 | if (count($inputArcs) === 0) { 51 | return false; 52 | } 53 | 54 | foreach ($inputArcs as $inputArc) { 55 | $place = $inputArc->getPlace(); 56 | $placeMarking = $marking->getPlaceMarking($place); 57 | 58 | if (null === $placeMarking) { 59 | return false; 60 | } 61 | 62 | if (count($placeMarking->getTokens()) < $inputArc->getWeight()) { 63 | return false; 64 | } 65 | } 66 | 67 | return true; 68 | } 69 | 70 | /** 71 | * {@inheritdoc} 72 | */ 73 | public function fire(TransitionInterface $transition, MarkingInterface $marking) 74 | { 75 | if (!$this->isEnabled($transition, $marking)) { 76 | throw new TransitionNotEnabledException('Cannot fire a disabled transition.'); 77 | } 78 | 79 | $inputArcs = $transition->getInputArcs(); 80 | $outputArcs = $transition->getOutputArcs(); 81 | 82 | // Remove tokens from the input places 83 | foreach ($inputArcs as $arc) { 84 | $arcWeight = $arc->getWeight(); 85 | $place = $arc->getPlace(); 86 | $placeMarking = $marking->getPlaceMarking($place); 87 | $tokens = $placeMarking->getTokens(); 88 | 89 | for ($i = 0; $i < $arcWeight; $i++) { 90 | $placeMarking->removeToken($tokens[$i]); 91 | } 92 | } 93 | 94 | // Add tokens to the output places 95 | foreach ($outputArcs as $arc) { 96 | $arcWeight = $arc->getWeight(); 97 | $place = $arc->getPlace(); 98 | $placeMarking = $marking->getPlaceMarking($place); 99 | 100 | if (null === $placeMarking) { 101 | $placeMarking = $this->factory->createPlaceMarking(); 102 | $placeMarking->setPlace($place); 103 | $marking->addPlaceMarking($placeMarking); 104 | } 105 | 106 | // Create the tokens 107 | $tokens = array(); 108 | for ($i = 0; $i < $arcWeight; $i++) { 109 | $tokens[] = $this->factory->createToken(); 110 | } 111 | 112 | $placeMarking->setTokens($tokens); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Petrinet/Builder/PetrinetBuilder.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Builder; 13 | 14 | use Petrinet\Model\FactoryInterface; 15 | use Petrinet\Model\NodeInterface; 16 | use Petrinet\Model\PlaceInterface; 17 | use Petrinet\Model\TransitionInterface; 18 | 19 | /** 20 | * Helps building Petrinets. 21 | * 22 | * @author Florian Voutzinos 23 | */ 24 | class PetrinetBuilder 25 | { 26 | /** 27 | * The created places. 28 | * 29 | * @var PlaceInterface[] 30 | */ 31 | private $places = array(); 32 | 33 | /** 34 | * The created transitions. 35 | * 36 | * @var TransitionInterface[] 37 | */ 38 | private $transitions = array(); 39 | 40 | /** 41 | * The factory. 42 | * 43 | * @var FactoryInterface 44 | */ 45 | private $factory; 46 | 47 | /** 48 | * Creates a new Petrinet builder. 49 | * 50 | * @param FactoryInterface $factory 51 | */ 52 | public function __construct(FactoryInterface $factory) 53 | { 54 | $this->factory = $factory; 55 | } 56 | 57 | /** 58 | * Creates a place. 59 | * 60 | * @return \Petrinet\Model\PlaceInterface 61 | */ 62 | public function place() 63 | { 64 | $place = $this->factory->createPlace(); 65 | $this->places[] = $place; 66 | 67 | return $place; 68 | } 69 | 70 | /** 71 | * Creates a transition. 72 | * 73 | * @return \Petrinet\Model\TransitionInterface 74 | */ 75 | public function transition() 76 | { 77 | $transition = $this->factory->createTransition(); 78 | $this->transitions[] = $transition; 79 | 80 | return $transition; 81 | } 82 | 83 | /** 84 | * Connects a place to a transition or vice-versa by an arc of the specified weight. 85 | * 86 | * @param NodeInterface $source 87 | * @param NodeInterface $target 88 | * @param integer $weight 89 | * 90 | * @return $this 91 | * 92 | * @throws \InvalidArgumentException 93 | */ 94 | public function connect(NodeInterface $source, NodeInterface $target, $weight = 1) 95 | { 96 | if ($source instanceof PlaceInterface && $target instanceof TransitionInterface) { 97 | $arc = $this->factory->createInputArc(); 98 | $arc->setPlace($source); 99 | $arc->setTransition($target); 100 | } elseif ($source instanceof TransitionInterface && $target instanceof PlaceInterface) { 101 | $arc = $this->factory->createOutputArc(); 102 | $arc->setPlace($target); 103 | $arc->setTransition($source); 104 | } else { 105 | throw new \InvalidArgumentException('An arc must connect a place to a transition or vice-versa.'); 106 | } 107 | 108 | $arc->setWeight($weight); 109 | $source->addOutputArc($arc); 110 | $target->addInputArc($arc); 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * Gets the created Petrinet. 117 | * 118 | * @return \Petrinet\Model\PetrinetInterface 119 | */ 120 | public function getPetrinet() 121 | { 122 | $petrinet = $this->factory->createPetrinet(); 123 | $petrinet->setPlaces($this->places); 124 | $petrinet->setTransitions($this->transitions); 125 | 126 | return $petrinet; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /spec/Petrinet/Model/MarkingSpec.php: -------------------------------------------------------------------------------- 1 | shouldHaveType('Petrinet\Model\Marking'); 15 | } 16 | 17 | function it_gets_the_place_marking_when_places_have_ids( 18 | PlaceInterface $placeOne, 19 | PlaceInterface $placeTwo, 20 | PlaceMarkingInterface $markingOne, 21 | PlaceMarkingInterface $markingTwo 22 | ) 23 | { 24 | $placeOne->getId()->willReturn(5); 25 | $placeTwo->getId()->willReturn(10); 26 | 27 | $markingOne->getPlace()->willReturn($placeOne); 28 | $markingTwo->getPlace()->willReturn($placeTwo); 29 | 30 | $this->addPlaceMarking($markingOne); 31 | $this->addPlaceMarking($markingTwo); 32 | 33 | $this->getPlaceMarking($placeOne)->shouldReturn($markingOne); 34 | $this->getPlaceMarking($placeTwo)->shouldReturn($markingTwo); 35 | } 36 | 37 | function it_gets_the_place_marking_when_places_have_no_id_but_same_instance( 38 | PlaceInterface $place, 39 | PlaceMarkingInterface $marking 40 | ) 41 | { 42 | $place->getId()->willReturn(null); 43 | $marking->getPlace()->willReturn($place); 44 | 45 | $this->addPlaceMarking($marking); 46 | $this->getPlaceMarking($place)->shouldReturn($marking); 47 | } 48 | 49 | function it_does_not_get_the_marking_when_there_is_none_and_place_id_is_null( 50 | PlaceInterface $placeOne, 51 | PlaceInterface $placeTwo, 52 | PlaceMarkingInterface $marking 53 | ) 54 | { 55 | $placeOne->getId()->willReturn(null); 56 | $placeTwo->getId()->willReturn(null); 57 | 58 | $marking->getPlace()->willReturn($placeOne); 59 | 60 | $this->addPlaceMarking($marking); 61 | $this->getPlaceMarking($placeOne)->shouldReturn($marking); 62 | $this->getPlaceMarking($placeTwo)->shouldReturn(null); 63 | } 64 | 65 | function it_adds_two_place_markings_when_places_have_no_id_but_different_instances( 66 | PlaceInterface $placeOne, 67 | PlaceInterface $placeTwo, 68 | PlaceMarkingInterface $markingOne, 69 | PlaceMarkingInterface $markingTwo 70 | ) 71 | { 72 | $placeOne->getId()->willReturn(null); 73 | $placeTwo->getId()->willReturn(null); 74 | 75 | $markingOne->getPlace()->willReturn($placeOne); 76 | $markingTwo->getPlace()->willReturn($placeTwo); 77 | 78 | $this->addPlaceMarking($markingOne); 79 | $this->addPlaceMarking($markingTwo); 80 | 81 | $this->getPlaceMarking($placeOne)->shouldReturn($markingOne); 82 | $this->getPlaceMarking($placeTwo)->shouldReturn($markingTwo); 83 | } 84 | 85 | function it_throws_an_exception_when_adding_a_duplicated_place_marking( 86 | PlaceInterface $placeOne, 87 | PlaceInterface $placeTwo, 88 | PlaceMarkingInterface $markingOne, 89 | PlaceMarkingInterface $markingTwo 90 | ) 91 | { 92 | $placeOne->getId()->willReturn(5); 93 | $placeTwo->getId()->willReturn(5); 94 | 95 | $markingOne->getPlace()->willReturn($placeOne); 96 | $markingTwo->getPlace()->willReturn($placeTwo); 97 | 98 | $this->addPlaceMarking($markingOne); 99 | 100 | $this 101 | ->shouldThrow(new \InvalidArgumentException('Cannot add two markings for the same place.')) 102 | ->duringAddPlaceMarking($markingTwo) 103 | ; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Petrinet/Model/Petrinet.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | use Doctrine\Common\Collections\ArrayCollection; 15 | 16 | /** 17 | * Implementation of PetrinetInterface. 18 | * 19 | * @author Florian Voutzinos 20 | */ 21 | class Petrinet implements PetrinetInterface 22 | { 23 | /** 24 | * The id. 25 | * 26 | * @var integer 27 | */ 28 | protected $id; 29 | 30 | /** 31 | * The places. 32 | * 33 | * @var ArrayCollection 34 | */ 35 | protected $places; 36 | 37 | /** 38 | * The transitions. 39 | * 40 | * @var ArrayCollection 41 | */ 42 | protected $transitions; 43 | 44 | /** 45 | * Creates a new Petrinet. 46 | */ 47 | public function __construct() 48 | { 49 | $this->places = new ArrayCollection(); 50 | $this->transitions = new ArrayCollection(); 51 | } 52 | 53 | /** 54 | * Gets the id. 55 | * 56 | * @return integer 57 | */ 58 | public function getId() 59 | { 60 | return $this->id; 61 | } 62 | 63 | /** 64 | * Adds a place. 65 | * 66 | * @param PlaceInterface $place 67 | */ 68 | public function addPlace(PlaceInterface $place) 69 | { 70 | $this->places[] = $place; 71 | } 72 | 73 | /** 74 | * Tells if the Petrinet has the given place. 75 | * 76 | * @param PlaceInterface $place 77 | * 78 | * @return boolean 79 | */ 80 | public function hasPlace(PlaceInterface $place) 81 | { 82 | return $this->places->contains($place); 83 | } 84 | 85 | /** 86 | * Removes a place. 87 | * 88 | * @param PlaceInterface $place 89 | */ 90 | public function removePlace(PlaceInterface $place) 91 | { 92 | $this->places->removeElement($place); 93 | } 94 | 95 | /** 96 | * {@inheritdoc} 97 | */ 98 | public function setPlaces($places) 99 | { 100 | $this->places = new ArrayCollection(); 101 | 102 | foreach ($places as $place) { 103 | $this->addPlace($place); 104 | } 105 | } 106 | 107 | /** 108 | * {@inheritdoc} 109 | */ 110 | public function getPlaces() 111 | { 112 | return $this->places; 113 | } 114 | 115 | /** 116 | * Adds a transition. 117 | * 118 | * @param TransitionInterface $transition 119 | */ 120 | public function addTransition(TransitionInterface $transition) 121 | { 122 | $this->transitions[] = $transition; 123 | } 124 | 125 | /** 126 | * Tells if the Petrinet has the given transition. 127 | * 128 | * @param TransitionInterface $transition 129 | * 130 | * @return boolean 131 | */ 132 | public function hasTransition(TransitionInterface $transition) 133 | { 134 | return $this->transitions->contains($transition); 135 | } 136 | 137 | /** 138 | * Removes a transition. 139 | * 140 | * @param TransitionInterface $transition 141 | */ 142 | public function removeTransition(TransitionInterface $transition) 143 | { 144 | $this->transitions->removeElement($transition); 145 | } 146 | 147 | /** 148 | * {@inheritdoc} 149 | */ 150 | public function setTransitions($transitions) 151 | { 152 | $this->transitions = new ArrayCollection(); 153 | 154 | foreach ($transitions as $transition) { 155 | $this->addTransition($transition); 156 | } 157 | } 158 | 159 | /** 160 | * {@inheritdoc} 161 | */ 162 | public function getTransitions() 163 | { 164 | return $this->transitions; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /spec/Petrinet/Model/FactorySpec.php: -------------------------------------------------------------------------------- 1 | shouldHaveType('Petrinet\Model\Factory'); 13 | } 14 | 15 | function it_creates_a_petrinet() 16 | { 17 | $this->createPetrinet()->shouldBeAnInstanceOf('Petrinet\Model\Petrinet'); 18 | } 19 | 20 | function it_throws_an_exception_if_the_object_is_not_a_petrinet_child() 21 | { 22 | $this->beConstructedWith('\stdClass'); 23 | 24 | $this 25 | ->shouldThrow(new \RuntimeException('The Petrinet class must implement "Petrinet\Model\PetrinetInterface".')) 26 | ->duringCreatePetrinet() 27 | ; 28 | } 29 | 30 | function it_creates_a_place() 31 | { 32 | $this->createPlace()->shouldBeAnInstanceOf('Petrinet\Model\Place'); 33 | } 34 | 35 | function it_throws_an_exception_if_the_object_is_not_a_place_child() 36 | { 37 | $this->beConstructedWith(null, '\stdClass'); 38 | 39 | $this 40 | ->shouldThrow(new \RuntimeException('The place class must implement "Petrinet\Model\PlaceInterface".')) 41 | ->duringCreatePlace() 42 | ; 43 | } 44 | 45 | function it_creates_a_transition() 46 | { 47 | $this->createTransition()->shouldBeAnInstanceOf('Petrinet\Model\Transition'); 48 | } 49 | 50 | function it_throws_an_exception_if_the_object_is_not_a_transition_child() 51 | { 52 | $this->beConstructedWith(null, null, '\stdClass'); 53 | 54 | $this 55 | ->shouldThrow(new \RuntimeException('The transition class must implement "Petrinet\Model\TransitionInterface".')) 56 | ->duringCreateTransition() 57 | ; 58 | } 59 | 60 | function it_creates_an_input_arc() 61 | { 62 | $this->createInputArc()->shouldBeAnInstanceOf('Petrinet\Model\InputArc'); 63 | } 64 | 65 | function it_throws_an_exception_if_the_object_is_not_an_input_arc_child() 66 | { 67 | $this->beConstructedWith(null, null, null, '\stdClass'); 68 | 69 | $this 70 | ->shouldThrow(new \RuntimeException('The input arc class must implement "Petrinet\Model\InputArcInterface".')) 71 | ->duringcreateInputArc() 72 | ; 73 | } 74 | 75 | function it_creates_an_output_arc() 76 | { 77 | $this->createOutputArc()->shouldBeAnInstanceOf('Petrinet\Model\OutputArc'); 78 | } 79 | 80 | function it_throws_an_exception_if_the_object_is_not_an_output_arc_child() 81 | { 82 | $this->beConstructedWith(null, null, null, null, '\stdClass'); 83 | 84 | $this 85 | ->shouldThrow(new \RuntimeException('The output arc class must implement "Petrinet\Model\OutputArcInterface".')) 86 | ->duringcreateOutputArc() 87 | ; 88 | } 89 | 90 | function it_creates_a_place_marking() 91 | { 92 | $this->createPlaceMarking()->shouldBeAnInstanceOf('Petrinet\Model\PlaceMarking'); 93 | } 94 | 95 | function it_throws_an_exception_if_the_object_is_not_a_place_marking_child() 96 | { 97 | $this->beConstructedWith(null, null, null, null, null, '\stdClass'); 98 | 99 | $this 100 | ->shouldThrow(new \RuntimeException('The place marking class must implement "Petrinet\Model\PlaceMarkingInterface".')) 101 | ->duringCreatePlaceMarking() 102 | ; 103 | } 104 | 105 | function it_creates_a_token() 106 | { 107 | $this->createToken()->shouldBeAnInstanceOf('Petrinet\Model\Token'); 108 | } 109 | 110 | function it_throws_an_exception_if_the_object_is_not_a_token_child() 111 | { 112 | $this->beConstructedWith(null, null, null, null, null, null, '\stdClass'); 113 | 114 | $this 115 | ->shouldThrow(new \RuntimeException('The token class must implement "Petrinet\Model\TokenInterface".')) 116 | ->duringCreateToken() 117 | ; 118 | } 119 | 120 | function it_creates_a_marking() 121 | { 122 | $this->createMarking()->shouldBeAnInstanceOf('Petrinet\Model\Marking'); 123 | } 124 | 125 | function it_throws_an_exception_if_the_object_is_not_a_marking_child() 126 | { 127 | $this->beConstructedWith(null, null, null, null, null, null, null, '\stdClass'); 128 | 129 | $this 130 | ->shouldThrow(new \RuntimeException('The marking class must implement "Petrinet\Model\MarkingInterface".')) 131 | ->duringCreateMarking() 132 | ; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /spec/Petrinet/Builder/MarkingBuilderSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith($factory); 18 | $this->shouldHaveType('Petrinet\Builder\MarkingBuilder'); 19 | } 20 | 21 | function it_marks_a_place_with_one_token( 22 | FactoryInterface $factory, 23 | PlaceMarkingInterface $placeMarking, 24 | PlaceInterface $place, 25 | TokenInterface $token 26 | ) 27 | { 28 | $factory->createPlaceMarking()->willReturn($placeMarking)->shouldBeCalled(); 29 | 30 | $placeMarking->setTokens(array($token))->shouldBeCalled(); 31 | $placeMarking->setPlace($place)->shouldBeCalled(); 32 | 33 | $this->beConstructedWith($factory); 34 | $this->mark($place, $token)->shouldReturn($this); 35 | } 36 | 37 | function it_marks_a_place_with_three_tokens( 38 | FactoryInterface $factory, 39 | PlaceMarkingInterface $placeMarking, 40 | PlaceInterface $place, 41 | TokenInterface $tokenOne, 42 | TokenInterface $tokenTwo, 43 | TokenInterface $tokenThree 44 | ) 45 | { 46 | $factory->createPlaceMarking()->willReturn($placeMarking); 47 | 48 | $placeMarking->setTokens(array($tokenOne, $tokenTwo, $tokenThree))->shouldBeCalled(); 49 | $placeMarking->setPlace($place)->shouldBeCalled(); 50 | 51 | $this->beConstructedWith($factory); 52 | $this->mark($place, array($tokenOne, $tokenTwo, $tokenThree))->shouldReturn($this); 53 | } 54 | 55 | function it_marks_a_place_with_the_specified_tokens_number( 56 | FactoryInterface $factory, 57 | TokenInterface $token, 58 | PlaceInterface $place, 59 | PlaceMarkingInterface $placeMarking 60 | ) 61 | { 62 | $placeMarking->setTokens(array($token, $token, $token))->shouldBeCalled(); 63 | $placeMarking->setPlace($place)->shouldBeCalled(); 64 | 65 | $factory->createPlaceMarking()->willReturn($placeMarking); 66 | $factory->createToken()->willReturn($token)->shouldBeCalledTimes(3); 67 | 68 | $this->beConstructedWith($factory); 69 | $this->mark($place, 3)->shouldReturn($this); 70 | } 71 | 72 | function it_throws_an_exception_when_passing_invalid_token_instances( 73 | FactoryInterface $factory, 74 | PlaceInterface $place 75 | ) 76 | { 77 | $this->beConstructedWith($factory); 78 | 79 | $this->shouldThrow( 80 | new \InvalidArgumentException( 81 | 'The $tokens argument must be an array, integer or a Petrinet\Model\TokenInterface instance.' 82 | )) 83 | ->duringMark($place, new \stdClass()); 84 | } 85 | 86 | function it_builds_a_marking_with_three_place_markings( 87 | FactoryInterface $factory, 88 | MarkingInterface $marking, 89 | PlaceMarkingInterface $placeMarkingOne, 90 | PlaceMarkingInterface $placeMarkingTwo, 91 | PlaceMarkingInterface $placeMarkingThree, 92 | PlaceInterface $placeOne, 93 | PlaceInterface $placeTwo, 94 | PlaceInterface $placeThree, 95 | TokenInterface $token 96 | ) 97 | { 98 | $factory->createMarking()->willReturn($marking); 99 | $factory->createPlaceMarking()->willReturn($placeMarkingOne, $placeMarkingTwo, $placeMarkingThree); 100 | $factory->createToken()->willReturn($token)->shouldBeCalledTimes(3); 101 | 102 | $marking->setPlaceMarkings(array($placeMarkingOne, $placeMarkingTwo, $placeMarkingThree))->shouldBeCalled(); 103 | 104 | $placeMarkingOne->setPlace($placeOne)->shouldBeCalled(); 105 | $placeMarkingOne->setTokens(array($token))->shouldBeCalled(); 106 | 107 | $placeMarkingTwo->setPlace($placeTwo)->shouldBeCalled(); 108 | $placeMarkingTwo->setTokens(array($token, $token))->shouldBeCalled(); 109 | 110 | $placeMarkingThree->setPlace($placeThree)->shouldBeCalled(); 111 | $placeMarkingThree->setTokens(array($token, $token, $token))->shouldBeCalled(); 112 | 113 | $this->beConstructedWith($factory); 114 | $this->mark($placeOne, $token); 115 | $this->mark($placeTwo, array($token, $token)); 116 | $this->mark($placeThree, 3); 117 | $this->getMarking()->shouldReturn($marking); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /spec/Petrinet/Builder/PetrinetBuilderSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith($factory); 19 | $this->shouldHaveType('Petrinet\Builder\PetrinetBuilder'); 20 | } 21 | 22 | function it_creates_a_place(FactoryInterface $factory, PlaceInterface $place) 23 | { 24 | $factory->createPlace()->willReturn($place); 25 | 26 | $this->beConstructedWith($factory); 27 | $this->place()->shouldReturn($place); 28 | } 29 | 30 | function it_creates_a_transition(FactoryInterface $factory, TransitionInterface $transition) 31 | { 32 | $factory->createTransition()->willReturn($transition); 33 | 34 | $this->beConstructedWith($factory); 35 | $this->transition()->shouldReturn($transition); 36 | } 37 | 38 | function it_connects_a_place_to_a_transition( 39 | FactoryInterface $factory, 40 | InputArcInterface $arc, 41 | TransitionInterface $transition, 42 | PlaceInterface $place 43 | ) 44 | { 45 | $factory->createPlace()->willReturn($place); 46 | $factory->createTransition()->willReturn($transition); 47 | $factory->createInputArc()->willReturn($arc); 48 | 49 | $arc->setPlace($place)->shouldBeCalled(); 50 | $arc->setTransition($transition)->shouldBeCalled(); 51 | $arc->setWeight(2)->shouldBeCalled(); 52 | 53 | $transition->addInputArc($arc)->shouldBeCalled(); 54 | $place->addOutputArc($arc)->shouldBeCalled(); 55 | 56 | $this->beConstructedWith($factory); 57 | $this->connect($place, $transition, 2)->shouldReturn($this); 58 | } 59 | 60 | function it_connects_a_transition_to_a_place( 61 | FactoryInterface $factory, 62 | OutputArcInterface $arc, 63 | TransitionInterface $transition, 64 | PlaceInterface $place 65 | ) 66 | { 67 | $factory->createPlace()->willReturn($place); 68 | $factory->createTransition()->willReturn($transition); 69 | $factory->createOutputArc()->willReturn($arc); 70 | 71 | $arc->setTransition($transition)->shouldBeCalled(); 72 | $arc->setPlace($place)->shouldBeCalled(); 73 | $arc->setWeight(1)->shouldBeCalled(); 74 | 75 | $transition->addOutputArc($arc)->shouldBeCalled(); 76 | $place->addInputArc($arc)->shouldBeCalled(); 77 | 78 | $this->beConstructedWith($factory); 79 | $this->connect($transition, $place)->shouldReturn($this); 80 | } 81 | 82 | function it_throws_an_excpetion_when_connecting_two_places( 83 | FactoryInterface $factory, 84 | PlaceInterface $placeOne, 85 | PlaceInterface $placeTwo 86 | ) 87 | { 88 | $this->beConstructedWith($factory); 89 | 90 | $this 91 | ->shouldThrow(new \InvalidArgumentException('An arc must connect a place to a transition or vice-versa.')) 92 | ->duringConnect($placeOne, $placeTwo) 93 | ; 94 | } 95 | 96 | function it_throws_an_exception_when_connecting_two_transitions( 97 | FactoryInterface $factory, 98 | TransitionInterface $transitionOne, 99 | TransitionInterface $transitionTwo 100 | ) 101 | { 102 | $this->beConstructedWith($factory); 103 | 104 | $this 105 | ->shouldThrow(new \InvalidArgumentException('An arc must connect a place to a transition or vice-versa.')) 106 | ->duringConnect($transitionOne, $transitionTwo) 107 | ; 108 | } 109 | 110 | function it_builds_a_petrinet( 111 | FactoryInterface $factory, 112 | PetrinetInterface $petrinet, 113 | PlaceInterface $placeOne, 114 | PlaceInterface $placeTwo, 115 | TransitionInterface $transitionOne, 116 | TransitionInterface $transitionTwo 117 | ) 118 | { 119 | $factory->createPetrinet()->willReturn($petrinet); 120 | $factory->createPlace()->willReturn($placeOne, $placeTwo); 121 | $factory->createTransition()->willReturn($transitionOne, $transitionTwo); 122 | 123 | $petrinet->setTransitions(array($transitionOne, $transitionTwo))->shouldBeCalled(); 124 | $petrinet->setPlaces(array($placeOne, $placeTwo))->shouldBeCalled(); 125 | 126 | $this->beConstructedWith($factory); 127 | $this->place(); 128 | $this->place(); 129 | $this->transition(); 130 | $this->transition(); 131 | $this->getPetrinet()->shouldReturn($petrinet); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Petrinet/Model/Factory.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Model; 13 | 14 | /** 15 | * Implementation of FactoryInterface. 16 | * 17 | * @author Florian Voutzinos 18 | */ 19 | class Factory implements FactoryInterface 20 | { 21 | private $petrinetClass; 22 | private $placeClass; 23 | private $transitionClass; 24 | private $inputArcClass; 25 | private $outputArcClass; 26 | private $placeMarkingClass; 27 | private $tokenClass; 28 | private $markingClass; 29 | 30 | /** 31 | * Creates a new factory. 32 | * 33 | * @param string $petrinetClass 34 | * @param string $placeClass 35 | * @param string $transitionClass 36 | * @param string $inputArcClass 37 | * @param string $outputArcClass 38 | * @param string $placeMarkingClass 39 | * @param string $tokenClass 40 | * @param string $markingClass 41 | */ 42 | public function __construct( 43 | $petrinetClass = 'Petrinet\Model\Petrinet', 44 | $placeClass = 'Petrinet\Model\Place', 45 | $transitionClass = 'Petrinet\Model\Transition', 46 | $inputArcClass = 'Petrinet\Model\InputArc', 47 | $outputArcClass = 'Petrinet\Model\OutputArc', 48 | $placeMarkingClass = 'Petrinet\Model\PlaceMarking', 49 | $tokenClass = 'Petrinet\Model\Token', 50 | $markingClass = 'Petrinet\Model\Marking' 51 | ) { 52 | $this->petrinetClass = $petrinetClass; 53 | $this->placeClass = $placeClass; 54 | $this->transitionClass = $transitionClass; 55 | $this->inputArcClass = $inputArcClass; 56 | $this->outputArcClass = $outputArcClass; 57 | $this->placeMarkingClass = $placeMarkingClass; 58 | $this->tokenClass = $tokenClass; 59 | $this->markingClass = $markingClass; 60 | } 61 | 62 | /** 63 | * {@inheritdoc} 64 | */ 65 | public function createPetrinet() 66 | { 67 | $petrinet = new $this->petrinetClass(); 68 | 69 | if (!$petrinet instanceof PetrinetInterface) { 70 | throw new \RuntimeException('The Petrinet class must implement "Petrinet\Model\PetrinetInterface".'); 71 | } 72 | 73 | return $petrinet; 74 | } 75 | 76 | /** 77 | * {@inheritdoc} 78 | */ 79 | public function createPlace() 80 | { 81 | $place = new $this->placeClass(); 82 | 83 | if (!$place instanceof PlaceInterface) { 84 | throw new \RuntimeException('The place class must implement "Petrinet\Model\PlaceInterface".'); 85 | } 86 | 87 | return $place; 88 | } 89 | 90 | /** 91 | * {@inheritdoc} 92 | */ 93 | public function createTransition() 94 | { 95 | $transition = new $this->transitionClass(); 96 | 97 | if (!$transition instanceof TransitionInterface) { 98 | throw new \RuntimeException('The transition class must implement "Petrinet\Model\TransitionInterface".'); 99 | } 100 | 101 | return $transition; 102 | } 103 | 104 | /** 105 | * {@inheritdoc} 106 | */ 107 | public function createInputArc() 108 | { 109 | $arc = new $this->inputArcClass(); 110 | 111 | if (!$arc instanceof InputArcInterface) { 112 | throw new \RuntimeException('The input arc class must implement "Petrinet\Model\InputArcInterface".'); 113 | } 114 | 115 | return $arc; 116 | } 117 | 118 | /** 119 | * {@inheritdoc} 120 | */ 121 | public function createOutputArc() 122 | { 123 | $arc = new $this->outputArcClass(); 124 | 125 | if (!$arc instanceof OutputArcInterface) { 126 | throw new \RuntimeException('The output arc class must implement "Petrinet\Model\OutputArcInterface".'); 127 | } 128 | 129 | return $arc; 130 | } 131 | 132 | /** 133 | * {@inheritdoc} 134 | */ 135 | public function createPlaceMarking() 136 | { 137 | $placeMarking = new $this->placeMarkingClass(); 138 | 139 | if (!$placeMarking instanceof PlaceMarkingInterface) { 140 | throw new \RuntimeException( 141 | 'The place marking class must implement "Petrinet\Model\PlaceMarkingInterface".' 142 | ); 143 | } 144 | 145 | return $placeMarking; 146 | } 147 | 148 | /** 149 | * {@inheritdoc} 150 | */ 151 | public function createToken() 152 | { 153 | $token = new $this->tokenClass(); 154 | 155 | if (!$token instanceof TokenInterface) { 156 | throw new \RuntimeException('The token class must implement "Petrinet\Model\TokenInterface".'); 157 | } 158 | 159 | return $token; 160 | } 161 | 162 | /** 163 | * {@inheritdoc} 164 | */ 165 | public function createMarking() 166 | { 167 | $marking = new $this->markingClass(); 168 | 169 | if (!$marking instanceof MarkingInterface) { 170 | throw new \RuntimeException('The marking class must implement "Petrinet\Model\MarkingInterface".'); 171 | } 172 | 173 | return $marking; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/Petrinet/Dumper/GraphvizDumper.php: -------------------------------------------------------------------------------- 1 | 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 Petrinet\Dumper; 13 | 14 | use Petrinet\Model\ArcInterface; 15 | use Petrinet\Model\InputArcInterface; 16 | use Petrinet\Model\MarkingInterface; 17 | use Petrinet\Model\OutputArcInterface; 18 | use Petrinet\Model\PetrinetInterface; 19 | use Petrinet\Model\PlaceInterface; 20 | use Petrinet\Model\TransitionInterface; 21 | 22 | /** 23 | * Dumps a Petrinet in Graphviz format. 24 | * 25 | * @author Florian Voutzinos 26 | */ 27 | class GraphvizDumper implements DumperInterface 28 | { 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function dump(PetrinetInterface $petrinet, MarkingInterface $marking = null) 33 | { 34 | $petrinetId = null === $petrinet->getId() ? spl_object_hash($petrinet) : $petrinet->getId(); 35 | $graph = sprintf("digraph \"%s\" {\n", $petrinetId); 36 | 37 | // Process the places 38 | foreach ($petrinet->getPlaces() as $place) { 39 | $graph .= sprintf( 40 | '"%s" [label="%s"]', 41 | $this->getPlaceId($place), 42 | $this->getPlaceLabel($place, $marking) 43 | ); 44 | $graph .= "\n"; 45 | } 46 | 47 | // Process the transitions 48 | foreach ($petrinet->getTransitions() as $transition) { 49 | $graph .= sprintf( 50 | '"%s" [label="%s" shape=box]', 51 | $this->getTransitionId($transition), 52 | $this->getTransitionLabel($transition) 53 | ); 54 | $graph .= "\n"; 55 | } 56 | 57 | // Process the arcs 58 | foreach ($this->getArcs($petrinet) as $arc) { 59 | $graph .= sprintf( 60 | '"%s" -> "%s" [label="%s"]', 61 | $this->getArcSourceId($arc), 62 | $this->getArcTargetId($arc), 63 | $arc->getWeight() 64 | ); 65 | $graph .= "\n"; 66 | } 67 | 68 | $graph .= '}'; 69 | 70 | return $graph; 71 | } 72 | 73 | /** 74 | * Gets the arcs in the given Petrinet. 75 | * 76 | * @param PetrinetInterface $petrinet 77 | * 78 | * @return \Petrinet\Model\ArcInterface[] 79 | */ 80 | private function getArcs(PetrinetInterface $petrinet) 81 | { 82 | $arcs = array(); 83 | 84 | foreach ($petrinet->getPlaces() as $place) { 85 | foreach ($place->getInputArcs() as $inputArc) { 86 | $inputArcId = null === $inputArc->getId() ? spl_object_hash($inputArc) : $inputArc->getId(); 87 | $arcs[$inputArcId] = $inputArc; 88 | } 89 | 90 | foreach ($place->getOutputArcs() as $outputArc) { 91 | $outputArcId = null === $outputArc->getId() ? spl_object_hash($outputArc) : $outputArc->getId(); 92 | $arcs[$outputArcId] = $outputArc; 93 | } 94 | } 95 | 96 | foreach ($petrinet->getTransitions() as $transition) { 97 | foreach ($transition->getInputArcs() as $inputArc) { 98 | $inputArcId = null === $inputArc->getId() ? spl_object_hash($inputArc) : $inputArc->getId(); 99 | $arcs[$inputArcId] = $inputArc; 100 | } 101 | 102 | foreach ($transition->getOutputArcs() as $outputArc) { 103 | $outputArcId = null === $outputArc->getId() ? spl_object_hash($outputArc) : $outputArc->getId(); 104 | $arcs[$outputArcId] = $outputArc; 105 | } 106 | } 107 | 108 | return $arcs; 109 | } 110 | 111 | private function getArcSourceId(ArcInterface $arc) 112 | { 113 | if ($arc instanceof InputArcInterface) { 114 | return $this->getPlaceId($arc->getPlace()); 115 | } 116 | 117 | return $this->getTransitionId($arc->getTransition()); 118 | } 119 | 120 | private function getArcTargetId(ArcInterface $arc) 121 | { 122 | if ($arc instanceof OutputArcInterface) { 123 | return $this->getPlaceId($arc->getPlace()); 124 | } 125 | 126 | return $this->getTransitionId($arc->getTransition()); 127 | } 128 | 129 | private function getPlaceTokensCount(PlaceInterface $place, MarkingInterface $marking = null) 130 | { 131 | if (null !== $marking && null !== $placeMarking = $marking->getPlaceMarking($place)) { 132 | return count($placeMarking->getTokens()); 133 | } 134 | 135 | return 0; 136 | } 137 | 138 | private function getPlaceId(PlaceInterface $place) 139 | { 140 | return null === $place->getId() ? spl_object_hash($place) : 'p_'.$place->getId(); 141 | } 142 | 143 | private function getPlaceLabel(PlaceInterface $place, MarkingInterface $marking = null) 144 | { 145 | $placeLabel = null === $place->getId() ? '' : $place->getId(); 146 | $tokensCount = $this->getPlaceTokensCount($place, $marking); 147 | 148 | if ($tokensCount > 1) { 149 | return sprintf('%s (%s tokens)', $placeLabel, $tokensCount); 150 | } 151 | 152 | return sprintf('%s (%s token)', $placeLabel, $tokensCount); 153 | } 154 | 155 | private function getTransitionId(TransitionInterface $transition) 156 | { 157 | return null === $transition->getId() ? spl_object_hash($transition) : 't_'.$transition->getId(); 158 | } 159 | 160 | private function getTransitionLabel(TransitionInterface $transition) 161 | { 162 | return null === $transition->getId() ? '' : $transition->getId(); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /spec/Petrinet/Dumper/GraphvizDumperSpec.php: -------------------------------------------------------------------------------- 1 | shouldHaveType('Petrinet\Dumper\GraphvizDumper'); 24 | } 25 | 26 | function it_dumps_a_petrinet( 27 | PetrinetInterface $petrinet, 28 | PlaceInterface $placeOne, 29 | PlaceInterface $placeTwo, 30 | TransitionInterface $transitionOne, 31 | TransitionInterface $transitionTwo, 32 | InputArcInterface $arcOne, 33 | OutputArcInterface $arcTwo, 34 | InputArcInterface $arcThree, 35 | OutputArcInterface $arcFour 36 | ) 37 | { 38 | $petrinet->getPlaces()->willReturn(array($placeOne, $placeTwo)); 39 | $petrinet->getTransitions()->willReturn(array($transitionOne, $transitionTwo)); 40 | $petrinet->getId()->willReturn(1); 41 | 42 | $placeOne->getInputArcs()->willReturn(array()); 43 | $placeOne->getOutputArcs()->willReturn(array($arcOne)); 44 | $placeOne->getId()->willReturn(1); 45 | 46 | $arcOne->getPlace()->willReturn($placeOne); 47 | $arcOne->getTransition()->willReturn($transitionOne); 48 | $arcOne->getId()->willReturn(1); 49 | $arcOne->getWeight()->willReturn(1); 50 | 51 | $transitionOne->getInputArcs()->willReturn(array($arcOne)); 52 | $transitionOne->getOutputArcs()->willReturn(array($arcTwo)); 53 | $transitionOne->getId()->willReturn(1); 54 | 55 | $arcTwo->getTransition()->willReturn($transitionOne); 56 | $arcTwo->getPlace()->willReturn($placeTwo); 57 | $arcTwo->getId()->willReturn(2); 58 | $arcTwo->getWeight()->willReturn(2); 59 | 60 | $placeTwo->getInputArcs()->willReturn(array($arcTwo)); 61 | $placeTwo->getOutputArcs()->willReturn(array($arcThree)); 62 | $placeTwo->getId()->willReturn(2); 63 | 64 | $arcThree->getPlace()->willReturn($placeTwo); 65 | $arcThree->getTransition()->willReturn($transitionTwo); 66 | $arcThree->getId()->willReturn(3); 67 | $arcThree->getWeight()->willReturn(1); 68 | 69 | $transitionTwo->getInputArcs()->willReturn(array($arcThree)); 70 | $transitionTwo->getOutputArcs()->willReturn(array($arcFour)); 71 | $transitionTwo->getId()->willReturn(2); 72 | 73 | $arcFour->getTransition()->willReturn($transitionTwo); 74 | $arcFour->getPlace()->willReturn($placeOne); 75 | $arcFour->getId()->willReturn(4); 76 | $arcFour->getWeight()->willReturn(2); 77 | 78 | $this->dump($petrinet)->shouldReturn($this->getFirstExpectedDotContent()); 79 | } 80 | 81 | function it_dumps_a_petrinet_with_marking( 82 | PetrinetInterface $petrinet, 83 | PlaceInterface $placeOne, 84 | PlaceInterface $placeTwo, 85 | PlaceInterface $placeThree, 86 | PlaceInterface $placeFour, 87 | TransitionInterface $transitionOne, 88 | TransitionInterface $transitionTwo, 89 | InputArcInterface $arcOne, 90 | OutputArcInterface $arcTwo, 91 | InputArcInterface $arcThree, 92 | OutputArcInterface $arcFour, 93 | OutputArcInterface $arcFive, 94 | InputArcInterface $arcSix, 95 | MarkingInterface $marking, 96 | PlaceMarkingInterface $placeOneMarking, 97 | PlaceMarkingInterface $placeThreeMarking, 98 | TokenInterface $token 99 | ) 100 | { 101 | // Petrinet 102 | $petrinet->getPlaces()->willReturn(array($placeOne, $placeTwo, $placeThree, $placeFour)); 103 | $petrinet->getTransitions()->willReturn(array($transitionOne, $transitionTwo)); 104 | $petrinet->getId()->willReturn(1); 105 | 106 | $placeOne->getInputArcs()->willReturn(array()); 107 | $placeOne->getOutputArcs()->willReturn(array($arcOne)); 108 | $placeOne->getId()->willReturn(1); 109 | 110 | $arcOne->getPlace()->willReturn($placeOne); 111 | $arcOne->getTransition()->willReturn($transitionOne); 112 | $arcOne->getId()->willReturn(1); 113 | $arcOne->getWeight()->willReturn(1); 114 | 115 | $transitionOne->getInputArcs()->willReturn(array($arcOne)); 116 | $transitionOne->getOutputArcs()->willReturn(array($arcTwo, $arcFive)); 117 | $transitionOne->getId()->willReturn(1); 118 | 119 | $arcTwo->getTransition()->willReturn($transitionOne); 120 | $arcTwo->getPlace()->willReturn($placeTwo); 121 | $arcTwo->getId()->willReturn(2); 122 | $arcTwo->getWeight()->willReturn(1); 123 | 124 | $placeTwo->getInputArcs()->willReturn(array($arcTwo)); 125 | $placeTwo->getOutputArcs()->willReturn(array($arcThree)); 126 | $placeTwo->getId()->willReturn(2); 127 | 128 | $arcThree->getPlace()->willReturn($placeTwo); 129 | $arcThree->getTransition()->willReturn($transitionTwo); 130 | $arcThree->getId()->willReturn(3); 131 | $arcThree->getWeight()->willReturn(2); 132 | 133 | $transitionTwo->getInputArcs()->willReturn(array($arcThree)); 134 | $transitionTwo->getOutputArcs()->willReturn(array($arcFour)); 135 | $transitionTwo->getId()->willReturn(2); 136 | 137 | $arcFour->getTransition()->willReturn($transitionTwo); 138 | $arcFour->getPlace()->willReturn($placeThree); 139 | $arcFour->getId()->willReturn(4); 140 | $arcFour->getWeight()->willReturn(1); 141 | 142 | $placeThree->getInputArcs()->willReturn(array($arcFour)); 143 | $placeThree->getOutputArcs()->willReturn(array()); 144 | $placeThree->getId()->willReturn(3); 145 | 146 | $arcFive->getTransition()->willReturn($transitionOne); 147 | $arcFive->getPlace()->willReturn($placeFour); 148 | $arcFive->getId()->willReturn(5); 149 | $arcFive->getWeight()->willReturn(1); 150 | 151 | $placeFour->getInputArcs()->willReturn(array($arcFive)); 152 | $placeFour->getOutputArcs()->willReturn(array($arcSix)); 153 | $placeFour->getId()->willReturn(4); 154 | 155 | $arcSix->getPlace()->willReturn($placeFour); 156 | $arcSix->getTransition()->willReturn($transitionTwo); 157 | $arcSix->getId()->willReturn(6); 158 | $arcSix->getWeight()->willReturn(1); 159 | 160 | // Marking 161 | $placeOneMarking->getTokens()->willReturn(array($token)); 162 | $placeThreeMarking->getTokens()->willReturn(array($token, $token)); 163 | 164 | $marking->getPlaceMarking($placeOne)->willReturn($placeOneMarking); 165 | $marking->getPlaceMarking($placeTwo)->willReturn(null); 166 | $marking->getPlaceMarking($placeThree)->willReturn($placeThreeMarking); 167 | $marking->getPlaceMarking($placeFour)->willReturn(null); 168 | 169 | $this->dump($petrinet, $marking)->shouldReturn($this->getSecondExpectedDotContent()); 170 | } 171 | 172 | private function getFirstExpectedDotContent() 173 | { 174 | return << "t_1" [label="1"] 181 | "t_1" -> "p_2" [label="2"] 182 | "p_2" -> "t_2" [label="1"] 183 | "t_2" -> "p_1" [label="2"] 184 | } 185 | DOT; 186 | } 187 | 188 | private function getSecondExpectedDotContent() 189 | { 190 | return << "t_1" [label="1"] 199 | "t_1" -> "p_2" [label="1"] 200 | "p_2" -> "t_2" [label="2"] 201 | "t_2" -> "p_3" [label="1"] 202 | "t_1" -> "p_4" [label="1"] 203 | "p_4" -> "t_2" [label="1"] 204 | } 205 | DOT; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /spec/Petrinet/Service/TransitionServiceSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith($factory); 23 | $this->shouldHaveType('Petrinet\Service\TransitionService'); 24 | } 25 | 26 | function it_tells_when_a_transition_is_enabled_in_a_given_marking( 27 | FactoryInterface $factory, 28 | InputArcInterface $arcOne, 29 | InputArcInterface $arcTwo, 30 | PlaceInterface $placeOne, 31 | PlaceInterface $placeTwo, 32 | PlaceMarkingInterface $placeOneMarking, 33 | PlaceMarkingInterface $placeTwoMarking, 34 | TransitionInterface $transition, 35 | MarkingInterface $marking, 36 | TokenInterface $token 37 | ) 38 | { 39 | $arcOne->getPlace()->willReturn($placeOne); 40 | $arcOne->getWeight()->willReturn(1); 41 | 42 | $arcTwo->getPlace()->willReturn($placeTwo); 43 | $arcTwo->getWeight()->willReturn(3); 44 | 45 | $placeOneMarking->getTokens()->willReturn(array($token)); 46 | $placeTwoMarking->getTokens()->willReturn(array($token, $token, $token)); 47 | 48 | $marking->getPlaceMarking($placeOne->getWrappedObject())->willReturn($placeOneMarking); 49 | $marking->getPlaceMarking($placeTwo->getWrappedObject())->willReturn($placeTwoMarking); 50 | 51 | $transition->getInputArcs()->willReturn(array($arcOne, $arcTwo)); 52 | 53 | $this->beConstructedWith($factory); 54 | $this->isEnabled($transition, $marking)->shouldReturn(true); 55 | } 56 | 57 | function it_tells_a_transition_is_disabled_when_there_is_no_items_in_input_arcs_array( 58 | FactoryInterface $factory, 59 | TransitionInterface $transition, 60 | MarkingInterface $marking 61 | ) 62 | { 63 | $transition->getInputArcs()->willReturn(array()); 64 | 65 | $this->beConstructedWith($factory); 66 | $this->isEnabled($transition, $marking)->shouldReturn(false); 67 | } 68 | 69 | function it_tells_a_transition_is_disabled_when_there_is_no_items_in_input_arcs_collection( 70 | FactoryInterface $factory, 71 | TransitionInterface $transition, 72 | MarkingInterface $marking, 73 | Collection $inputArcs 74 | ) 75 | { 76 | $inputArcs->count()->willReturn(0); 77 | 78 | $transition->getInputArcs()->willReturn($inputArcs); 79 | 80 | $this->beConstructedWith($factory); 81 | $this->isEnabled($transition, $marking)->shouldReturn(false); 82 | } 83 | 84 | function it_tells_a_transition_is_disabled_when_there_is_no_marking_for_the_input_places( 85 | FactoryInterface $factory, 86 | InputArcInterface $arc, 87 | PlaceInterface $place, 88 | TransitionInterface $transition, 89 | MarkingInterface $marking 90 | ) 91 | { 92 | $arc->getPlace()->willReturn($place); 93 | $arc->getWeight()->willReturn(1); 94 | 95 | $marking->getPlaceMarking($place->getWrappedObject())->willReturn(null); 96 | $transition->getInputArcs()->willReturn(array($arc)); 97 | 98 | $this->beConstructedWith($factory); 99 | $this->isEnabled($transition, $marking)->shouldReturn(false); 100 | } 101 | 102 | function it_tells_when_a_transition_is_disabled_in_a_given_marking( 103 | FactoryInterface $factory, 104 | InputArcInterface $arcOne, 105 | InputArcInterface $arcTwo, 106 | PlaceInterface $placeOne, 107 | PlaceInterface $placeTwo, 108 | PlaceMarkingInterface $placeOneMarking, 109 | PlaceMarkingInterface $placeTwoMarking, 110 | TransitionInterface $transition, 111 | MarkingInterface $marking, 112 | TokenInterface $token 113 | ) 114 | { 115 | $arcOne->getPlace()->willReturn($placeOne); 116 | $arcOne->getWeight()->willReturn(1); 117 | 118 | $arcTwo->getPlace()->willReturn($placeTwo); 119 | $arcTwo->getWeight()->willReturn(1); 120 | 121 | $placeOneMarking->getTokens()->willReturn(array($token)); 122 | $placeTwoMarking->getTokens()->willReturn(array()); 123 | 124 | $marking->getPlaceMarking($placeOne->getWrappedObject())->willReturn($placeOneMarking); 125 | $marking->getPlaceMarking($placeTwo->getWrappedObject())->willReturn($placeTwoMarking); 126 | 127 | $transition->getInputArcs()->willReturn(array($arcOne, $arcTwo)); 128 | 129 | $this->beConstructedWith($factory); 130 | $this->isEnabled($transition, $marking)->shouldReturn(false); 131 | } 132 | 133 | function it_fires_an_enabled_transition_with_three_input_places_and_two_output_places( 134 | FactoryInterface $factory, 135 | MarkingInterface $marking, 136 | PlaceInterface $placeOne, 137 | PlaceInterface $placeTwo, 138 | PlaceInterface $placeThree, 139 | PlaceInterface $placeFour, 140 | PlaceInterface $placeFive, 141 | InputArcInterface $arcOne, 142 | InputArcInterface $arcTwo, 143 | InputArcInterface $arcThree, 144 | OutputArcInterface $arcFour, 145 | OutputArcInterface $arcFive, 146 | TransitionInterface $transition, 147 | TokenInterface $token, 148 | TokenInterface $tokenOne, 149 | TokenInterface $tokenTwo, 150 | TokenInterface $tokenThree, 151 | TokenInterface $tokenFour, 152 | TokenInterface $tokenFive, 153 | TokenInterface $tokenSix, 154 | PlaceMarkingInterface $placeOneMarking, 155 | PlaceMarkingInterface $placeTwoMarking, 156 | PlaceMarkingInterface $placeThreeMarking, 157 | PlaceMarkingInterface $placeFourMarking, 158 | PlaceMarkingInterface $placeFiveMarking 159 | ) 160 | { 161 | $placeOne->getOutputArcs()->willReturn(array($arcOne)); 162 | $placeTwo->getOutputArcs()->willReturn(array($arcTwo)); 163 | $placeThree->getOutputArcs()->willReturn(array($arcThree)); 164 | $placeFour->getInputArcs()->willReturn(array($arcFour)); 165 | $placeFive->getInputArcs()->willReturn(array($arcFive)); 166 | 167 | $arcOne->getPlace()->willReturn($placeOne); 168 | $arcOne->getTransition()->willReturn($transition); 169 | $arcOne->getWeight()->willReturn(3); 170 | 171 | $arcTwo->getPlace()->willReturn($placeTwo); 172 | $arcTwo->getTransition()->willReturn($transition); 173 | $arcTwo->getWeight()->willReturn(2); 174 | 175 | $arcThree->getPlace()->willReturn($placeThree); 176 | $arcThree->getTransition()->willReturn($transition); 177 | $arcThree->getWeight()->willReturn(1); 178 | 179 | $arcFour->getTransition()->willReturn($transition); 180 | $arcFour->getPlace()->willReturn($placeFour); 181 | $arcFour->getWeight()->willReturn(2); 182 | 183 | $arcFive->getTransition()->willReturn($transition); 184 | $arcFive->getPlace()->willReturn($placeFive); 185 | $arcFive->getWeight()->willReturn(1); 186 | 187 | $placeOneMarking->getTokens()->willReturn(array($tokenOne, $tokenTwo, $tokenThree)); 188 | $placeTwoMarking->getTokens()->willReturn(array($tokenFour, $tokenFive)); 189 | $placeThreeMarking->getTokens()->willReturn(array($tokenSix)); 190 | 191 | $marking->getPlaceMarking($placeOne->getWrappedObject())->willReturn($placeOneMarking); 192 | $marking->getPlaceMarking($placeTwo->getWrappedObject())->willReturn($placeTwoMarking); 193 | $marking->getPlaceMarking($placeThree->getWrappedObject())->willReturn($placeThreeMarking); 194 | $marking->getPlaceMarking($placeFour->getWrappedObject())->willReturn($placeFourMarking); 195 | $marking->getPlaceMarking($placeFive->getWrappedObject())->willReturn($placeFiveMarking); 196 | 197 | $transition->getInputArcs()->willReturn(array($arcOne, $arcTwo, $arcThree)); 198 | $transition->getOutputArcs()->willReturn(array($arcFour, $arcFive)); 199 | 200 | $factory->createToken()->willReturn($token); 201 | 202 | // Expects tokens to be removed from the input places 203 | $placeOneMarking->removeToken($tokenOne)->shouldBeCalled(); 204 | $placeOneMarking->removeToken($tokenTwo)->shouldBeCalled(); 205 | $placeOneMarking->removeToken($tokenThree)->shouldBeCalled(); 206 | 207 | $placeTwoMarking->removeToken($tokenFour)->shouldBeCalled(); 208 | $placeTwoMarking->removeToken($tokenFive)->shouldBeCalled(); 209 | 210 | $placeThreeMarking->removeToken($tokenSix)->shouldBeCalled(); 211 | 212 | // Expects tokens to be added to the output places 213 | $placeFourMarking->setTokens(array($token, $token))->shouldBeCalled(); 214 | $placeFiveMarking->setTokens(array($token))->shouldBeCalled(); 215 | 216 | $this->beConstructedWith($factory); 217 | $this->fire($transition, $marking); 218 | } 219 | 220 | function it_creates_the_output_places_marking_if_not_existing_when_firing_a_transition( 221 | FactoryInterface $factory, 222 | PlaceMarkingInterface $placeOneMarking, 223 | PlaceMarkingInterface $placeTwoMarking, 224 | MarkingInterface $marking, 225 | PlaceInterface $placeOne, 226 | PlaceInterface $placeTwo, 227 | TransitionInterface $transition, 228 | InputArcInterface $arcOne, 229 | OutputArcInterface $arcTwo, 230 | TokenInterface $token 231 | ) 232 | { 233 | $placeOne->getOutputArcs()->willReturn(array($arcOne)); 234 | 235 | $arcOne->getPlace()->willReturn($placeOne); 236 | $arcOne->getTransition()->willReturn($transition); 237 | $arcOne->getWeight()->willReturn(1); 238 | 239 | $transition->getInputArcs()->willReturn(array($arcOne)); 240 | $transition->getOutputArcs()->willReturn(array($arcTwo)); 241 | 242 | $arcTwo->getTransition()->willReturn($transition); 243 | $arcTwo->getPlace()->willReturn($placeTwo); 244 | $arcTwo->getWeight()->willReturn(1); 245 | 246 | $factory->createToken()->willReturn($token)->shouldBeCalled(); 247 | $factory->createPlaceMarking()->willReturn($placeTwoMarking)->shouldBeCalled(); 248 | 249 | $placeOneMarking->getTokens()->willReturn(array($token)); 250 | 251 | $marking->getPlaceMarking($placeOne->getWrappedObject())->willReturn($placeOneMarking); 252 | $marking->getPlaceMarking($placeTwo->getWrappedObject())->willReturn(null); 253 | 254 | $placeOneMarking->removeToken($token)->shouldBeCalled(); 255 | $placeTwoMarking->setPlace($placeTwo)->shouldBeCalled(); 256 | $placeTwoMarking->setTokens(array($token))->shouldBeCalled(); 257 | $marking->addPlaceMarking($placeTwoMarking)->shouldBeCalled(); 258 | 259 | $this->beConstructedWith($factory); 260 | $this->fire($transition, $marking); 261 | } 262 | 263 | function it_throws_an_exception_when_firing_a_disabled_transition( 264 | FactoryInterface $factory, 265 | MarkingInterface $marking, 266 | PlaceMarkingInterface $placeMarking, 267 | TransitionInterface $transition, 268 | PlaceInterface $place, 269 | InputArcInterface $arc 270 | ) 271 | { 272 | $place->getOutputArcs()->willReturn(array($arc)); 273 | $transition->getInputArcs()->willReturn(array($arc)); 274 | 275 | $arc->getPlace()->willReturn($place); 276 | $arc->getTransition()->willReturn($transition); 277 | $arc->getWeight()->willReturn(1); 278 | 279 | $placeMarking->getTokens()->willReturn(array()); 280 | $marking->getPlaceMarking($place)->willReturn($placeMarking); 281 | 282 | $this->beConstructedWith($factory); 283 | 284 | $this 285 | ->shouldThrow(new TransitionNotEnabledException('Cannot fire a disabled transition.')) 286 | ->duringFire($transition, $marking) 287 | ; 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /docs/documentation.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | This library follows the definition of the basic Petrinet model with weighted arcs as described 4 | [here](http://en.wikipedia.org/wiki/Petri_net), roughly: 5 | 6 | - there are two types of nodes: Places and Transitions 7 | - these nodes are connected with Arcs having a weight 8 | - a Petrinet is a set of Places and Transitions 9 | - a Place Marking associates a set of Tokens to a place 10 | - a Marking is a set of Place Markings 11 | - a Petrinet can be executed against a Marking 12 | 13 | In workflow words, the Petrinet is the workflow structure and each marking is a workflow instance. 14 | 15 | ## Model 16 | 17 | This library provides the models of basic Petrinets that you can use directly to work without persistence 18 | or extend with your custom entities mapped to a database with any ORM. 19 | 20 | ## Builder 21 | 22 | All builders make usage of a `FactoryInterface` that is reponsible for creating model instances. 23 | By default, the factory is configured to create models present in this library, but it is possible to configure it 24 | to create your custom model instances (for example Doctrine Entities) by passing the class names to its constructor. 25 | 26 | ```php 27 | // Instanciating the factory 28 | $factory = new \Petrinet\Model\Factory(); 29 | ``` 30 | 31 | ### Petrinet Builder 32 | 33 | The Petrinet builder helps creating places and transitions, connecting them with arcs 34 | and retrieving the resulting Petrinet. 35 | 36 | #### Overview 37 | 38 | ```php 39 | // Instanciating the builder 40 | $builder = new \Petrinet\Builder\PetrinetBuilder($factory); 41 | 42 | // Creating a place 43 | $place = $builder->place(); 44 | 45 | // Creating a transition 46 | $transition = $builder->transition(); 47 | 48 | // Connecting a place to a transition 49 | $builder->connect($place, $transition); 50 | 51 | // Connecting a transition to a place 52 | $builder->connect($transition, $place); 53 | 54 | // Connecting a place to a transition with an arc of weight 3 55 | $builder->connect($place, $transition, 3); 56 | 57 | // Retrieving the Petrinet 58 | $petrinet = $builder->getPetrinet(); 59 | ``` 60 | 61 | #### Example 62 | 63 | This example shows how to create the following Petrinet: ![Petrinet](images/petrinet_builder.png). 64 | 65 | ```php 66 | $petrinet = $builder 67 | ->connect($builder->place(), $t1 = $builder->transition()) 68 | ->connect($t1, $p2 = $builder->place()) 69 | ->connect($t1, $p3 = $builder->place()) 70 | ->connect($p2, $t2 = $builder->transition()) 71 | ->connect($p3, $t2) 72 | ->connect($t2, $builder->place()) 73 | ->getPetrinet(); 74 | ``` 75 | 76 | ### Marking Builder 77 | 78 | The marking builder helps creating place markings and retrieving the resulting Petrinet marking 79 | (the places can be created manually or using the `PetrinetBuilder` above). 80 | 81 | #### Overview 82 | 83 | ```php 84 | // Instanciating the builder 85 | $builder = new \Petrinet\Builder\MarkingBuilder($factory); 86 | 87 | // Marks a place with the specified tokens number 88 | $builder->mark($place, 3); 89 | 90 | // Marks a place with the specified token 91 | $builder->mark($place, new \Petrinet\Model\Token()); 92 | 93 | // Marks a place with the specified tokens 94 | $builder->mark($place, array(new \Petrinet\Model\Token(), new \Petrinet\Model\Token())); 95 | 96 | // Retrieving the Marking 97 | $marking = $builder->getMarking(); 98 | ``` 99 | 100 | #### Example 101 | 102 | This example shows how to create a marking containing two place markings: 103 | 104 | - The place `$p1` is marked with 3 tokens 105 | - The place `$p2` is marked with 2 tokens 106 | 107 | ```php 108 | $marking = $markingBuilder 109 | ->mark($p1, 3) 110 | ->mark($p2, 2) 111 | ->getMarking(); 112 | ``` 113 | 114 | ## Service 115 | 116 | The transition service allows you to check if a transition is enabled in a given marking and fire an enabled 117 | transition. 118 | 119 | ```php 120 | // Instanciates the transition service 121 | $transitionService = new \Petrinet\Service\TransitionService($factory); 122 | 123 | // Checks if the transition is enabled in the given marking 124 | $transitionService->isEnabled($transition, $marking); 125 | 126 | // Fires the transition in the given marking 127 | try { 128 | $transitionService->fire($transition, $marking); 129 | } catch (\Petrinet\Service\Exception\TransitionNotEnabledException $e) { 130 | // The transition is not enabled and cannot be fired 131 | } 132 | ``` 133 | 134 | Firing a transition will modify the place markings by removing and adding new tokens to them. 135 | It will also create missing place markings if not existing. 136 | The persistence of the marking after firing a transition is up to you. 137 | 138 | ## Dumper 139 | 140 | ### Graphviz Dumper 141 | 142 | The Graphviz dumper dumps a Petrinet as a string in `dot` format that can be processed by 143 | the [Graphviz](http://www.graphviz.org) software. 144 | 145 | #### Usage 146 | 147 | ```php 148 | // Instanciates the Dumper 149 | $dumper = new \Petrinet\Dumper\GraphvizDumper(); 150 | 151 | // Dumps the Petrinet structure 152 | $string = $dumper->dump($petrinet); 153 | 154 | // Dumps the Petrinet in a given marking 155 | $string = $dumper->dump($petrinet, $marking); 156 | ``` 157 | 158 | You can write the resulting string in a file: 159 | 160 | ```php 161 | file_put_contents('petrinet.dot', $string); 162 | ``` 163 | 164 | and transform it to a PNG image using the command: 165 | 166 | ```bash 167 | $ dot -Tpng petrinet.dot > petrinet.png 168 | ``` 169 | 170 | You will obtain this kind of image: 171 | 172 | ![Petrinet](images/graphviz.png). 173 | 174 | ## Database Mapping 175 | 176 | ### Doctrine ORM 177 | 178 | The following example shows the basic mapping for the Petrinet model classes using Doctrine2 ORM. 179 | 180 | #### Petrinet 181 | 182 | ```php 183 |