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

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

james.rus52/state-machine
=========================

A very lightweight yet powerful PHP state machine

0.6(5y ago)017MITPHPPHP ^7.1.3|^8.0

Since May 26Pushed 5y agoCompare

[ Source](https://github.com/jamesRUS52/state-machine)[ Packagist](https://packagist.org/packages/james.rus52/state-machine)[ Docs](https://github.com/winzou/StateMachine)[ RSS](/packages/jamesrus52-state-machine/feed)WikiDiscussions master Synced 6d ago

READMEChangelogDependencies (5)Versions (15)Used By (0)

A very lightweight yet powerful PHP state machine
=================================================

[](#a-very-lightweight-yet-powerful-php-state-machine)

Define your states, define your transitions and your callbacks: we do the rest. The era of hard-coded states is over!

[![Build Status](https://camo.githubusercontent.com/7d09d14b92a51933fb0c27dec22e478aa60d6c9aa93b45bbdac84cf2f3280ebe/68747470733a2f2f7472617669732d63692e6f72672f77696e7a6f752f73746174652d6d616368696e652e7376673f6272616e63683d6d6173746572)](https://travis-ci.org/winzou/state-machine)

Installation (via composer)
---------------------------

[](#installation-via-composer)

```
{
    "require": {
        "james.rus52/state-machine": "~0.5"
    }
}
```

Usage
-----

[](#usage)

### Configure a state machine graph

[](#configure-a-state-machine-graph)

In order to use the state machine, you first need to define a graph. A graph is a definition of states, transitions and optionnally callbacks ; all attached on an object from your domain. Multiple graphes can be attached to the same object.

Let's define a graph called *myGraphA* for our `DomainObject` object:

```
$config = [
                  'graph'  => 'Request',
                  'property_path' => 'Status',
                  'states' => [
                      RequestStatus::NEW    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE,
                              RequestAction::DELETE
                          ],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ],
                              RequestAction::DELETE => ['b_name' => 's_delete_request' ,'roles' => ['author'], 'onclick' => 'ConfirmDeleteRequest();' ]
                          ]
                      ],
                      RequestStatus::ANALYZE    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE,
                              RequestAction::ESCALATE,
                              RequestAction::DELETE,
                              RequestAction::SUSPEND,
                              RequestAction::UNSUSPEND,
                              ],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ],
                              RequestAction::ESCALATE => ['b_name' => 's_escalation', 'onclick' => 'ShowModalWindow(\'owner_model_window\',\'SubmitForm\',\'owner_model_window_form\');' ],
                              RequestAction::DELETE => ['b_name' => 's_delete_request' ,'roles' => ['superadmin'], 'onclick' => 'ConfirmDeleteRequest();' ],
                              RequestAction::SUSPEND => ['b_name' => 's_suspend' ],
                              RequestAction::UNSUSPEND => ['b_name' => 's_unsuspend'],
                          ],
                          'conditions' => [
                              RequestAction::SUSPEND => ['object', 'isSuspended', false ],
                              RequestAction::UNSUSPEND => ['object', 'isSuspended', true ],
                          ]
                      ],
                      RequestStatus::APPROVE    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE,
                              RequestAction::ESCALATE,
                              RequestAction::DELETE,
                              RequestAction::SUSPEND,
                              RequestAction::UNSUSPEND,
                              ],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ],
                              RequestAction::ESCALATE => ['b_name' => 's_escalation', 'onclick' => 'ShowModalWindow(\'owner_model_window\',\'SubmitForm\',\'owner_model_window_form\');' ],
                              RequestAction::DELETE => ['b_name' => 's_delete_request' ,'roles' => ['superadmin'], 'onclick' => 'ConfirmDeleteRequest();' ],
                              RequestAction::SUSPEND => ['b_name' => 's_suspend' ],
                              RequestAction::UNSUSPEND => ['b_name' => 's_unsuspend'],
                          ],
                          'conditions' => [
                              RequestAction::SUSPEND => ['object', 'isSuspended', false ],
                              RequestAction::UNSUSPEND => ['object', 'isSuspended', true ],
                          ]
                      ],
                      RequestStatus::APPROVED    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE,
                              RequestAction::ESCALATE,
                              RequestAction::DELETE,
                              RequestAction::SUSPEND,
                              RequestAction::UNSUSPEND,
                              ],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ],
                              RequestAction::ESCALATE => ['b_name' => 's_escalation', 'onclick' => 'ShowModalWindow(\'owner_model_window\',\'SubmitForm\',\'owner_model_window_form\');' ],
                              RequestAction::DELETE => ['b_name' => 's_delete_request' ,'roles' => ['superadmin'], 'onclick' => 'ConfirmDeleteRequest();' ],
                              RequestAction::SUSPEND => ['b_name' => 's_suspend' ],
                              RequestAction::UNSUSPEND => ['b_name' => 's_unsuspend'],
                          ],
                          'conditions' => [
                              RequestAction::SUSPEND => ['object', 'isSuspended', false ],
                              RequestAction::UNSUSPEND => ['object', 'isSuspended', true ],
                          ]
                      ],
                      RequestStatus::SENT    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ]
                          ]
                      ],
                      RequestStatus::DELIVERED    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE,
                              RequestAction::ESCALATE,
                              RequestAction::SUSPEND,
                              RequestAction::UNSUSPEND,
                          ],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ],
                              RequestAction::ESCALATE => ['b_name' => 's_escalation', 'onclick' => 'ShowModalWindow(\'owner_model_window\',\'SubmitForm\',\'owner_model_window_form\');' ],
                              RequestAction::SUSPEND => ['b_name' => 's_suspend' ],
                              RequestAction::UNSUSPEND => ['b_name' => 's_unsuspend'],
                          ],
                          'conditions' => [
                              RequestAction::SUSPEND => ['object', 'isSuspended', false ],
                              RequestAction::UNSUSPEND => ['object', 'isSuspended', true ],
                          ]
                      ],
                      RequestStatus::COMPLETED    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ]
                          ]
                      ],
                      RequestStatus::CANCELED    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ]
                          ]
                      ],
                      RequestStatus::REJECTED    => [
                          'actions' => [
                              RequestAction::COMMENT,
                              RequestAction::CLONE],
                          'properties' => [
                              RequestAction::COMMENT => ['b_name' => 's_comment' ],
                              RequestAction::CLONE => ['b_name' => 's_clone_request' ]
                          ]
                      ],
                  ],
                  'transitions' => [
                      RequestTransition::CANCEL  => [
                          'from' => [RequestStatus::NEW],
                          'to' => RequestStatus::CANCELED,
                          'properties' => ['b_name' => 's_to_cancel', 'css_class' => 'btn-outline-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::TO_ANALYZE => [
                          'from' => [RequestStatus::NEW],
                          'to' => RequestStatus::ANALYZE,
                          'properties' => ['b_name' => 's_to_analyze', 'css_class' => 'btn-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::REJECT  => [
                          'from' => [RequestStatus::ANALYZE],
                          'to' => RequestStatus::REJECTED,
                          'properties' => ['b_name' => 's_to_reject_admin', 'css_class' => 'btn-outline-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::BACK_TO_AUTHOR  => [
                          'from' => [RequestStatus::ANALYZE],
                          'to' => RequestStatus::NEW,
                          'properties' => ['b_name' => 's_return_to_author', 'css_class' => 'btn-outline-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::ANALYZE  => [
                          'from' => [RequestStatus::ANALYZE],
                          'to' => RequestStatus::APPROVE,
                          'properties' => ['b_name' => 's_aprove_my_resources', 'css_class' => 'btn-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::RETURN  => [
                          'from' => [RequestStatus::APPROVE],
                          'to' => RequestStatus::ANALYZE,
                          'properties' => ['b_name' => 's_to_returnanalyze', 'css_class' => 'btn-outline-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::REJECT  => [
                          'from' => [RequestStatus::APPROVE, RequestStatus::ANALYZE ],
                          'to' => RequestStatus::REJECTED,
                          'properties' => ['b_name' => 's_to_reject', 'css_class' => 'btn-outline-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::APPROVE  => [
                          'from' => [RequestStatus::APPROVE],
                          'to' => RequestStatus::APPROVED,
                          'properties' => ['b_name' => 's_to_approve', 'css_class' => 'btn-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::SEND  => [
                          'from' => [RequestStatus::APPROVED],
                          'to' => RequestStatus::SENT,
                          'properties' => ['b_name' => 's_to_partner', 'css_class' => 'btn-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::DELIVER  => [
                          'from' => [RequestStatus::SENT],
                          'to' => RequestStatus::DELIVERED,
                          'properties' => ['roles' => ['system']]],
                      RequestTransition::RESEND => [
                          'from' => [RequestStatus::DELIVERED],
                          'to' => RequestStatus::SENT,
                          'properties' => ['b_name' => 's_resend', 'css_class' => 'btn-primary', 'roles' => ['executor','superadmin']]
                      ],
                      RequestTransition::COMPLETE  => [
                          'from' => [RequestStatus::DELIVERED],
                          'to' => RequestStatus::COMPLETED, 'properties' => ['roles' => ['system']]
                      ],
                      RequestTransition::REOPEN => [
                          'from' => [RequestStatus::COMPLETED, RequestStatus::REJECTED, RequestStatus::CANCELED],
                          'to' => RequestStatus::SENT,
                          'properties' => ['b_name' => 's_reopen', 'css_class' => 'btn-primary', 'roles' => ['author', 'executor','superadmin']]
                      ],
                  ],
                  'callbacks' => [
                      'lock' => [
                          [
                              'do'   => ['object','getLock'],
                          ],
                      ],
                      'unlock' => [
                          [
                              'do'   => ['object','releaseLock'],
                          ],
                      ],
                      'before' => [
                          [
                              'on' => RequestTransition::BACK_TO_AUTHOR,
                              'do'   => ['object','BackToAuthor'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::CANCEL,
                              'do'   => ['object','Cancel'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::TO_ANALYZE,
                              'do'   => ['object','ToAnalyze'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::ANALYZE,
                              'do'   => ['object','ToApprove'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::RETURN,
                              'do'   => ['object','BackToAnalyze'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::REJECT,
                              'from' => RequestStatus::ANALYZE,
                              'do'   => ['object','ToRejectByAdmin'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::REJECT,
                              'from' => RequestStatus::APPROVE,
                              'do'   => ['object','ToReject'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::APPROVE,
                              'do'   => ['object','ToComplete'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::SEND,
                              'do'   => ['object','ToSendPartner'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::RESEND,
                              'do'   => ['object','ToSendPartner'],
                              'args' => [$this->params]
                          ],
                          [
                              'on' => RequestTransition::REOPEN,
                              'do'   => ['object','Reopen'],
                              'args' => [$this->params]
                          ],
                      ],
                      'action' => [
                          [
                              'action' => RequestAction::COMMENT,
                              'do'   => ['object','AddComment'],
                              'args' => [$this->params['ta_comment'] ?? null]
                          ],
                          [
                              'action' => RequestAction::CLONE,
                              'do'   => ['object','CloneRequest'],
                          ],
                          [
                              'action' => RequestAction::ESCALATE,
                              'on' => [RequestStatus::ANALYZE, RequestStatus::APPROVE, RequestStatus::APPROVED, RequestStatus::DELIVERED],
                              'do'   => ['object','Escalate'],
                              'args' => [$this->params['s_escalation_owner'] ?? null]
                          ],
                          [
                              'action' => RequestAction::DELETE,
                              'on' => [RequestStatus::NEW, RequestStatus::ANALYZE, RequestStatus::APPROVE, RequestStatus::APPROVED],
                              'do'   => ['object','DeleteRequest'],
                          ],
                          [
                              'action' => RequestAction::SUSPEND,
                              'do'   => ['object','Suspend'],
                              'args' => [$this->params['ta_comment'] ?? null]
                          ],
                          [
                              'action' => RequestAction::UNSUSPEND,
                              'do'   => ['object','Unsuspend'],
                              'args' => [$this->params['ta_comment'] ?? null]
                          ],
                      ],
                      'guard' => [
                          [
                              'on' => RequestTransition::RESEND,
                              'do' =>  ['object','hasResourcesError'],
                          ]
                      ]
                  ]
              ];
```

So, in the previous example, the graph has 6 possible states, and those can be achieved by applying some transitions to the object. For example, when creating a new `DomainObject`, you would apply the 'create' transition to the object, and after that the state of it would become *pending*.

### Using the state machine

[](#using-the-state-machine)

#### Definitions

[](#definitions)

The state machine is the object actually manipulating your object. By using the state machine you can test if a transition can be applied, actually apply a transition, retrieve the current state, etc. *A state machine is specific to a couple object + graph.* It means that if you want to manipulate another object, or the same object with another graph, *you need another state machine*.

The factory helps you to get the state machine for these couples object + graph. You give an object and a graph name to it, and it will return you the state machine for this couple. If you want to have this factory as a service in your Symfony2 application, please see the [corresponding StateMachineBundle](https://github.com/winzou/StateMachineBundle).

#### Usage

[](#usage-1)

Please refer to the several examples in the `examples` folder.

#### Callbacks

[](#callbacks)

Callbacks are used to guard transitions or execute some code before or after applying transitions.

Guarding callbacks must return a `bool`. If a guard returns `false`, a transition cannot be performed.

##### Credits

[](#credits)

This library has been highly inspired by , but has taken another direction.

##### James' version of SM

[](#james-version-of-sm)

Cloned from [https://github.com/sebdesign/state-machine](sebdesign/state-machine)

I've added some new futures

1. Properties - user defined info, that you can use via

- getTransitionProperties
- getStateProperties
- hasTransitionProperties
- hasStateProperties

2. lock/unlock - You may implement locking your object, while someone do transition on document that you try to Transit too
3. Action - State can has some actions that don't doing transition and locking document too.
4. Conditions - This is a condition for state action, like Guard for transitions

###  Health Score

29

—

LowBetter than 59% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity6

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity66

Established project with proven stability

 Bus Factor1

Top contributor holds 60.9% 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 ~185 days

Recently: every ~262 days

Total

14

Last Release

1968d ago

PHP version history (3 changes)0.1PHP &gt;=5.3.0

0.4.0PHP ^7.1.3

0.6PHP ^7.1.3|^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/a64ca777106023ca0997b82cf8bce53932d67b442b16154d859bbd15fe9f2171?d=identicon)[jamesRUS52](/maintainers/jamesRUS52)

---

Top Contributors

[![winzou](https://avatars.githubusercontent.com/u/702928?v=4)](https://github.com/winzou "winzou (28 commits)")[![jamesRUS52](https://avatars.githubusercontent.com/u/39707635?v=4)](https://github.com/jamesRUS52 "jamesRUS52 (4 commits)")[![sebdesign](https://avatars.githubusercontent.com/u/667144?v=4)](https://github.com/sebdesign "sebdesign (4 commits)")[![cawolf](https://avatars.githubusercontent.com/u/1932623?v=4)](https://github.com/cawolf "cawolf (1 commits)")[![cordoval](https://avatars.githubusercontent.com/u/328359?v=4)](https://github.com/cordoval "cordoval (1 commits)")[![dantleech](https://avatars.githubusercontent.com/u/530801?v=4)](https://github.com/dantleech "dantleech (1 commits)")[![bendavies](https://avatars.githubusercontent.com/u/625392?v=4)](https://github.com/bendavies "bendavies (1 commits)")[![jgiacobbi](https://avatars.githubusercontent.com/u/2099012?v=4)](https://github.com/jgiacobbi "jgiacobbi (1 commits)")[![patrick-mcdougle](https://avatars.githubusercontent.com/u/1392496?v=4)](https://github.com/patrick-mcdougle "patrick-mcdougle (1 commits)")[![Richtermeister](https://avatars.githubusercontent.com/u/624921?v=4)](https://github.com/Richtermeister "Richtermeister (1 commits)")[![blazarecki](https://avatars.githubusercontent.com/u/1443312?v=4)](https://github.com/blazarecki "blazarecki (1 commits)")[![snoob](https://avatars.githubusercontent.com/u/1806237?v=4)](https://github.com/snoob "snoob (1 commits)")[![bpolaszek](https://avatars.githubusercontent.com/u/5569077?v=4)](https://github.com/bpolaszek "bpolaszek (1 commits)")

---

Tags

eventstatestatemachinecallback

###  Code Quality

TestsPHPUnit

### Embed Badge

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

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

###  Alternatives

[winzou/state-machine

A very lightweight yet powerful PHP state machine

52113.7M18](/packages/winzou-state-machine)[symfony/workflow

Provides tools for managing a workflow or finite state machine

62842.3M170](/packages/symfony-workflow)[sebdesign/laravel-state-machine

Winzou State Machine service provider for Laravel

3401.3M1](/packages/sebdesign-laravel-state-machine)[yohang/finite

A simple PHP Finite State Machine

1.3k3.5M10](/packages/yohang-finite)[iben12/laravel-statable

Statable trait for Laravel Eloquent models

96299.8k1](/packages/iben12-laravel-statable)[gomachan46/state-machine

simple state machine with annotations for PHP, inspired by AASM known as a Ruby state machine.

1893.9k](/packages/gomachan46-state-machine)

PHPackages © 2026

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