webnarmin/automata

Framework-agnostic orchestration engine for finite state machines, statecharts, and multi-agent systems in PHP.

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 1

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/webnarmin/automata

dev-main 2025-10-11 18:54 UTC

This package is auto-updated.

Last update: 2025-10-11 18:55:26 UTC


README

CI PHPStan PHPUnit License

Framework-agnostic orchestration engine for finite state machines (FSM), statecharts, and small multi-agent systems in PHP.

Features

  • Decouples automata execution from application code through a central orchestrator
  • Supports collaborative scenarios with FSM automata and pluggable cycle middleware
  • Provides immutable input payloads, shared context storage, and snapshot/restore facilities
  • Ships with lightweight container and context implementations without framework dependencies
  • Includes PHPUnit coverage for the orchestrator lifecycle and transition handling

Requirements

  • PHP 8.1 or newer
  • Composer

Installation

Install the library via Composer:

composer require webnarmin/automata

Or add it to your project's composer.json:

{
    "require": {
        "webnarmin/automata": "^1.0"
    }
}

Then run composer install.

Add the library to your project through Composer or clone this repository and autoload it via the included PSR-4 configuration.

Getting Started

Instantiate the message bus, context, and orchestrator. Register automata that implement Automata\Contracts\AutomatonInterface and return a Automata\Core\CycleResponse, then start the orchestrator using the identifier of the initial FSM automaton.

<?php

use Automata\Core\Context\ArrayContext;
use Automata\Core\Orchestrator;
use Automata\Contracts\AutomatonInterface;
use Automata\Contracts\ContextInterface;
use Automata\Contracts\InputInterface;
use Automata\Core\CycleRequest;
use Automata\Core\CycleResponse;

$context = new ArrayContext();

$automaton = new class implements AutomatonInterface {
    public function getId(): string { return 'demo.automaton'; }
    public function onEnter(ContextInterface $context): void { /* setup */ }
    public function process(CycleRequest $request): CycleResponse {
        $context = $request->getContext();
        // business logic mutating $context or reading $request->getInput()...
        return new CycleResponse(); // chain ->withCommand()/->withEvent() to enqueue messages
    }
    public function onLeave(ContextInterface $context): void { /* cleanup */ }
};

$orchestrator = new Orchestrator($context);
$orchestrator->registerState($automaton);
$orchestrator->activate('demo.automaton');
$orchestrator->tick(new class implements InputInterface {});

Snapshots of automata and context state can be captured with Orchestrator::snapshot() and later reapplied using Orchestrator::activateFromSnapshot() to support persistence or time-travel debugging.

// Capture a snapshot for later recovery
$snapshot = $orchestrator->snapshot();

// ...later, possibly in a different process
$restoredContext = new ArrayContext();
$restoredOrchestrator = new Orchestrator($restoredContext);
$restoredOrchestrator->registerState($automaton);
$restoredOrchestrator->activateFromSnapshot($snapshot);

// Continue processing with the same automaton after restoring
$restoredOrchestrator->tick(new class implements InputInterface {});

Traffic Light Example

The examples/traffic-light directory contains a self-contained CLI simulation that demonstrates:

  • three color-specific automata coordinating through transitions
  • a middleware counting completed ticks
  • command and event dispatch coordinated via CycleResponse

Run the demo after installing dependencies:

php examples/traffic-light/run.php

A sample run prints the state changes and demonstrates snapshot/restore:

=== Initial execution ===
[2025-10-11 10:25:56] Traffic light changed from RED to GREEN
   State -> color=GREEN | total_ticks=2 (even)
   State -> color=GREEN | total_ticks=3 (odd)
[2025-10-11 10:25:56] Traffic light changed from GREEN to YELLOW
   State -> color=YELLOW | total_ticks=4 (even)
   State -> color=YELLOW | total_ticks=5 (odd)

=== Snapshot saved after tick 4 (active automaton: traffic_light.yellow) ===

=== Restored execution ===
   State -> color=YELLOW | total_ticks=6 (even)
[2025-10-11 10:25:56] Traffic light changed from YELLOW to RED
   State -> color=RED | total_ticks=7 (odd)
[2025-10-11 10:25:56] Traffic light changed from RED to GREEN
   State -> color=GREEN | total_ticks=8 (even)
   State -> color=GREEN | total_ticks=9 (odd)

Simulation complete.

Testing

composer test

License

This project is released under the MIT License. See LICENSE for details.