potfur / statemachine
State machine
Requires
- php: >=5.4
Requires (Dev)
- phpmd/phpmd: ~2.2
- phpunit/phpunit: ~4.6
- squizlabs/php_codesniffer: ~2.3
This package is not auto-updated.
Last update: 2024-10-26 17:55:57 UTC
README
StateMachine is an implementation of state pattern, also can be treated as non-deterministic finite automata. In different words - machine will react to events and move conext from one state to another depending on result of executed command.
StateMachine can be used to describe order & payment processing, newsletter opt-in process, customer registration - any not trivial process.
Processes
StateMachine follows process defined by states taken by context and events that activate transitions between them.
Each of events can define two transitions successful and erroneous. Also, each event can have a callable command that will be executed before transition. Value returned by it, will decide which transition will be chosen.
When command returns truthy
value - StateMachine will follow successful transitions, any falsy
value will be erroneous transition.
If event had no command - StateMachine will follow successfull transition.
Each state can have special event StateMachine::ON_STATE_WAS_SET
which will be triggered as soon as context enters state.
Schema
Processes are constructed from schemas.
StateMachine comes with ArrayFactory
which creates Process
instances from plain arrays.
$commandResults = [ true, // from new to pending false, // from pending to error (onStateWasSet) true, // from error to pending (onStateWasSet) true, // from pending to done (onStateWasSet) ]; $command = function () use (&$commandResults) { if (!count($commandResults)) { throw new \InvalidArgumentException('Out of results'); } return array_shift($commandResults); }; $schema = [ 'name' => 'testSchema', 'initialState' => 'new', 'states' => [ [ 'name' => 'new', 'events' => [ [ 'name' => 'goPending', 'targetState' => 'pending', 'errorState' => 'error', 'command' => $command, ] ], ], [ 'name' => 'pending', 'events' => [ [ 'name' => StateMachine::ON_STATE_WAS_SET, 'targetState' => 'done', 'errorState' => 'error', 'command' => $command ] ], ], [ 'name' => 'error', 'events' => [ [ 'name' => StateMachine::ON_STATE_WAS_SET, 'targetState' => 'pending', 'errorState' => 'error', 'command' => $command ] ], ], [ 'name' => 'done', 'events' => [], ] ] ]; $process = (new ArrayFactory($schema))->getProcess();
Payload and triggering events
To transit from one state to another an event needs to be trigered. When it happens StateMachine will pass provided context wrapped as a payload to command, execute it and follow resulting transitions.
$payload = PayloadEnvelope::wrap('context'); $machine = new StateMachine($process); $history = $machine->triggerEvent('goPending', $payload);
PayloadEnvelope
is an implementation of Payload
- simple wrapper that holds context. It can be anything, simple value, array, object etc.
::triggerEvent
method returns history of transitions, ie. list of all states that context passed.