PHP code example of php-architecture-kit / state-machine

1. Go to this page and download the library: Download php-architecture-kit/state-machine 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/ */

    

php-architecture-kit / state-machine example snippets


use PhpArchitecture\StateMachine\Foundation\StateMachine;
use PhpArchitecture\StateMachine\Foundation\Execution\Execution;
use PhpArchitecture\StateMachine\Foundation\Node\Node;
use PhpArchitecture\StateMachine\Foundation\Node\Identity\NodeId;
use PhpArchitecture\StateMachine\Foundation\Node\Handler\NodeHandlerInterface;
use PhpArchitecture\StateMachine\Foundation\Node\Handler\NodeHandlerContext;
use PhpArchitecture\StateMachine\Foundation\Node\Handler\NodeHandlerResult;

// 1. Define a node
final class SendEmailNode extends Node
{
    public function handlerClass(): string
    {
        return SendEmailHandler::class;
    }
}

// 2. Implement the handler
final class SendEmailHandler implements NodeHandlerInterface
{
    public function handle(NodeHandlerContext $context): NodeHandlerResult
    {
        // do work...
        return NodeHandlerResult::Continue;
    }
}

// 3. Define the workflow
final class OrderWorkflow extends StateMachine
{
    public function __construct(ContainerInterface $container)
    {
        parent::__construct($container);

        $send   = new SendEmailNode();
        $notify = new NotifyAdminNode();

        $this->addNode($send)
             ->addNode($notify)
             ->addTransition($send->id, $notify->id);
    }
}

// 4. Run
$machine   = new OrderWorkflow($container);
$execution = Execution::create();
$execution->pointers->startAt($sendNodeId);

$status = $machine->execute($execution);
// ExecutionStatus::Completed | ::Running | ::Suspended

$status = $machine->execute($execution);

interface NodeHandlerInterface
{
    public function handle(NodeHandlerContext $context): NodeHandlerResult;
}

$this->addTransition($from->id, $to->id);

// with a condition closure
$this->addTransition($from->id, $to->id, function (States $states): TransitionConditionDecision {
    return $states->getState('order') !== null
        ? TransitionConditionDecision::Accepted
        : TransitionConditionDecision::Wait;
});

$execution = Execution::create();
$execution->pointers->startAt($entryNodeId);

$execution->states->defineState('order', [
    new StateDetail('status', 'pending'),
    new StateDetail('amount', 1500),
]);

// read inside a handler or transition condition
$order = $context->states->getState('order');
$status = $order?->details['status']?->value;

new StateMachineConfig(
    allowCycles: true,
    allowSelfLoops: true,
    allowParallelTransitions: true,
    transitionStrategies: [...],
    pointersSelectionStrategy: new AllPointersUntilBlockedStrategy(),
)

$await = AwaitStateComponent::create('payment_result', detailName: 'status', timeout: 60);

$machine->addDefinition($await);
$machine->addTransition($prevNode->id,       $await->input->trigger);
$machine->addTransition($await->output->done,    $nextNode->id);
$machine->addTransition($await->output->expired, $timeoutNode->id);

$switch = SwitchCaseComponent::create([
    'approved' => fn(States $s): bool => $s->getState('decision')?->details['value']?->value === 'approved',
    'rejected' => fn(States $s): bool => $s->getState('decision')?->details['value']?->value === 'rejected',
]);

$machine->addDefinition($switch);
$machine->addTransition($prevNode->id,           $switch->input->trigger);
$machine->addTransition($switch->output->approved, $approvedNode->id);
$machine->addTransition($switch->output->rejected, $rejectedNode->id);

// all branches always fire
$parallel = ParallelComponent::create(['email', 'sms', 'audit']);

// some branches are conditional
$parallel = ParallelComponent::create(
    branches:   ['email', 'sms', 'audit'],
    conditions: [
        'sms'   => fn(States $s): bool => $s->getState('user')?->details['hasSms']?->value === true,
        'audit' => fn(States $s): bool => $s->getState('order')?->details['highValue']?->value === true,
    ],
);

$machine->addDefinition($parallel);
$machine->addTransition($prevNode->id,           $parallel->input->trigger);
$machine->addTransition($parallel->output->email, $emailNode->id);
$machine->addTransition($parallel->output->sms,   $smsNode->id);
$machine->addTransition($parallel->output->audit, $auditNode->id);

$join = AwaitAllComponent::create(['email', 'sms', 'audit']);

$machine->addDefinition($join);
$machine->addTransition($emailNode->id, $join->input->email);
$machine->addTransition($smsNode->id,   $join->input->sms);
$machine->addTransition($auditNode->id, $join->input->audit);
$machine->addTransition($join->output->done, $nextNode->id);

$async = AsyncComponent::create(
    stateName:   'payment_result',
    taskFactory: fn(States $s): Task => new ProcessPaymentTask(
        $s->getState('order')?->details['id']?->value,
    ),
    timeout: 120,
);

$machine->addDefinition($async);
$machine->addTransition($prevNode->id,      $async->input->trigger);
$machine->addTransition($async->output->done,    $nextNode->id);
$machine->addTransition($async->output->expired, $timeoutNode->id);

$execution->states->defineState('payment_result', [
    new StateDetail('status', 'completed'),
]);
$machine->execute($execution); // pointer resumes