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

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

sofa/state-machine
==================

Easy to use implementation of the Finite State Machine

0.4.1(1y ago)8162.7k↓28.8%MITPHPPHP &gt;=7.1.0

Since Jul 21Pushed 1y ago1 watchersCompare

[ Source](https://github.com/jarektkaczyk/state-machine)[ Packagist](https://packagist.org/packages/sofa/state-machine)[ Fund](https://softonsofa.com)[ GitHub Sponsors](https://github.com/jarektkaczyk)[ RSS](/packages/sofa-state-machine/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (5)Dependencies (1)Versions (6)Used By (0)

Sofa/StateMachine
=================

[](#sofastatemachine)

#### [Finite State Machine](https://en.wikipedia.org/wiki/Finite-state_machine) implementation

[](#finite-state-machine-implementation)

[![Downloads](https://camo.githubusercontent.com/fa9f27d1dc340b88b3bcd454f383cee94edfaa0e98d6eb315bfc762c9890d14f/68747470733a2f2f706f7365722e707567782e6f72672f736f66612f73746174652d6d616368696e652f646f776e6c6f616473)](https://packagist.org/packages/sofa/state-machine) [![stable](https://camo.githubusercontent.com/10cc71cc0e590d7bca3e76445cf4f73c720ff10281d61ca693f1dfa8b084f428/68747470733a2f2f706f7365722e707567782e6f72672f736f66612f73746174652d6d616368696e652f762f737461626c652e737667)](https://packagist.org/packages/sofa/state-machine)[![Coverage Status](https://camo.githubusercontent.com/29210c31b3a7f317743628c1f21f3b9b285b090fadce0fada8956a235b94a0ab/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f6a6172656b746b61637a796b2f73746174652d6d616368696e652f62616467652e7376673f6272616e63683d6d6173746572)](https://coveralls.io/github/jarektkaczyk/state-machine?branch=master)

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

[](#installation)

Add package to your project:

```
composer require sofa/state-machine

```

Usage
-----

[](#usage)

State machine helps you eliminate `switch` and/or `if/else` statements in your code to determine available actions in given state.

Let's use a naive example from a Laravel's Blade view template and underlying Eloquent `Order` model:

```
@foreach($orders as $order)
    {{ $order->reference }} status: {{ $order->status }}

    @if($order->status === 'new')
        start processing

    @elseif($order->status === 'awaiting_payment')
        record payment

    @elseif($order->status === 'awaiting_shipment')
        save tracking number

    @elseif($order->status === 'in_delivery')
        record delivery
        open claim

    @elseif($order->status === 'complete')
        open claim

    @elseif($order->status === 'processing_claim')
        refund
        close claim
    @endif
@endforeach
```

This quickly gets out of hand, especially when a new status is introduced or the processing order changes.

---

**To streamline it, we can implement state machine for the Order entity:**

1. implement interface on the `Order` model

    ```
    class Order extends Model implements \Sofa\StateMachine\StateMachineInterface
    {
        //...

        public function getCurrentState() : string
        {
            return $this->status;
        }

        public function setState(string $state) : void
        {
            $this->status = $state;
            $this->save();
        }
    }
    ```
2. define available transitions and prepare data for the template:

    ```
    $transitions = [
        Transition::make(/*from_state*/ 'new', /*action*/ 'start processing', /*to_state*/ 'awaiting_payment'),
        Transition::make('awaiting_payment', 'record payment', 'awaiting_shipment'),
        Transition::make('awaiting_shipment', 'save tracking number', 'in_delivery'),
        Transition::make('in_delivery', 'record delivery', 'complete'),
        Transition::make('in_delivery', 'open claim', 'processing_claim'),
        Transition::make('complete', 'open claim', 'processing_claim'),
        Transition::make('processing_claim', 'close claim', 'complete'),
        Transition::make('processing_claim', 'refund', 'refunded'),
    ];

    foreach ($orders as $order) {
        $order_state = new \Sofa\StateMachine\Fsm($order, $transitions);

        $order->available_actions = $order_state->getAvailableActions();
    }
    ```
3. and we end up with controller &amp; template code decoupled from the Process logic &amp; order:

    ```
    @foreach($orders as $order)
        {{ $order->reference }} status: {{ $order->status }}

        @foreach($order->available_actions as $action)
            {{ $action }}
        @endforeach
    @endforeach
    ```
4. finally let's process the actions

    ```
    // controller handling the action
    public function handleAction($order_id, Request $request)
    {
        $order_state = new \Sofa\StateMachine\Fsm(Order::find($order_id), $transitions);

        $this->validate($request, [
            'action' => Rule::in($order_state->getAvailableActions()),
            // ...
        ]);
        $order_state->process($request->get('action'));

        return Redirect::to('some/place');
    }
    ```

With this setup we no **longer have to change our controllers or views, whenever business requirements change**. Instead we add a new transition to the state machine definition.

---

#### I need more control during transition - how to?

[](#i-need-more-control-during-transition---how-to)

The above example assumes very simple transition process, ie. `$order->status = $new_status`. This can be enough sometimes, but often we will need more flexibility during transitions. To address this need you can customize your `Transition` definitions, so they turn from simple **POPO** into `callable` that will be invoked, when state machine processes appropriate **action**:

```
class Refund extends \Sofa\StateMachine\Transition
{
    public function __invoke(StateMachineInterface $order, $payload)
    {
        // $payload is any object you pass to the process method:
        // $order_state->process('refund', $anything_you_need_here);
        $order->refunded_at = $payload['time'];
        $order->refunded_by = $payload['user_id'];

        $order->setState($this->to_state);
    }
}

// Then our transitions definition would like something like:
$transitions = [
    // ...
    Transition::make('processing_claim', 'close claim', 'complete'),
    Refund::make('processing_claim', 'refund', 'refunded'),
];
```

---

Happy Coding!

#### Contribution

[](#contribution)

All contributions are welcome. Make your PR PSR-2 compliant and tested.

###  Health Score

37

—

LowBetter than 83% of packages

Maintenance37

Infrequent updates — may be unmaintained

Popularity38

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 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.

###  Release Activity

Cadence

Every ~573 days

Total

5

Last Release

568d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/34d383bf50d6c73fc747d89a5efacd41ccecc9695aec04148a7c04fc00ef26e7?d=identicon)[jarektkaczyk](/maintainers/jarektkaczyk)

---

Top Contributors

[![jarektkaczyk](https://avatars.githubusercontent.com/u/6928818?v=4)](https://github.com/jarektkaczyk "jarektkaczyk (10 commits)")

---

Tags

statestatemachinefsmfinite-state machine

###  Code Quality

TestsPHPUnit

### Embed Badge

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

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

###  Alternatives

[symfony/workflow

Provides tools for managing a workflow or finite state machine

62942.3M170](/packages/symfony-workflow)[yohang/finite

A simple PHP Finite State Machine

1.3k3.5M10](/packages/yohang-finite)[winzou/state-machine

A very lightweight yet powerful PHP state machine

52113.7M18](/packages/winzou-state-machine)[eftec/statemachineone

A state Machine library for business processes

1144.0k](/packages/eftec-statemachineone)[sebdesign/laravel-state-machine

Winzou State Machine service provider for Laravel

3401.3M1](/packages/sebdesign-laravel-state-machine)[iben12/laravel-statable

Statable trait for Laravel Eloquent models

96299.8k1](/packages/iben12-laravel-statable)

PHPackages © 2026

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