php-architecture-kit / state-machine
Graph-based state machine engine for PHP — supports parallel pointers, conditional transitions, typed state, and domain events.
Package info
github.com/php-architecture-kit/state-machine
pkg:composer/php-architecture-kit/state-machine
Requires
- php: ^8.4
- php-architecture-kit/clock: ^1.0
- php-architecture-kit/domain-core: ^1.0
- php-architecture-kit/graph: ^1.0
- php-architecture-kit/technical: ^1.0
- php-architecture-kit/uuid: ^1.0
- psr/clock: ^1.0
- psr/container: ^2.0
This package is auto-updated.
Last update: 2026-05-07 10:02:27 UTC
README
Graph-based state machine engine for PHP. Supports parallel pointers, conditional transitions, typed state, pluggable transition strategies, and domain events — all built on top of php-architecture-kit/graph.
Features
- Graph-backed — transitions are directed edges, nodes are vertices
- Parallel pointers — multiple execution cursors moving through the graph simultaneously
- Conditional transitions — guard each edge with a
TransitionCondition - Typed state — key-value
Stateobjects attached to eachExecution - Domain events — pointer and state lifecycle emits
DomainEventinstances - Pluggable strategies — swap transition selection and pointer scheduling strategies
- PHP 8.4+ — uses asymmetric visibility and readonly classes
Installation
composer require php-architecture-kit/state-machine
Quick Start
use PhpArchitecture\StateMachine\StateMachine; use PhpArchitecture\StateMachine\Foundation\Execution\Execution; use PhpArchitecture\StateMachine\Foundation\Node\Node; use PhpArchitecture\StateMachine\Foundation\Node\Identity\NodeId; // 1. Define nodes final class SendEmail extends Node { public static function new(): self { return new self(NodeId::new()); } public function handlerClass(): string { return SendEmailHandler::class; } } // 2. Implement the handler use PhpArchitecture\StateMachine\Foundation\Node\Handler\NodeHandlerInterface; use PhpArchitecture\StateMachine\Foundation\Node\Handler\NodeHandlerContext; use PhpArchitecture\StateMachine\Foundation\Node\Handler\NodeHandlerResult; final class SendEmailHandler implements NodeHandlerInterface { public function handle(NodeHandlerContext $context): NodeHandlerResult { // do work... return NodeHandlerResult::Continue; } } // 3. Build and run the state machine final class OrderWorkflow extends StateMachine { public function build(): static { $start = SendEmail::new(); $finish = NotifyAdmin::new(); $this->addNode($start) ->addNode($finish) ->addTransition($start->id, $finish->id); return $this; } } $machine = (new OrderWorkflow($container))->build(); $execution = Execution::create(); $execution->pointers->createPointer($startNodeId); $status = $machine->execute($execution); // ExecutionStatus::Completed | ::Running | ::Suspended
Core Concepts
StateMachine
Extend StateMachine to define your workflow graph. Call addNode() and addTransition() in a build() method, then call execute(Execution $execution) to advance all pointers one scheduling round.
$status = $machine->execute($execution);
Returns ExecutionStatus::Completed when all pointers finish, ExecutionStatus::Suspended when blocked, or ExecutionStatus::Running when progress was made but pointers remain.
Node
Extend abstract class Node for each step in the workflow. Override handlerClass() to return the FQCN of the NodeHandlerInterface implementation that the PSR-11 container will resolve.
Override transitionStrategy() to control how outgoing transitions are selected for that node (defaults to AllValidTransitionsStrategy).
NodeHandlerInterface
interface NodeHandlerInterface { public function handle(NodeHandlerContext $context): NodeHandlerResult; }
NodeHandlerContext provides the ExecutionId, current Node, Pointer, and States. Return NodeHandlerResult::Completed to advance the pointer or NodeHandlerResult::Suspended to pause it.
Transition
Directed edge between two NodeIds. Created via Transition::create(). Optionally attach a TransitionCondition as a guard.
$this->addTransition($from->id, $to->id, new MyCondition());
Execution
Holds all Pointers and States for one running instance.
$execution = Execution::create(); $execution->pointers->createPointer($entryNodeId);
Serialise and restore with Execution::recreate().
Pointer
Tracks a single cursor's position (nodeId) and progress (currentStep). Pointers can be forked for parallel branches.
State
Named key-value bags attached to an execution.
$execution->states->define('order', [ new StateDetail('status', 'pending'), ]);
Retrieve and modify state inside handlers via $context->states.
StateMachineConfig
Controls graph constraints and pluggable strategies:
new StateMachineConfig( allowCycles: true, allowSelfLoops: true, allowParallelTransitions: true, transitionStrategies: [ new WaitAndForkStrategy(), new WaitStrategy(), new SingleTransitionStrategy(), new ForkTransitionStrategy(), new RejectStrategy(), ], pointersSelectionStrategy: new AllPointersUntilBlockedStrategy(), )
Built-in Transition Strategies
| Strategy | Behaviour |
|---|---|
SingleTransitionStrategy |
Follows the one valid outgoing transition |
AllValidTransitionsStrategy |
Follows all valid outgoing transitions |
ForkTransitionStrategy |
Forks a new pointer for each valid transition |
WaitStrategy |
Suspends the pointer until a transition becomes valid |
WaitAndForkStrategy |
Waits, then forks when multiple transitions become valid |
RejectStrategy |
Throws when no strategy matched (safety net) |
Built-in Pointer Selection Strategies
| Strategy | Behaviour |
|---|---|
AllPointersUntilBlockedStrategy |
Advances every pointer until each one blocks (default) |
AllPointersStepStrategy |
Advances every pointer exactly one step per execute() call |
License
MIT