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

[phannaly/php-datetime-khmer

The PHP library for convert datetime to Khmer

1510.3k](/packages/phannaly-php-datetime-khmer)[moosend/website-tracking

By installing the Moosend PHP Tracking library you are can track page views, product views, add to cart events and successful purchases. You can later use these details to segment your user base, run automations, check how successful your latest promo has been and how many conversions your landing page has led to.

1020.9k1](/packages/moosend-website-tracking)

PHPackages © 2026

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