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

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

star/state-machine
==================

PHP implementation of a configurable State Machine, Workflow, transitions for your domain models

4.0.0(6mo ago)211.6k2[2 issues](https://github.com/yvoyer/php-state/issues)[1 PRs](https://github.com/yvoyer/php-state/pulls)MITPHPPHP &gt;=8.2CI passing

Since Apr 8Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/yvoyer/php-state)[ Packagist](https://packagist.org/packages/star/state-machine)[ RSS](/packages/star-state-machine/feed)WikiDiscussions master Synced 3d ago

READMEChangelog (7)Dependencies (10)Versions (19)Used By (0)

PHP State machine
=================

[](#php-state-machine)

[![Build Status](https://github.com/yvoyer/php-state/actions/workflows/php.yml/badge.svg)](https://github.com/yvoyer/php-state/actions/workflows/php.yml/badge.svg)

This package help you build a workflow for a designated context, that can be encapsulated inside the given context.

It was designed to avoid having a hard dependency to the package. The library do not require you to implement any method. All the code you need to write can be encapsulated inside your context class, and it stays hidden from your other object.

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

[](#installation)

`composer require star/state-machine`

Features
--------

[](#features)

States
------

[](#states)

A state is just a name in which a context can find itself in. The state is usually kept in a persistence platform, or kept in the model using a [string](https://github.com/yvoyer/php-state/blob/master/examples/ContextUsingBuilderTest.php)representation or a [StateMetadata](https://github.com/yvoyer/php-state/blob/master/examples/ContextUsingCustomMetadataTest.php) class.

Transitions
-----------

[](#transitions)

A transition is an action on the context that will move from one state to the other. A transition can only have one destination state, since there is no way for the machine to know which state to go to. On the other hand, a transition may have multiple starting states.

If no transition contains the context's current state as a start start, an exception will be raised (unless another [TransitionCallback](https://github.com/yvoyer/php-state/blob/master/src/Callbacks/) is given).

Attributes
----------

[](#attributes)

Attributes are used to mark a state as having a meaning to the context.

Ie. Given you need a state to be considered active or closed while another state should not, you just need to add the `is_active` and `is_closed` attributes to the states that needs them.

```
// your context using the builder
public function isActive()
{
    return $this->stateMachine()-hasAttribute("is_active");
}
```

```
// your context using the metadata
public function isActive()
{
    return $this->state->hasAttribute("is_active");
}
```

Examples of usage
-----------------

[](#examples-of-usage)

Given you have a `Post` context that can have the following states:

- **Draft**: The post is visible only to the creator and moderators
- **Published**: The post is visible to all users
- **Archived**: The post is visible only to the creator

The post's allowed workflow should be as follow:

TransitionsdraftpublishedarchiveddraftN/ApublishN/ApublishedN/AN/AarchivearchivedN/AunarchiveN/AYou `Post` class can be defined as one of the following pattern.

### Using the builder in your model

[](#using-the-builder-in-your-model)

```
class Post
{
    /**
     * @var string
     */
    private $state;

    public function publish()
    {
        $this->state = $this->stateMachine()->transit("publish", $this);
    }

    public function archive()
    {
        $this->state = $this->stateMachine()->transit("archive", $this);
    }

    public function unarchive()
    {
        $this->state = $this->stateMachine()->transit("unarchive", $this);
    }

    public function isClosed()
    {
        return $this->stateMachine()->hasAttribute("is_closed");
    }

    /**
     * @return StateMachine
     */
    private function stateMachine()
    {
        return StateBuilder::build()
            ->allowTransition("publish", "draft", "published")
            ->allowTransition("archive", "published", "archived")
            ->allowTransition("unarchive", "published", "draft")
            ->addAttribute("is_closed", ["archived", "drafted"])
            ->create($this->state);
    }
}
```

### Wrap the workflow in a class

[](#wrap-the-workflow-in-a-class)

If you have multiple models that can have the same workflow, defining a class that wraps the workflow can be done using the [StateMetadata](https://github.com/yvoyer/php-state/blob/master/src/StateMetadata.php).

```
final class MyStateWorkflow extends StateMetadata
{
    protected function __construct()
    {
        parent::__construct('pending');
    }

    protected function createMachine(StateBuilder $builder)
    {
        $builder->allowTransition("publish", "draft", "published")
        $builder->allowTransition("archive", "published", "archived")
        $builder->allowTransition("unarchive", "published", "draft")
        $builder->addAttribute("is_closed", ["archived", "drafted"])
    }
}

class Post
{
    /**
     * @var string
     */
    private $state;

    public function __construct()
    {
        $this->>state = new MyStateWorkflow();
    }

    public function publish()
    {
        $this->state = $this->state->transit("publish", $this);
    }

    public function archive()
    {
        $this->state = $this->state->transit("archive", $this);
    }

    public function unarchive()
    {
        $this->state = $this->state->transit("unarchive", $this);
    }

    public function isClosed()
    {
        return $this->state->hasAttribute("is_closed");
    }
}
```

Persistence of state
--------------------

[](#persistence-of-state)

The package supports the following persistence engine:

- [Doctrine](https://github.com/doctrine/doctrine2): Can be used using `@Embeddable`, see [Example of usage](https://github.com/yvoyer/php-state/blob/master/examples/DoctrineMappedContextTest.php).

Events
------

[](#events)

The state machine has an internal event handling systems.

Multiple events are triggered at different places, which enables you to hook into the system to add behavior on certain transitions.

Subscribers that listens to these events will have their configured callback(s) called for any transitions.

- `StateEventStore::BEFORE_TRANSITION`: The event is performed before any transition on the context. See `TransitionWasRequested`.
- `StateEventStore::AFTER_TRANSITION`: This event is performed after any transition is executed on the context. See `TransitionWasSuccessful`.
- `StateEventStore::FAILURE_TRANSITION`: This event is performed before the transition exception is triggered. See `TransitionWasFailed`.

**Subscribing a listener in the machine**

```
$stateMachine->addListener(
    StateEventStore::BEFORE_TRANSITION,
    function(TransitionWasRequested $event) {
        // do something
    }
);
```

Transition callbacks
--------------------

[](#transition-callbacks)

When requesting a transition, another way to hook in the process is to pass a [TransitionCallback](https://github.com/yvoyer/php-state/blob/master/src/Callbacks/).

Transition callbacks allow to perform an action before, after or when the transition is not allowed. By default, an exception is triggered. see [AlwaysThrowExceptionOnFailure](https://github.com/yvoyer/php-state/blob/master/src/Callbacks/AlwaysThrowExceptionOnFailure).

**Callback on a transition**

```
$this->state->transit("transition", $this, new DoSomethingOnSuccessIfConditionMatches());
```

###  Health Score

49

—

FairBetter than 94% of packages

Maintenance58

Moderate activity, may be stable

Popularity27

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity84

Battle-tested with a long release history

 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 ~264 days

Recently: every ~5 days

Total

13

Last Release

200d ago

Major Versions

1.0.0-rc1 → 2.0.02020-02-13

2.1.0 → 3.0.02022-10-16

3.3.0 → 4.0.02025-12-14

PHP version history (7 changes)1.0.0-betaPHP &gt;=5.5

2.0.0PHP ^7.1

2.1.0PHP ^7.2

3.0.0PHP ^7.4|^8.0

3.1.1PHP &gt;=7.4

3.2.0PHP &gt;=7.4|^8.0

4.0.0PHP &gt;=8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/36116f724f1bd3ed808e54e2b2de6c17209c5c2c1954685fba18958ec8d055ee?d=identicon)[yvoyer](/maintainers/yvoyer)

---

Top Contributors

[![yvoyer](https://avatars.githubusercontent.com/u/1745744?v=4)](https://github.com/yvoyer "yvoyer (38 commits)")

---

Tags

phpstatestate-machinestate-managementstate-patternstatustransitionworkflow

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[friendsofphp/php-cs-fixer

A tool to automatically fix PHP code style

13.5k251.2M25.2k](/packages/friendsofphp-php-cs-fixer)[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.5k5.9M736](/packages/sylius-sylius)[matomo/matomo

Matomo is the leading Free/Libre open analytics platform

21.7k38.9k](/packages/matomo-matomo)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.4M203](/packages/sulu-sulu)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

751291.4k43](/packages/civicrm-civicrm-core)[akeneo/pim-community-dev

Akeneo PIM, the future of catalog management is open!

1.0k624.1k86](/packages/akeneo-pim-community-dev)

PHPackages © 2026

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