Skip to content
Potfur edited this page May 12, 2015 · 4 revisions

StateMachine

Scrutinizer Code Quality Code Coverage Build Status License

StateMachine is an implementation of non-deterministic finite automata (this can be also subsumed under state pattern implementation). In different words - machine will move context from one state to another, depending on result of executed commands. Can be used to describe order & payment processing, newsletter opt-in process, customer registration - anything not trivial.

Pros:

  • improves decoupling (also decoupling from framework)
  • state specific behaviour is described by simple commands and not as monolithic classes,
  • process and states can be easily modified,
  • commands can be shared between states, process and context

Cons:

  • scatters behaviour to other objects that are not necessary connected to context,
  • can lead to code duplication,
  • context can end in undefined (eg. removed) state

How does it work

Even if state machine can be called in fancy academic names and stuff - it's quite simple concept.

Lets say that there is some kind of business process, like customer registration. When customer creates his account - he has state registered, but to fully activate his account he need to click on activation link in e-mail that was sent. When he does that - his state changes to activated.

This simple can be easily represented as Process class. Both registered and activated states will be State classes. To change state from registered to activated an instance of Event must be created, e.g. activate.

Since state machine comes with array adapter, this can be represented as array below. That /* commands */ represents commands|Commands (anything callable) that will be executed when activate event is triggered.

[
    'name' => 'testSchema',
    'subjectClass' => '\stdClass',
    'initialState' => 'new',
    'states' => [
        [
            'name' => 'registered',
            'events' => [
                [
                    'name' => 'activate',
                    'targetState' => 'activated',
                    'commands' => [ /* commands */ ]
                ]
            ],
        ],
        [
            'name' => 'activated',
            'events' => [],
        ]
    ]
]

This will be enough to render process as PNG or SVG using Renderer (requires Graphviz).

But not enough to execute run. To work, state machine requires also tree handlers to read/store Customer entity, write timeouts and lock processed entities.

Full potential

State machine events can be triggered manually - every time user does something, program will trigger some event and move context to next state. But to use the full potential of state machine special Event should be used, like onStateWasSet and onTimeout. onStateWasSet is an event triggered every time context enters new state. onTimeout requires some kind of cron job, that periodically checks for expired timeouts and triggers them.

Clone this wiki locally