Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

28 Commits

Repository files navigation

PHP Finite-state Machine

Finite-state machine allows you to create an object, containing different states and transitions between them, that can change its behaivour according to the current state.

Install:

For installing the FSM machine use Composer. Simply add following to your composer.json file:

"require": {
"zinovyev/php-fsm": "0.6.5"
}

Creating a State machine steps:

  • Create State classes and define states:
class StateA extends State
{
publicfunctionfoo($name)
{
printf("Hello, %s!", $name);
}
}
  • Create a new FSM\Client instance (StateMachine).
  • Bind your States to the Client instance, apply transactions and set initial state:
class StateMachine extends Client
{
publicfunction __construct()
{
parent::__construct();
/* ... */$this
->addState($stateA)
/* ... */
->createTransition('fourth', 'stateA', 'stateB')
/* ... */

or

$stateMachine = newClient;
$stateMachine
->addState($stateA)
->addState($stateB)
->createTransition('fourth', 'stateA', 'stateB')
;
  • Use Memento if you want do store current State and parameters and restore them later:
$memento = $stateMachine->createMemento();

and restore:

$stateMachine = newStateMachine();
$stateMachine->applyMemento($memento);
  • That's all =) Your simple State machine is now configured and ready for use!

Example code:

<?phprequire_once('vendor/autoload.php');
useFSM\Client;
useFSM\State\State;
useFSM\State\StateInterface;
/** * StateA class */class StateA extends State
{
publicfunctionfoo($f)
{
return$f;
}
}
/** * StateB class */class StateB extends State
{
publicfunctionbar($b)
{
return$b * 2;
}
}
/** * StateC class */class StateC extends State
{
publicfunctionfooBar($fb)
{
returnstrrev($fb);
}
publicfunctionshuffle(array$array) {
shuffle($array);
return$array;
}
}
/** * StateMachine example class */class StateMachine extends Client
{
publicfunction__construct()
{
parent::__construct();
// Create StateA initial type instance of class StateA$stateA = newStateA();
$stateA->setName('stateA');
$stateA->setType(StateInterface::TYPE_INITIAL);
// Create StateB finite type instance of class StateB$stateB = newStateB();
$stateB->setName('stateB');
$stateB->setType(StateInterface::TYPE_FINITE);
// Create StateC regular type instance of class StateC$stateC = newStateC();
$stateC->setName('stateC');
$stateC->setType(StateInterface::TYPE_REGULAR);
// Create StateD regular type instance of class StateB$stateD = newStateB();
$stateD->setName('stateD');
$stateD->setType(StateInterface::TYPE_REGULAR); // Attach states and transitions$this
->addState($stateA)
->addState($stateB)
->addState($stateC)
->addState($stateD)
->setInitialState($stateA)
->createTransition('initial', 'stateA', 'stateA')
->createTransition('second', 'stateA', 'stateC')
->createTransition('secondAlternative', 'stateA', 'stateD')
->createTransition('third', 'stateC', 'stateD')
->createTransition('thirdAlternative', 'stateD', 'stateC')
->createTransition('fourth', 'stateD', 'stateB')
->createTransition('fourthAlternative', 'stateC', 'stateB')
->createTransition('shortWay', 'stateA', 'stateB')
;
}
}
// Create new StateMachine instance$stateMachine = newStateMachine();
$stateMachine->foo = 'bar'; // Add public property// Test StateA stateprintf("%d) State machine is at state: '%s'. Test function call result is: '%s'\n", 1,
$stateMachine->getCurrentState()->getName(), // Get State Name$stateMachine->callAction('foo', $properties = [100]) // Call State function
);
// Accept transition "initial"$stateMachine->acceptTransitionByName('initial');
printf("%d) State machine is at SAME state: '%s'. Test function call result: '%s'\n", 2,
$stateMachine->getCurrentState()->getName(), // Get State Name$stateMachine->callAction('foo', $properties = [200]) // Call State function
);
// Accept transition "second"$stateMachine->acceptTransitionByName('second');
printf("%d) State machine is at state: '%s'. Test function call result: '%s'\n", 3,
$stateMachine->getCurrentState()->getName(), // Get State Name$stateMachine->fooBar('foo bar') // Call State function
);
// Create a memento (snapshot) of the current state$memento = $stateMachine->createMemento();
// Unset StateMachine$stateMachine = null;
unset($stateMachine);
// Restore StateMachine from a snapshot (Memento)$stateMachine = newStateMachine();
$stateMachine->applyMemento($memento);
printf("=*= Check property value after restore: \$foo='%s' =*=\n", $stateMachine->foo);
// Accept transition "third"$stateMachine->acceptTransitionByName('third');
printf("%d) State machine is at state '%s'. Test function call result: '%s'\n", 4,
$stateMachine->getCurrentState()->getName(), // Get State Name$stateMachine->bar(1) // Call State function
);
// Accept transition "fourth"$stateMachine->acceptTransitionByName('fourth');
printf("%d) State machine is at state '%s'. Test function call result: '%s'\n", 5,
$stateMachine->getCurrentState()->getName(), // Get State Name$stateMachine->bar(2) // Call State function
);

Example code execution result:

If you run the code in your console, you'll see the following output:

1) State machine is at state: 'stateA'. Test function call result is: '100'
2) State machine is at SAME state: 'stateA'. Test function call result: '200'
3) State machine is at state: 'stateC'. Test function call result: 'rab oof'
=*= Check property value after restore: $foo='bar' =*=
4) State machine is at state 'stateD'. Test function call result: '2'
5) State machine is at state 'stateB'. Test function call result: '4'

About

PHP Finite-state Machine

Resources

Stars

9 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages