Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/hollodotme/state-machine-generator
PHP code generator for OOP state machines
https://github.com/hollodotme/state-machine-generator
code-generator oop php state-machine
Last synced: 3 days ago
JSON representation
PHP code generator for OOP state machines
- Host: GitHub
- URL: https://github.com/hollodotme/state-machine-generator
- Owner: hollodotme
- License: mit
- Created: 2017-02-15T13:04:48.000Z (almost 8 years ago)
- Default Branch: master
- Last Pushed: 2020-03-25T03:29:10.000Z (almost 5 years ago)
- Last Synced: 2024-11-21T12:46:15.486Z (2 months ago)
- Topics: code-generator, oop, php, state-machine
- Language: PHP
- Size: 54.7 KB
- Stars: 2
- Watchers: 2
- Forks: 2
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
[![Build Status](https://travis-ci.org/hollodotme/state-machine-generator.svg?branch=master)](https://travis-ci.org/hollodotme/state-machine-generator)
[![Coverage Status](https://coveralls.io/repos/github/hollodotme/state-machine-generator/badge.svg?branch=master)](https://coveralls.io/github/hollodotme/state-machine-generator?branch=master)# State machine generator
A PHP code generator for OOP state machines.
This package is based on the work of [Sebastian Bergmann and his state repository](https://github.com/sebastianbergmann/state).
## Requirements
* PHP >= 7.1
* PHPUnit >= 6.0 (for running the generated tests)## Features
* Command based CLI API
* Support for namespaces
* States can be represented as and reconstituted from strings
* Configurable output paths
* Configurable meta data
* Template specification file creation
* Generator available as a PHAR file## Installation
Download the current stable PHAR file from the [releases section](https://github.com/hollodotme/state-machine-generator/releases/).
wget https://github.com/hollodotme/state-machine-generator/releases/download/vX.X.X/state-machine-generator.phar
If you like verify it with its GPG signature:wget https://github.com/hollodotme/state-machine-generator/releases/download/vX.X.X/state-machine-generator.phar.asc
## Usage
### Create a sample specification file
```bash
$ php state-machine-generator.phar create:specfile /path/to/specification-file.xml
```### Generate state machine classes incl. exceptions and unit tests
```bash
$ php state-machine-generator.phar generate:machine /path/to/specification-file.xml
```## Example
### Specification file
```xml
```
### Generated files
```
- /src
|- /States
| |- /Exceptions
| | |- IllegalStateTransition
| | `- InvalidStateString
| |- /Interfaces
| | `- DoorState.php
| |- AbstractDoorState.php
| |- ClosedDoorState.php
| |- LockedDoorState.php
| `- OpenDoorState.php
`- Door.php
- /tests
|- ClosedDoorStateTest.php
|- LockedDoorStateTest.php
`- OpenDoorStateTest.php
```#### Interface `DoorState`
```php
*/
namespace YourVendor\YourProject\States\Interfaces;
/**
* Interface DoorState
* @package YourVendor\YourProject\States\Interfaces
*/
interface DoorState
{
public function open() : DoorState;public function close() : DoorState;
public function lock() : DoorState;
public function unlock() : DoorState;
public function toString() : string;
public function __toString() : string;
}
```### Class `AbstractDoorState`
```php
*/
namespace YourVendor\YourProject\States;
use YourVendor\YourProject\States\Interfaces\DoorState;
use YourVendor\YourProject\States\Exceptions\IllegalStateTransition;
use YourVendor\YourProject\States\Exceptions\InvalidStateString;/**
* Class AbstractDoorState
* @package YourVendor\YourProject\States
*/
abstract class AbstractDoorState implements DoorState
{
protected const OPEN = 'open';protected const CLOSED = 'closed';
protected const LOCKED = 'locked';
public function open() : DoorState
{
throw (new IllegalStateTransition())->withDesiredState(self::OPEN);
}public function close() : DoorState
{
throw (new IllegalStateTransition())->withDesiredState(self::CLOSED);
}public function lock() : DoorState
{
throw (new IllegalStateTransition())->withDesiredState(self::LOCKED);
}public function unlock() : DoorState
{
throw (new IllegalStateTransition())->withDesiredState(self::CLOSED);
}public function __toString() : string
{
return $this->toString();
}/**
* @param string $stateString
* @throws InvalidStateString
* @return DoorState
*/
public static function fromString(string $stateString) : DoorState
{
switch($stateString)
{
case self::OPEN:
return new OpenDoorState();case self::CLOSED:
return new ClosedDoorState();case self::LOCKED:
return new LockedDoorState();default:
throw (new InvalidStateString())->withStateString($stateString);
}
}
}
```#### Class `ClosedDoorState`
```php
*/
namespace YourVendor\YourProject\States;
use YourVendor\YourProject\States\Interfaces\DoorState;
/**
* Class ClosedDoorState
* @package YourVendor\YourProject\States
*/
final class ClosedDoorState extends AbstractDoorState
{
public function open() : DoorState
{
return new OpenDoorState();
}public function lock() : DoorState
{
return new LockedDoorState();
}public function toString() : string
{
return self::CLOSED;
}
}
```#### Class `LockedDoorState`
```php
*/
namespace YourVendor\YourProject\States;
use YourVendor\YourProject\States\Interfaces\DoorState;
/**
* Class LockedDoorState
* @package YourVendor\YourProject\States
*/
final class LockedDoorState extends AbstractDoorState
{
public function unlock() : DoorState
{
return new ClosedDoorState();
}public function toString() : string
{
return self::LOCKED;
}
}
```#### Class `OpenDoorState`
```php
*/
namespace YourVendor\YourProject\States;
use YourVendor\YourProject\States\Interfaces\DoorState;
/**
* Class OpenDoorState
* @package YourVendor\YourProject\States
*/
final class OpenDoorState extends AbstractDoorState
{
public function close() : DoorState
{
return new ClosedDoorState();
}public function toString() : string
{
return self::OPEN;
}
}
```#### Class `Door`
```php
*/
namespace YourVendor\YourProject;
use YourVendor\YourProject\States\Interfaces\DoorState;
use YourVendor\YourProject\States\Exceptions\IllegalStateTransition;
use YourVendor\YourProject\States\OpenDoorState;
use YourVendor\YourProject\States\ClosedDoorState;
use YourVendor\YourProject\States\LockedDoorState;/**
* Class Door
* @package YourVendor\YourProject
*/
class Door
{
/** @var DoorState */
private $state;public function __construct(DoorState $state)
{
$this->setState($state);
}private function setState(DoorState $state)
{
$this->state = $state;
}/**
* @throws IllegalStateTransition
*/
public function open()
{
$this->setState($this->state->open());
}/**
* @throws IllegalStateTransition
*/
public function close()
{
$this->setState($this->state->close());
}/**
* @throws IllegalStateTransition
*/
public function lock()
{
$this->setState($this->state->lock());
}/**
* @throws IllegalStateTransition
*/
public function unlock()
{
$this->setState($this->state->unlock());
}public function isOpen() : bool
{
return ($this->state instanceof OpenDoorState);
}public function isClosed() : bool
{
return ($this->state instanceof ClosedDoorState);
}public function isLocked() : bool
{
return ($this->state instanceof LockedDoorState);
}
}
```#### Test class `ClosedDoorStateTest`
```php
*/
namespace YourVendor\YourProject\Tests\Unit;
use YourVendor\YourProject\Door;
use YourVendor\YourProject\States\AbstractDoorState;
use YourVendor\YourProject\States\Exceptions\InvalidStateString;
use YourVendor\YourProject\States\Exceptions\IllegalStateTransition;
use YourVendor\YourProject\States\ClosedDoorState;/**
* Class ClosedDoorStateTest
* @package YourVendor\YourProject\Tests\Unit
*/
final class ClosedDoorStateTest extends \PHPUnit\Framework\TestCase
{
/** @var Door */
private $door;protected function setUp()
{
$this->door = new Door(new ClosedDoorState());
}public function testIsNotOpen()
{
$this->assertFalse($this->door->isOpen());
}public function testIsClosed()
{
$this->assertTrue($this->door->isClosed());
}public function testIsNotLocked()
{
$this->assertFalse($this->door->isLocked());
}public function testCanBeOpened()
{
$this->door->open();$this->assertTrue($this->door->isOpen());
}/**
* @expectedException \YourVendor\YourProject\States\Exceptions\IllegalStateTransition
*/
public function testCannotBeClosed()
{
try
{
$this->door->close();
}
catch(IllegalStateTransition $e)
{
$this->assertEquals('closed', $e->getDesiredState());throw $e;
}
}public function testCanBeLocked()
{
$this->door->lock();$this->assertTrue($this->door->isLocked());
}/**
* @expectedException \YourVendor\YourProject\States\Exceptions\IllegalStateTransition
*/
public function testCannotBeUnlocked()
{
try
{
$this->door->unlock();
}
catch(IllegalStateTransition $e)
{
$this->assertEquals('closed', $e->getDesiredState());throw $e;
}
}public function testStateCanBeRepresentedAsString()
{
$state = new ClosedDoorState();$this->assertEquals('closed', (string)$state);
$this->assertEquals('closed', $state->toString());
}public function testCanBuildStateFromStringRepresentation()
{
$state = AbstractDoorState::fromString('closed');$this->assertInstanceOf(ClosedDoorState::class, $state);
}/**
* @expectedException \YourVendor\YourProject\States\Exceptions\InvalidStateString
*/
public function testInvalidStateStringThrowsException()
{
try
{
AbstractDoorState::fromString('closed.invalid');
}
catch (InvalidStateString $e)
{
$this->assertEquals('closed.invalid', $e->getStateString());throw $e;
}
}
}
```#### Test class `LockedDoorStateTest`
```php
*/
namespace YourVendor\YourProject\Tests\Unit;
use YourVendor\YourProject\Door;
use YourVendor\YourProject\States\AbstractDoorState;
use YourVendor\YourProject\States\Exceptions\InvalidStateString;
use YourVendor\YourProject\States\Exceptions\IllegalStateTransition;
use YourVendor\YourProject\States\LockedDoorState;/**
* Class LockedDoorStateTest
* @package YourVendor\YourProject\Tests\Unit
*/
final class LockedDoorStateTest extends \PHPUnit\Framework\TestCase
{
/** @var Door */
private $door;protected function setUp()
{
$this->door = new Door(new LockedDoorState());
}public function testIsNotOpen()
{
$this->assertFalse($this->door->isOpen());
}public function testIsNotClosed()
{
$this->assertFalse($this->door->isClosed());
}public function testIsLocked()
{
$this->assertTrue($this->door->isLocked());
}/**
* @expectedException \YourVendor\YourProject\States\Exceptions\IllegalStateTransition
*/
public function testCannotBeOpened()
{
try
{
$this->door->open();
}
catch(IllegalStateTransition $e)
{
$this->assertEquals('open', $e->getDesiredState());throw $e;
}
}/**
* @expectedException \YourVendor\YourProject\States\Exceptions\IllegalStateTransition
*/
public function testCannotBeClosed()
{
try
{
$this->door->close();
}
catch(IllegalStateTransition $e)
{
$this->assertEquals('closed', $e->getDesiredState());throw $e;
}
}/**
* @expectedException \YourVendor\YourProject\States\Exceptions\IllegalStateTransition
*/
public function testCannotBeLocked()
{
try
{
$this->door->lock();
}
catch(IllegalStateTransition $e)
{
$this->assertEquals('locked', $e->getDesiredState());throw $e;
}
}public function testCanBeUnlocked()
{
$this->door->unlock();$this->assertTrue($this->door->isClosed());
}public function testStateCanBeRepresentedAsString()
{
$state = new LockedDoorState();$this->assertEquals('locked', (string)$state);
$this->assertEquals('locked', $state->toString());
}public function testCanBuildStateFromStringRepresentation()
{
$state = AbstractDoorState::fromString('locked');$this->assertInstanceOf(LockedDoorState::class, $state);
}/**
* @expectedException \YourVendor\YourProject\States\Exceptions\InvalidStateString
*/
public function testInvalidStateStringThrowsException()
{
try
{
AbstractDoorState::fromString('locked.invalid');
}
catch (InvalidStateString $e)
{
$this->assertEquals('locked.invalid', $e->getStateString());throw $e;
}
}
}
```#### Test class `OpenDoorStateTest`
```php
*/
namespace YourVendor\YourProject\Tests\Unit;
use YourVendor\YourProject\Door;
use YourVendor\YourProject\States\AbstractDoorState;
use YourVendor\YourProject\States\Exceptions\InvalidStateString;
use YourVendor\YourProject\States\Exceptions\IllegalStateTransition;
use YourVendor\YourProject\States\OpenDoorState;/**
* Class OpenDoorStateTest
* @package YourVendor\YourProject\Tests\Unit
*/
final class OpenDoorStateTest extends \PHPUnit\Framework\TestCase
{
/** @var Door */
private $door;protected function setUp()
{
$this->door = new Door(new OpenDoorState());
}public function testIsOpen()
{
$this->assertTrue($this->door->isOpen());
}public function testIsNotClosed()
{
$this->assertFalse($this->door->isClosed());
}public function testIsNotLocked()
{
$this->assertFalse($this->door->isLocked());
}/**
* @expectedException \YourVendor\YourProject\States\Exceptions\IllegalStateTransition
*/
public function testCannotBeOpened()
{
try
{
$this->door->open();
}
catch(IllegalStateTransition $e)
{
$this->assertEquals('open', $e->getDesiredState());throw $e;
}
}public function testCanBeClosed()
{
$this->door->close();$this->assertTrue($this->door->isClosed());
}/**
* @expectedException \YourVendor\YourProject\States\Exceptions\IllegalStateTransition
*/
public function testCannotBeLocked()
{
try
{
$this->door->lock();
}
catch(IllegalStateTransition $e)
{
$this->assertEquals('locked', $e->getDesiredState());throw $e;
}
}/**
* @expectedException \YourVendor\YourProject\States\Exceptions\IllegalStateTransition
*/
public function testCannotBeUnlocked()
{
try
{
$this->door->unlock();
}
catch(IllegalStateTransition $e)
{
$this->assertEquals('closed', $e->getDesiredState());throw $e;
}
}public function testStateCanBeRepresentedAsString()
{
$state = new OpenDoorState();$this->assertEquals('open', (string)$state);
$this->assertEquals('open', $state->toString());
}public function testCanBuildStateFromStringRepresentation()
{
$state = AbstractDoorState::fromString('open');$this->assertInstanceOf(OpenDoorState::class, $state);
}/**
* @expectedException \YourVendor\YourProject\States\Exceptions\InvalidStateString
*/
public function testInvalidStateStringThrowsException()
{
try
{
AbstractDoorState::fromString('open.invalid');
}
catch (InvalidStateString $e)
{
$this->assertEquals('open.invalid', $e->getStateString());throw $e;
}
}
}
```### Test execution result with TestDox and text coverage summary
```
PHPUnit 6.0.6 by Sebastian Bergmann and contributors.Runtime: PHP 7.1.1 with Xdebug 2.5.0
Configuration: /Users/hollodotme/Sites/StateMachineGenerator/build/phpunit.xmlYourVendor\YourProject\Tests\Unit\ClosedDoorState
[x] Is not open
[x] Is closed
[x] Is not locked
[x] Can be opened
[x] Cannot be closed
[x] Can be locked
[x] Cannot be unlocked
[x] State can be represented as string
[x] Can build state from string representation
[x] Invalid state string throws exceptionYourVendor\YourProject\Tests\Unit\LockedDoorState
[x] Is not open
[x] Is not closed
[x] Is locked
[x] Cannot be opened
[x] Cannot be closed
[x] Cannot be locked
[x] Can be unlocked
[x] State can be represented as string
[x] Can build state from string representation
[x] Invalid state string throws exceptionYourVendor\YourProject\Tests\Unit\OpenDoorState
[x] Is open
[x] Is not closed
[x] Is not locked
[x] Cannot be opened
[x] Can be closed
[x] Cannot be locked
[x] Cannot be unlocked
[x] State can be represented as string
[x] Can build state from string representation
[x] Invalid state string throws exceptionCode Coverage Report Summary:
Classes: 100.00% (7/7)
Methods: 100.00% (26/26)
Lines: 100.00% (42/42)
```