Quick Start Examples
Simple examples you can copy and adapt.
Calculator
A single-state machine with inline actions and event payloads.
php
<?php
namespace App\Machines;
use Tarfinlabs\EventMachine\Actor\Machine;
use Tarfinlabs\EventMachine\ContextManager;
use Tarfinlabs\EventMachine\Definition\EventDefinition;
use Tarfinlabs\EventMachine\Definition\MachineDefinition;
class CalculatorMachine extends Machine
{
public static function definition(): ?MachineDefinition
{
return MachineDefinition::define(
config: [
'initial' => 'ready',
'context' => [
'result' => 0,
],
'states' => [
'ready' => [
'on' => [
'ADD' => ['actions' => 'additionAction'],
'SUBTRACT' => ['actions' => 'subtractionAction'],
'MULTIPLY' => ['actions' => 'multiplicationAction'],
'DIVIDE' => [
'guards' => 'notDivideByZeroGuard',
'actions' => 'divisionAction',
],
],
],
],
],
behavior: [
'guards' => [
'notDivideByZeroGuard' => fn(ContextManager $c, EventDefinition $e): bool
=> $e->payload['value'] !== 0,
],
'actions' => [
'additionAction' => fn(ContextManager $c, EventDefinition $e)
=> $c->result += $e->payload['value'],
'subtractionAction' => fn(ContextManager $c, EventDefinition $e)
=> $c->result -= $e->payload['value'],
'multiplicationAction' => fn(ContextManager $c, EventDefinition $e)
=> $c->result *= $e->payload['value'],
'divisionAction' => fn(ContextManager $c, EventDefinition $e)
=> $c->result /= $e->payload['value'],
],
],
);
}
}Usage
php
$machine = CalculatorMachine::create();
$machine->send(['type' => 'ADD', 'payload' => ['value' => 10]]);
$machine->send(['type' => 'MULTIPLY', 'payload' => ['value' => 3]]);
$machine->send(['type' => 'SUBTRACT', 'payload' => ['value' => 5]]);
expect($machine->state->context->result)->toBe(25);Traffic Light Counter
A counter with typed context, validation guards, and event classes.
Context Class
php
<?php
namespace App\Machines\TrafficLights;
use Spatie\LaravelData\Optional;
use Tarfinlabs\EventMachine\ContextManager;
use Spatie\LaravelData\Attributes\Validation\Min;
use Spatie\LaravelData\Attributes\Validation\IntegerType;
class TrafficLightsContext extends ContextManager
{
public function __construct(
#[IntegerType]
#[Min(0)]
public int|Optional $count,
) {
parent::__construct();
if ($this->count instanceof Optional) {
$this->count = 0;
}
}
}Machine Definition
php
<?php
namespace App\Machines\TrafficLights;
use Tarfinlabs\EventMachine\Actor\Machine;
use Tarfinlabs\EventMachine\Definition\MachineDefinition;
use Tarfinlabs\EventMachine\Behavior\ValidationGuardBehavior;
use Tarfinlabs\EventMachine\Behavior\ActionBehavior;
class IsEvenGuard extends ValidationGuardBehavior
{
public ?string $errorMessage = 'Count is not even';
public function __invoke(TrafficLightsContext $context): bool
{
return $context->count % 2 === 0;
}
}
class IncrementAction extends ActionBehavior
{
public function __invoke(TrafficLightsContext $context): void
{
$context->count++;
}
}
class MultiplyByTwoAction extends ActionBehavior
{
public function __invoke(TrafficLightsContext $context): void
{
$context->count *= 2;
}
}
class TrafficLightsMachine extends Machine
{
public static function definition(): MachineDefinition
{
return MachineDefinition::define(
config: [
'initial' => 'active',
'context' => TrafficLightsContext::class,
'states' => [
'active' => [
'on' => [
'INCREMENT' => ['actions' => IncrementAction::class],
'MULTIPLY' => [
'guards' => IsEvenGuard::class,
'actions' => MultiplyByTwoAction::class,
],
],
],
],
],
);
}
}Usage
php
$machine = TrafficLightsMachine::create();
$machine->send(['type' => 'INCREMENT']); // count = 1
$machine->send(['type' => 'INCREMENT']); // count = 2
$machine->send(['type' => 'MULTIPLY']); // count = 4 (2 is even, multiply passes)
expect($machine->state->context->count)->toBe(4);
// Guard blocks when count is odd
$machine->send(['type' => 'INCREMENT']); // count = 5
expect(fn() => $machine->send(['type' => 'MULTIPLY']))
->toThrow(MachineValidationException::class);