PHP code example of rolfvreijdenberger / izzum-statemachine

1. Go to this page and download the library: Download rolfvreijdenberger/izzum-statemachine library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

rolfvreijdenberger / izzum-statemachine example snippets


//retrieve the id somewhere from your application (form, database, domain model, user input etc)
$identifier = new Identifier('198442', 'order');//the identifier for your machine
$context = new Context($identifier);//other parameters for Context will be explained later
$machine = new StateMachine($context);//create the statemachine

$new = new State('new', State::TYPE_INITIAL);//there must be 1 initial state
$action = new State('action');//a normal state
$done = new State('done', State::TYPE_FINAL);//one of potentially many final states
$machine->addTransition(new Transition($new, $action, 'go'));//add a transition between states that is triggered by an event
$machine->addTransition(new Transition($action, $done, 'finish'));
//result: 3 states and 2 transitions defined on the statemachine

$context = $machine->getContext();
echo $context->getPersistenceAdapter();//echo works because of Memory::__toString()
# Memory
echo $context->getEntityId();//get the id for your domain model (entity)
# 198442
echo $context->getMachine();//get the name of the statemachine
# order
echo count($machine->getStates());//get the defined states directly from the machine
# 3
echo count($machine->getTransitions());//get the defined transitions directly from the machine
# 2

$state = $machine->getCurrentState();
echo $state->getName();
# new
echo $state->getType();
# initial
echo $machine->getInitialState();//echo works because of State::__toString()
# new
foreach($machine->getStates() as $state) {
    echo $state->getName();
}
# new, action, done

//action, or any state ending with 'ew'
$regex = new State('regex:/action|.*ew$/', State::TYPE_REGEX);
$pause = new State('pause');
$machine->addTransition(new Transition($regex, $pause), 'pause');
//new->pause, action->pause

//the current state 'new', has a transition that can be triggered by the 'go' event
echo $machine->hasEvent('go'); 
# true
//current state is 'new' and 'finish' is only a valid event for the 'action' state
echo $machine->hasEvent('finish');
# false
echo $machine->canHandle('go');//this will check the guard logic for the 'go' event (explanation later)
# true
//transitions have a name derived from their 'from' and 'to' states
echo $machine->canTransition('new_to_action');
# true
//not in the 'action' state
echo $machine->canTransition('action_to_done');
# false
//check the state itself for a transition
echo $machine->getCurrentState()->hasTransition('new_to_action');
# true
foreach ($machine->getTransitions() as $transition) {
    echo $transition->getName() . ":" . $transition->getEvent(); 
}
# new_to_action:go, action_to_done:finish, new_to_pause:pause, action_to_pause:pause

//the next three lines all produce the same result and are different ways to perform the transition to 'action' from the state 'new'
$machine->handle('go');//handle the 'go' trigger/event to transition to 'action' state
$machine->go();//use the trigger name directly as a method on the machine
$machine->transition('new_to_action');//transition by name <from>_to_<to>

//suppose we are in the 'action' state, the next lines produce the same result: a transition to 'done'
$machine->handle('finish');//transition to 'done' state via 'finish' trigger/event
$machine->finish();
$machine->transition('action_to_done');

echo $machine->run();//perform the first transition from the current state that can run
# true
//perform as many transitions as the machine allows: 
//each transition will go to the 'to' state and will try the next transition from there,
//until it is in a final state or transitions are not possible for that new current state.
echo $machine->runToCompletion();//suppose we started in 'new', then 2 transitions will be made
# 2

$identifier = new Identifier('198442', 'order-machine');
$builder = new OrderBuilder();
$context = new Context($identifier, $builder);
$machine = new StateMachine($context);
//the builder class for an Order would look like this:
class OrderBuilder extends EntityBuilder{
  protected function build(Identifier $identifier) {
    return new Order($identifier->getEntityId());
  }
}

$forbidden = new State('forbidden');
$closure = function($entity, $event){return false;};
$transition = new Transition($new, $forbidden, 'thoushaltnotpass', null, null, $closure);
// or: $transition->setGuardCallable($closure);
$machine->addTransition($transition);
echo $machine->hasEvent('thoushaltnotpass');
# true
echo $machine->handle('thoushaltnotpass');//transition will not be made
# false
echo $machine->transition('new_to_forbidden');
# false
echo $machine->getCurrentState();//still in the same state
# new

$forbidden = new State('forbidden');
$rule = '\izzum\rules\FalseRule';
$transition = new Transition($new, $forbidden, 'thoushaltnotpass', $rule);
// or: $transition->setRuleName($rule);
$machine->addTransition($transition);
echo $machine->hasEvent('thoushaltnotpass');
# true
echo $machine->handle('thoushaltnotpass');//transition will not be made
# false
echo $machine->transition('new_to_forbidden');
#> false
echo $machine->getCurrentState();//still in the same state
# new

class IsAllowedToShip extends Rule {
  public function __construct(Order $order) { $this->order = $order;}
  protected function _applies() { return $this->order->isPaid(); }
}

$rule = '\izzum\rules\IsAllowedToShip';
$transition = new Transition($action, new State('shipping'), 'ship', $rule);

class MyEventHandlingClass {
  public function onCheckCanTransition($identifier, $transition, $event) { 
    echo "checking transition (" . $identifier->getEntityId() . ") " . $transition->getName() ' for event: ' . $event;
    //normally, you would put your guard logic here...
    return true;
  }
}

$builder = new ModelBuilder(new MyEventHandlingClass());
$context = new Context($identifier, $builder);
$statemachine = new StateMachine($context);
//load the machine here and assume we are in the 'new' state in which a transition can be triggered by the 'go' event
$statemachine->handle('go');
# checking transition (198442) new_to_action for event: go

Class OrderDelivery extends Command {
public function __construct(Order $order) { $this->order = $order;}
  protected function _execute() { $this->order->deliver(); }
}
$command = '\izzum\command\OrderDelivery';
//assume we are using the rule from the example
$transition = new Transition($action, new State('shipping'), 'ship', $rule, $command);


$adapter = new Session();
$machine = new StateMachine(new Context(new Identifier('session', 'rainbow-machine'), null, $adapter));
$machine->run();


$identifier('UUID-1234-ACD3-2156', 'data-migration-machine');
$adapter = new PDO('pgsql:host=localhost;port=5432;dbname=izzum');
//or for mysql
$adapter = new PDO('mysql:host=localhost;dbname=izzum');
//or for sqlite
$adapter = new PDO('"sqlite:izzum.db"');
$context = new Context($identifier, $builder, $adapter);
$statemachine = new StateMachine($context);
$adapter->load($statemachine);//the adapter can also act as a loader
$statemachine->add('creation of machine...');

$identifier('UUID-1234-ACD3-2156', 'data-migration-machine');
$adapter = new Redis('127.0.0.1', 6379);
//or use mongodb
$adapter = new MongoDB('mongodb://localhost:27017');
$context = new Context($identifier, $builder, $adapter);
$statemachine = new StateMachine($context);
$adapter->load($statemachine);//the adapter can also act as a loader
$statemachine->add('creation of machine...');

$statemachine = new StateMachine(new Context(new Identifier('198442' , 't-shirt-production-facility-machine')));
$file = __DIR__ . '/machines.xml';
$loader = XML::createFromFile($file);
$loader->load($statemachine);
$statemachine->runToCompletion();

$statemachine = new StateMachine(new Context(new Identifier('btc-data-generator' , 'blockchain-parsing-machine')));
$file = __DIR__ . '/machines.json';
$loader = JSON::createFromFile($file);
$loader->load($statemachine);
$statemachine->runToCompletion();

$statemachine = new StateMachine(new Context(new Identifier('wolverine' , 'mutant-machine')));
$file = __DIR__ . '/machines.yaml';
$loader = YAML::createFromFile($file);
$loader->load($statemachine);
$statemachine->runToCompletion();

$statemachine = new StateMachine(new Context(new Identifier('spiderman' , 'superhero-machine')));
$adapter = new PDO('pgsql:host=208.64.123.130;port=5432;dbname=izzum');
$adapter->load($statemachine);
$statemachine->run();

$redis = new Redis('127.0.0.1', 6379);
$machine = new StateMachine(new Context(new Identifier(1988442, 'crazy-machine'), null, $redis));
//set the configuration. Normally, this would be done directly on redis in a 
//seperate process before using the statemachine (eg: during deployment)
$configuration = file_get_contents(__DIR__ .'/redis-configuration-example.json');
$redis->set(Redis::KEY_CONFIGURATION, $configuration);
//load the machine
$redis->load($machine);


$loader = XML::createFromFile(__DIR__ . '/configuration.xml');
$writer = new PDO('pgsql:host=208.64.123.130;port=5432;dbname=izzum');//postgres backend
$identifier = new Identifier('198442', 'awesome-machine');
$delegator = new ReaderWriterDelegator($loader, $writer);
$context = new Context($identifier, null, $delegator);
$machine = new StateMachine($context);
$delegator->load($machine);//loads from xml file
$machine->run();//stores data in postgres