Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save acorncom/c41f56ea1b8b29693b79 to your computer and use it in GitHub Desktop.
Save acorncom/c41f56ea1b8b29693b79 to your computer and use it in GitHub Desktop.

FiniteStateMachine Trait

Add a DSL to the PHP Finite package, borrowed from the Ruby StateMachine gem.

Usage

In your Stateful Class, add the stateMachineConfig() method and call initStateMachine() method at initialization (__contruct() method).

Example

Check MyStatefulClass.php and Usage.php.

Versions

My previous version of this DSL can be found here

License

MIT

Use At Your Own Risk

<?php
use Finite\StateMachine\StateMachine;
/**
* The FiniteStateMachine Trait.
* It provides easy ways to deal with Stateful objects and StateMachine
* Prerequisite: install Finite package (https://github.com/yohang/Finite#readme)
* Usage: in your Stateful Class, add the stateMachineConfig() protected method
* and call initStateMachine() method at initialization (__contruct() method)
*
* @author Tortue Torche <[email protected]>
*/
trait FiniteStateMachine
{
/**
* @var Finite\StateMachine\StateMachine
*/
protected $stateMachine;
/**
* @var array
*/
protected $finiteLoader;
/**
* @return array
*/
protected abstract function stateMachineConfig();
public function initStateMachine()
{
$this->finiteLoader = $this->stateMachineConfig();
$loader = new Finite\Loader\ArrayLoader($this->finiteLoader);
$sm = new StateMachine($this);
$loader->load($sm);
$sm->initialize();
$this->stateMachine = $sm;
}
/**
* Sets the object state
*
* @param string $state
*/
public function setFiniteState($state)
{
$this->state = $state;
}
/**
* Get the object state
*
* @return string
*/
public function getFiniteState()
{
return $this->state;
}
/**
* @return Finite\StateMachine\StateMachine
*/
protected function getStateMachine()
{
return $this->stateMachine;
}
/**
* @return Finite\State\State
*/
public function getCurrentState()
{
return $this->getStateMachine()->getCurrentState();
}
/**
* @return string
*/
public function getState()
{
return $this->getCurrentState()->getName();
}
/**
* @return array<string>
*/
public function getTransitions()
{
return $this->getCurrentState()->getTransitions();
}
/**
* @return array<string>
*/
public function getProperties()
{
return $this->getCurrentState()->getProperties();
}
/**
* @param string $property
*
* @return bool
*/
public function hasProperty($property)
{
return $this->getCurrentState()->has($property);
}
/**
* @param string $targetState
*
* @return bool
*/
public function is($targetState)
{
return $this->getState() === $targetState;
}
/**
* @param string $transitionName
*
* @return bool
*/
public function can($transitionName)
{
return $this->getStateMachine()->can($transitionName);
}
/**
* @param string $transitionName
*
* @return mixed
*
* @throws Finite\Exception\StateException
*/
public function apply($transitionName)
{
return $this->getStateMachine()->apply($transitionName);
}
}
<?php
class MyStatefulClass implements Finite\StatefulInterface
{
use FiniteStateMachine;
public function __construct()
{
$this->initStateMachine();
}
protected function stateMachineConfig()
{
return [
//'class' => get_class(),//useful?
'states' => [
's1' => [
'type' => 'initial',
'properties' => ['deletable' => true, 'editable' => true],
],
's2' => [
'type' => 'normal',
'properties' => [],
],
's3' => [
'type' => 'final',
'properties' => [],
]
],
'transitions' => [
't12' => ['from' => ['s1'], 'to' => 's2'],
't23' => ['from' => ['s2'], 'to' => 's3'],
't21' => ['from' => ['s2'], 'to' => 's1'],
],
'callbacks' => [
'before' => [
['on' => 't12', 'do' => [$this, 'beforeTransitionT12']],
['from' => 's2', 'to' => 's3', 'do' => function($myStatefulInstance, $transitionEvent) {
echo "Before callback from 's2' to 's3'";// debug
}],
['from' => '-s3', 'to' => ['s3' ,'s1'], 'do' => [$this, 'fromStatesS1S2ToS1S3']],
],
'after' => [
['from' => 'all', 'to' => 'all', 'do' => [$this, 'afterAllTransitions']],
],
],
];
}
public function beforeTransitionT12($myStatefulInstance, $transitionEvent)
{
echo "Function called before transition: '".$transitionEvent->getTransition()->getName()."' !";// debug
}
public function fromStatesS1S2ToS1S3()
{
echo "Before callback from states 's1' or 's2' to 's1' or 's3'";// debug
}
public function afterAllTransitions($myStatefulInstance, $transitionEvent)
{
echo "After All Transitions !";// debug
}
}
<?php
$myStatefulObject = new MyStatefulClass;
$myStatefulObject->getState(); // → "s1"
$myStatefulObject->can('t23'); // → false
$myStatefulObject->can('t12'); // → true
$myStatefulObject->apply('t12'); // → NULL
$myStatefulObject->is('s2'); // → true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment