PHPackages                             jeroen-g/statinator - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. jeroen-g/statinator

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

jeroen-g/statinator
===================

Next-gen PHP state machines

0.2(5y ago)5182[2 PRs](https://github.com/Jeroen-G/statinator/pulls)MITPHPPHP ^7.4|^8.0

Since Jun 15Pushed 1y ago1 watchersCompare

[ Source](https://github.com/Jeroen-G/statinator)[ Packagist](https://packagist.org/packages/jeroen-g/statinator)[ RSS](/packages/jeroen-g-statinator/feed)WikiDiscussions master Synced yesterday

READMEChangelog (2)Dependencies (3)Versions (5)Used By (0)

Statinator
==========

[](#statinator)

[![CI/CD](https://camo.githubusercontent.com/2c7c7b3a366204600fe7cda683222a1f7761f3d86e5ec26c92ba9ca2653d5fc6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f4a65726f656e2d472f616c70696e652d6172746973616e2f43493f6c6162656c3d43492532464344267374796c653d666c61742d737175617265)](https://github.com/Jeroen-G/alpine-artisan/actions?query=workflow%3ACI%2FCD)

Next-gen PHP state machines and state charts.

Installation
------------

[](#installation)

`composer require jeroen-g/statinator`

Usage
-----

[](#usage)

Imagine you want to create a state machine for a light switch in which the light can be in the state ON or OFF. A diagram would look as follows:

[![diagram](docs/lights-diagram.png)](docs/lights-diagram.png)

*Source: [statecharts.github.io](https://statecharts.github.io/)*

The Statinator configuration for this state machine would look like this:

```
$config = [
    'states' => [
      'LIGHTS_ON',
      'LIGHTS_OFF',
    ],
    'transitions' => [
        'FLICK_ON' => [
            'from' => ['LIGHTS_OFF'],
            'to' => ['LIGHTS_ON'],
        ],
        'FLICK_OFF' => [
            'from' => ['LIGHTS_ON'],
            'to' => ['LIGHTS_OFF'],
        ],
    ],
];
```

Then in your application you should instantiate Statinator with this configuration.

```
use JeroenG\Statinator\Statinator;
$statinator = new Statinator($config);
```

The Statinator accepts a second parameter as a repository implementing the `ActionRepositoryInterface`, this (optionally) stores successful and failed actions.

Any object that you want to use with a state machine should implement the `StatableInterface` contract. For the example of the light switch this might be:

```
use JeroenG\Statinator\StatableInterface;

class LightSwitch implements StatableInterface {
    private string $currentState;

    public function __construct(string $initialState)
    {
        $this->currentState = $initialState;
    }

    public function getState(): string
    {
        return $this->currentState;
    }

    public function setState(string $to): void
    {
        $this->currentState = $to;
    }
}
```

Anywhere in your code, you may use the Statinator instance to get a state machine and interact with its state and transitions.

```
$lightSwitch = new LightSwitch('LIGHTS_OFF');
$sm = $statinator->get($lightSwitch);

$sm->getState(); // LIGHTS_OFF
$sm->can('FLICK_ON'); // true
$sm->can('FLICK_OFF'); // false

$sm->apply('FLICK_ON');

$sm->getState(); // LIGHTS_ON
$sm->can('FLICK_ON'); // false
$sm->can('FLICK_OFF'); // true
```

While seeing objects move from one state to another is quite cool, the real power lies with the actions that you may define for transitions.

Before and after a state changes, the state machine executes any available actions. You can define actions as part of the global configuration, or by using a setter on the Statinator.

```
// Using the configuration
$config = [
    // ...
    'transitions' => [
        'FLICK_ON' => [
            'from' => ['LIGHTS_OFF'],
            'to' => ['LIGHTS_ON'],
            'on' => [
                'entry' => NotifyLightsAreOn::class,
                'exit' => NotifyLightsAreOff::class,
            ],
        ],
    // ...
    ],
];

// Or using a setter
$statinator->onEntry('FLICK_ON', NotifyLightsAreOn::class);
$statinator->onExit('FLICK_ON', NotifyLightsAreOff::class);
```

The actions can be a `callable` or a class implementing `ActionableInterface`.

```
$statinator->onEntry('FLICK_OFF', fn(StateMachineInterface $stateMachine, string $transition) => var_dump('Called it!'));
```

```
use JeroenG\Statinator\ActionableInterface;
use JeroenG\Statinator\StateMachineInterface;

class NotifyLightsAreOn implements ActionableInterface {
    private StateMachineInterface $stateMachine;
    private string $transition;

    public function getState(): string
    {
        return $this->stateMachine->getState();
    }

    public function execute(StateMachineInterface $stateMachine, string $transition): void
    {
        $this->stateMachine = $stateMachine;
        $this->transition = $transition;

        $notifier = new MyNotifier();
        $notifier->send('Lights are turned on');
    }

    public function getTransition(): string
    {
        return $this->transition;
    }
}
```

If your action implements the `ActionableInterface` it could be saved using a repository that is passed when you instantiated Statinator. This package ships with an `ArrayActionRepository` that is used by default and does not persist the data. Another possibility is the `LogActionRepository`, this one requires a PSR-compliant logger where the data will be persisted. You can of course also make your own repository (maybe one that uses the database) as long as it implements the `ActionRepositoryInterface`.

Contributing
------------

[](#contributing)

The project includes a Makefile to run install it, run the tests and check the code style.

###  Health Score

29

—

LowBetter than 60% of packages

Maintenance30

Infrequent updates — may be unmaintained

Popularity12

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 85.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~186 days

Total

2

Last Release

1971d ago

PHP version history (2 changes)0.1PHP ^7.4

0.2PHP ^7.4|^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/0d8700d69abe733de151f8cf49084e99ded7b9d34d7b0d1cd8f3825f5d925ff3?d=identicon)[JeroenG](/maintainers/JeroenG)

---

Top Contributors

[![Jeroen-G](https://avatars.githubusercontent.com/u/1116853?v=4)](https://github.com/Jeroen-G "Jeroen-G (6 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

###  Code Quality

TestsPHPUnit

Code StyleECS

### Embed Badge

![Health badge](/badges/jeroen-g-statinator/health.svg)

```
[![Health](https://phpackages.com/badges/jeroen-g-statinator/health.svg)](https://phpackages.com/packages/jeroen-g-statinator)
```

###  Alternatives

[mitch/hashids

Laravel package for Hashids

105108.8k](/packages/mitch-hashids)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
