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

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

philiprehberger/php-state-machine
=================================

Declarative state machine with guards, hooks, and transition history

v1.2.0(2mo ago)138MITPHPPHP ^8.2CI passing

Since Mar 15Pushed 1mo agoCompare

[ Source](https://github.com/philiprehberger/php-state-machine)[ Packagist](https://packagist.org/packages/philiprehberger/php-state-machine)[ Docs](https://github.com/philiprehberger/php-state-machine)[ GitHub Sponsors](https://github.com/philiprehberger)[ RSS](/packages/philiprehberger-php-state-machine/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (6)Versions (10)Used By (0)

PHP State Machine
=================

[](#php-state-machine)

[![Tests](https://github.com/philiprehberger/php-state-machine/actions/workflows/tests.yml/badge.svg)](https://github.com/philiprehberger/php-state-machine/actions/workflows/tests.yml)[![Latest Version on Packagist](https://camo.githubusercontent.com/3d70b0be479326e10783f9300a7c38f45e1052014397c6fe9392358a9be39e2c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7068696c69707265686265726765722f7068702d73746174652d6d616368696e652e737667)](https://packagist.org/packages/philiprehberger/php-state-machine)[![Last updated](https://camo.githubusercontent.com/816120cc210a444c6108766f4fe0a9fe29e479cdcdf40a0dfe4093376fc5c864/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6173742d636f6d6d69742f7068696c69707265686265726765722f7068702d73746174652d6d616368696e65)](https://github.com/philiprehberger/php-state-machine/commits/main)

Declarative state machine with guards, hooks, and transition history.

Requirements
------------

[](#requirements)

- PHP 8.2+

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

[](#installation)

```
composer require philiprehberger/php-state-machine
```

Usage
-----

[](#usage)

### Define a state machine

[](#define-a-state-machine)

```
use PhilipRehberger\StateMachine\StateMachine;

$sm = StateMachine::define()
    ->states(['pending', 'processing', 'shipped', 'delivered', 'cancelled'])
    ->initial('pending')
    ->stateProperty('state')
    ->transition('process', 'pending', 'processing')
    ->transition('ship', 'processing', 'shipped')
    ->transition('deliver', 'shipped', 'delivered')
    ->transition('cancel', ['pending', 'processing'], 'cancelled')
    ->build();
```

### Apply transitions

[](#apply-transitions)

```
$order = new Order(); // $order->state === 'pending'

$result = $sm->apply($order, 'process');
// $order->state === 'processing'
// $result->from === 'pending'
// $result->to === 'processing'
```

### Pass a payload to transitions

[](#pass-a-payload-to-transitions)

```
$sm = StateMachine::define()
    ->states(['pending', 'approved', 'rejected'])
    ->initial('pending')
    ->transition('approve', 'pending', 'approved')
        ->guard(fn (object $order, array $payload) => ($payload['role'] ?? '') === 'manager')
        ->before(fn (object $order, array $payload) => $order->log[] = 'Approved by '.$payload['user'])
    ->transition('reject', 'pending', 'rejected')
    ->build();

$sm->apply($order, 'approve', ['role' => 'manager', 'user' => 'Alice']);
```

The `$payload` array is passed through to all guards, before hooks, and after hooks. It defaults to `[]` when omitted, so existing guards and hooks that accept only one parameter continue to work.

### Check if a transition is allowed

[](#check-if-a-transition-is-allowed)

```
$sm->can($order, 'ship');    // true
$sm->can($order, 'deliver'); // false
```

### Get available transitions

[](#get-available-transitions)

```
$sm->allowedTransitions($order); // ['ship', 'cancel']
$sm->availableTransitions($order); // ['ship', 'cancel'] (alias)

// Payload is forwarded to guards when checking availability:
$sm->availableTransitions($order, ['role' => 'manager']);
```

### Guards

[](#guards)

Guards are callables that must return `true` for the transition to proceed:

```
$sm = StateMachine::define()
    ->states(['pending', 'processing', 'shipped'])
    ->initial('pending')
    ->transition('process', 'pending', 'processing')
        ->guard(fn (object $order, array $payload) => $order->isPaid)
    ->transition('ship', 'processing', 'shipped')
    ->build();
```

### Before and after hooks

[](#before-and-after-hooks)

```
$sm = StateMachine::define()
    ->states(['pending', 'processing'])
    ->initial('pending')
    ->transition('process', 'pending', 'processing')
        ->before(fn (object $order, array $payload) => $order->log[] = 'Processing started')
        ->after(fn (object $order, array $payload) => $order->log[] = 'Processing complete')
    ->build();
```

### State entry/exit hooks

[](#state-entryexit-hooks)

```
$sm = StateMachine::define()
    ->states(['draft', 'review', 'published'])
    ->initial('draft')
    ->onEnter('review', fn (object $entity, string $transition) => $entity->log[] = "Entered review via $transition")
    ->onExit('draft', fn (object $entity, string $transition) => $entity->log[] = "Left draft via $transition")
    ->transition('submit', 'draft', 'review')
    ->transition('approve', 'review', 'published')
    ->build();
```

### Rollback the last transition

[](#rollback-the-last-transition)

```
$sm->apply($order, 'process');
$sm->rollback($order);
// $order->state === 'pending'
```

### Mermaid diagram export

[](#mermaid-diagram-export)

```
echo $sm->toMermaid();
// stateDiagram-v2
//     [*] --> pending
//     pending --> processing : process
//     processing --> shipped : ship
//     shipped --> delivered : deliver
//     pending --> cancelled : cancel
//     processing --> cancelled : cancel
```

### Transition history

[](#transition-history)

```
$sm->apply($order, 'process');
$sm->apply($order, 'ship');

$history = $sm->history();
$history->all();  // [TransitionResult, TransitionResult]
$history->last(); // TransitionResult { transition: 'ship', from: 'processing', to: 'shipped' }
```

API
---

[](#api)

MethodDescription`StateMachine::define()`Create a new `StateMachineBuilder``$sm->apply(object $entity, string $transition, array $payload = [])`Apply a transition, returns `TransitionResult``$sm->can(object $entity, string $transition, array $payload = [])`Check if a transition is allowed`$sm->allowedTransitions(object $entity, array $payload = [])`Get names of all allowed transitions`$sm->availableTransitions(object $entity, array $payload = [])`Alias for `allowedTransitions()``$sm->currentState(object $entity)`Get the entity's current state`$sm->rollback(object $entity)`Revert the most recent transition`$sm->toMermaid()`Generate a Mermaid state diagram string`$sm->history()`Get the `TransitionHistory` instance`$sm->initialState()`Get the defined initial state`$sm->states()`Get all defined states### StateMachineBuilder

[](#statemachinebuilder)

MethodDescription`->states(array $states)`Define valid states`->initial(string $state)`Set the initial state`->stateProperty(string $property)`Set the entity property name (default: `'state'`)`->onEnter(string $state, callable $hook)`Register a hook that fires when entering a state`->onExit(string $state, callable $hook)`Register a hook that fires when leaving a state`->transition(string $name, string|array $from, string $to)`Define a transition`->build()`Build the `StateMachine`### TransitionBuilder

[](#transitionbuilder)

MethodDescription`->guard(callable $guard)`Add a guard `(object $entity, array $payload): bool``->before(callable $hook)`Add a before-transition hook `(object $entity, array $payload): void``->after(callable $hook)`Add an after-transition hook `(object $entity, array $payload): void`Development
-----------

[](#development)

```
composer install
vendor/bin/phpunit
vendor/bin/pint --test
```

Support
-------

[](#support)

If you find this project useful:

⭐ [Star the repo](https://github.com/philiprehberger/php-state-machine)

🐛 [Report issues](https://github.com/philiprehberger/php-state-machine/issues?q=is%3Aissue+is%3Aopen+label%3Abug)

💡 [Suggest features](https://github.com/philiprehberger/php-state-machine/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)

❤️ [Sponsor development](https://github.com/sponsors/philiprehberger)

🌐 [All Open Source Projects](https://philiprehberger.com/open-source-packages)

💻 [GitHub Profile](https://github.com/philiprehberger)

🔗 [LinkedIn Profile](https://www.linkedin.com/in/philiprehberger)

License
-------

[](#license)

[MIT](LICENSE)

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance88

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity52

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

Total

9

Last Release

89d ago

### Community

Maintainers

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

---

Top Contributors

[![philiprehberger](https://avatars.githubusercontent.com/u/8218077?v=4)](https://github.com/philiprehberger "philiprehberger (14 commits)")

---

Tags

workflowhooksfsmstate-machinetransitionsguards

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[pwm/s-flow

A lightweight library for defining state machines

742.5k](/packages/pwm-s-flow)[shrink0r/workflux

Finite state machine for php.

375.6k1](/packages/shrink0r-workflux)[tarfin-labs/event-machine

Event-driven state machines for Laravel with event sourcing, type-safe context, and full audit trail.

199.1k](/packages/tarfin-labs-event-machine)[piece/stagehand-fsm

A finite state machine

3666.2k3](/packages/piece-stagehand-fsm)[ringierimu/state-workflow

Laravel State Workflow provide tools for defining and managing workflows and activities with ease.

3264.7k](/packages/ringierimu-state-workflow)[phpmentors/stagehand-fsm

A finite state machine

361.1k](/packages/phpmentors-stagehand-fsm)

PHPackages © 2026

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