PHPackages                             dflydev/finite-state-machine - 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. dflydev/finite-state-machine

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

dflydev/finite-state-machine
============================

Yet another finite-state machine implementation

74.0k1[1 PRs](https://github.com/dflydev/dflydev-finite-state-machine/pulls)PHPCI failing

Since Apr 17Pushed 6mo agoCompare

[ Source](https://github.com/dflydev/dflydev-finite-state-machine)[ Packagist](https://packagist.org/packages/dflydev/finite-state-machine)[ RSS](/packages/dflydev-finite-state-machine/feed)WikiDiscussions master Synced yesterday

READMEChangelogDependenciesVersions (3)Used By (0)

Finite-State Machine
====================

[](#finite-state-machine)

This library is yet another finite-state machine implementation.

[![Build Status](https://github.com/dflydev/dflydev-finite-state-machine/workflows/Build%20Status/badge.svg)](https://github.com/dflydev/dflydev-finite-state-machine/workflows/Build%20Status/badge.svg)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/b3398587e97b5eb5485030f54c907ec55654ba9f2bd25ca539b361658f020cd1/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f64666c796465762f64666c796465762d66696e6974652d73746174652d6d616368696e652f6261646765732f7175616c6974792d73636f72652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/dflydev/dflydev-finite-state-machine/?branch=master)[![Code Coverage](https://camo.githubusercontent.com/bccce8df3ad9c53ee1ca78eeca29335d10146c9d71deb69d2b7baeeb266b918a/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f64666c796465762f64666c796465762d66696e6974652d73746174652d6d616368696e652f6261646765732f636f7665726167652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/dflydev/dflydev-finite-state-machine/?branch=master)[![Code Climate](https://camo.githubusercontent.com/803da6fcac13f0c1a0ea22d70a359ccee12d891dfca3253e6292b729ef5d9505/68747470733a2f2f636f6465636c696d6174652e636f6d2f6769746875622f64666c796465762f64666c796465762d66696e6974652d73746174652d6d616368696e652f6261646765732f6770612e737667)](https://codeclimate.com/github/dflydev/dflydev-finite-state-machine)

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

[](#installation)

```
composer require dflydev/finite-state-machine
```

Usage
-----

[](#usage)

Given the following definition for a domain class:

```
class DomainObject
{
    public string $state;
    public ?string $spy = null;

    public function __construct(string $state = 'new')
    {
        $this->state = $state;
    }
}
```

Given the following state definition for the "graphA" graph of our domain object:

```
$domainObjectGraphDefinition = [
   'class' => DomainObject::class,
   'graph' => 'graphA', // default is "default"
   'property_path' => 'state', // Configures `PropertyObjectProxy`
   'metadata' => [
       'title' => 'Graph A',
   ],
   'states' => [
       // a state as associative array
       ['name' => 'new'],
       // a state as associative array with metadata
       [
           'name' => 'pending_review',
           'metadata' => ['title' => 'Pending Review'],
       ],
       // states as string
       'awaiting_changes',
       'accepted',
       'published',
       'rejected',
   ],

   // list of all possible transitions
   'transitions' => [
       'create' => [
           'from' => ['new'],
           'to' => 'pending_review',
       ],
       'ask_for_changes' => [
           'from' =>  ['pending_review', 'accepted'],
           'to' => 'awaiting_changes',
           'metadata' => ['title' => 'Ask for changes'],
       ],
       'cancel_changes' => [
           'from' => ['awaiting_changes'],
           'to' => 'pending_review',
       ],
       'submit_changes' => [
           'from' => ['awaiting_changes'],
           'to' =>  'pending_review',
       ],
       'approve' => [
           'from' => ['pending_review', 'rejected'],
           'to' =>  'accepted',
       ],
       'publish' => [
           'from' => ['accepted'],
           'to' =>  'published',
       ],
   ],

   // list of all callbacks
   'callbacks' => [
       // will be called when testing a transition
       'guard' => [
           'guard_on_approving_from_rejected' => [
               // call the callback on a specific transition
               'on' => 'approve',
               'from' => 'rejected',
               // will call the method of this class
               'do' => function (
                   object $object,
                   Transition $transition,
                   State $fromState,
                   State $toState
               ) {
                   $object->spy = 'guard_on_approving_from_rejected';

                   // If a guard returns false, the transition will not happen
                   return false;
               },
               // arguments for the callback
               'args' => ['object'],
           ],
       ],

       // will be called before applying a transition
       'before' => [
           'spy-before-approve' => [
               'on' => 'ask_for_changes',
               'from' => 'accepted',
               'do' => function (
                   string $when,
                   object $object,
                   Transition $transition,
                   State $fromState,
                   State $toState
               ) {
                   Assert::equals($fromState->name(), $object->state);

                   $object->spy = $when . ' ask_for_changes from accepted';
               },
           ]
       ],

       // will be called after applying a transition
       'after' => [
           'spy-after-approve' => [
               'on' => 'ask_for_changes',
               'from' => 'accepted',
               'do' => function (
                   string $when,
                   object $object,
                   Transition $transition,
                   State $fromState,
                   State $toState
               ) {
                   Assert::equals($toState->name(), $object->state);
                   Assert::equals('before ask_for_changes from accepted', $object->spy);

                   $object->spy = $when . ' ask_for_changes from accepted';
               },
           ]
       ],
   ]
];
```

```
use Dflydev\FiniteStateMachine\FiniteStateMachineFactory;
use Dflydev\FiniteStateMachine\Graph\GraphResolver;
use Dflydev\FiniteStateMachine\Loader\WinzouArrayLoader;
use Dflydev\FiniteStateMachine\ObjectProxy\ObjectProxyResolver;
use Dflydev\FiniteStateMachine\ObjectProxy\PropertyObjectProxyFactory;

$graphResolver = new GraphResolver();
$objectProxyResolver = new ObjectProxyResolver();

// Add an object proxy that can directly read the state property from our objects
$objectProxyResolver->add(new PropertyObjectProxyFactory());

// Load a graph definition into our graph resolver
(new WinzouArrayLoader($graphResolver))->load($domainObjectGraphDefinition);

$finiteStateMachineFactory = new FiniteStateMachineFactory(
    $this->getGraphResolver(),
    $this->getObjectProxyResolver()
);

$finiteStateMachine = $finiteStateMachineFactory->build($object);

// "new"
$finiteStateMachine->currentState()->name();

// (bool) false
$finiteStateMachine->can('ask_for_changes');

// (bool) true
$finiteStateMachine->can('create');

$finiteStateMachine->apply('create');

// "pending_review"
$finiteStateMachine->currentState()->name();
```

Graph Definitions and Loaders
-----------------------------

[](#graph-definitions-and-loaders)

A graph is a named collection of states, transitions, and callbacks. An object may have multiple graphs defined.

The `GraphResolver` is responsible for resolving the graph definition for a given object (and optionally a graph name).

A `Graph` can be created manually and added to a `GraphResolver`. A `Loader` can be used to load a `Graph` into a `GraphResolver` based on specific types of resources.

### winzou/state-machine

[](#winzoustate-machine)

This library ships with `WinzouArrayLoader`, a `Loader` implementation that is loosely drop-in compatible with [winzou/state-machine](https://github.com/winzou/state-machine) array-based graph definitions.

### Custom

[](#custom)

This library ships with a `Loader` contract. Implementing this interface allows for the creation of custom graph definitions.

License
-------

[](#license)

MIT, see [LICENSE](LICENSE).

###  Health Score

31

—

LowBetter than 65% of packages

Maintenance46

Moderate activity, may be stable

Popularity23

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% 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.

### Community

Maintainers

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

---

Top Contributors

[![simensen](https://avatars.githubusercontent.com/u/191200?v=4)](https://github.com/simensen "simensen (8 commits)")

### Embed Badge

![Health badge](/badges/dflydev-finite-state-machine/health.svg)

```
[![Health](https://phpackages.com/badges/dflydev-finite-state-machine/health.svg)](https://phpackages.com/packages/dflydev-finite-state-machine)
```

###  Alternatives

[codingfreaks/cf-cookiemanager

Manage cookies, scripts, and GDPR compliance on your Typo3 website with CodingFreaks Typo3 Cookie Manager. Customize cookie banners, streamline workflow, and enhance user experience. Ensure GDPR compliance and take control of cookie management with our Typo3 cookie management extension. Visit the official Typo3 Documentation page to learn more.

1833.0k](/packages/codingfreaks-cf-cookiemanager)[arachne/entity-loader

Enables object parameters in nette/application.

116.0k2](/packages/arachne-entity-loader)

PHPackages © 2026

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