PHP state machine framework

2019-01-30 19:59发布

I doubt that is there any state machine framework like https://github.com/pluginaweek/state_machine for PHP.

I've had to define many if-else logical clauses, and I would like something to help make it more fun by just defining:

  1. Condition required to transition
  2. State after transition

Then this can be reused to check if conditions match or not, for example

$customer->transition('platinum');

I expect this line of code to implicitly check if the customer can transition or not. Or explicitly check by:

$customer->canTransitTo('platinum');

Thanks in advance, noomz

7条回答
劳资没心,怎么记你
2楼-- · 2019-01-30 20:31

For the record, I write (yet another) state machine

https://github.com/EFTEC/StateMachineOne

However, it's based in automation rather of event (however it is possible to use a field as event). The transition of state is triggered by values, manually or by code.

For example, the parking example:

where pedal, turnkey, gas, speed and brake are fields defined and controlled by code.

$smachine->addTransition(PARKED,IDLING
    ,'when pedal = 1 and turnkey = 1 and gas > 0');
$smachine->addTransition(IDLING,DRIVING
    ,'when gas > 0 and speed > 0');
$smachine->addTransition(DRIVING,IDLING
    ,'when brake = 1 and speed = 0');
$smachine->addTransition(IDLING,PARKED
    ,'when turnkey = 0 and speed = 0');

So, it is even possible to program advanced features such as what if the car runs out of gas or even modify values.

Features:

  • It stores the information into the database (optional).
  • It has a UI (for testing) see image

state machine

I also published an article about it

https://medium.com/cook-php/creating-a-state-machine-using-php-ddef9395430e

查看更多
Fickle 薄情
3楼-- · 2019-01-30 20:39
We Are One
4楼-- · 2019-01-30 20:42

I've been working on a simple PHP finite state machine library, kind of similar to the rails state_machine. The code is here: https://github.com/chriswoodford/techne/tree/v0.1

A car example, similar to the selected answer (above) would look something like this:

Initialization

  $machine = new StateMachine\FiniteStateMachine();
  $machine->addEvent('start', array('parked' => 'idling'));
  $machine->addEvent('drive', array('idling' => 'driving'));
  $machine->addEvent('stop', array('driving' => 'idling'));
  $machine->addEvent('park', array('idling' => 'parked'));
  $machine->setInitialState('parked');

Usage

  $machine->start();
  echo $machine->getCurrentStatus();
  // prints "idling"     

  $machine->drive();
  echo $machine->getCurrentStatus();
  // prints "driving"

  $machine->stop();
  echo $machine->getCurrentStatus();
  // prints "idling"

  $machine->park();
  echo $machine->getCurrentStatus();
  // prints "parked"

It lacks an explicitly defined interface that since it uses the __call() magic method to accept messages, but that could easily be resolved with an adapter.

查看更多
霸刀☆藐视天下
5楼-- · 2019-01-30 20:44

I don't know a framework like this (which doesn't mean it does not exist). But while not as feature packed as the linked framework, the State pattern is rather simple to implement. Consider this naive implementation below:

interface EngineState
{
    public function startEngine();
    public function moveForward();
}

class EngineTurnedOffState implements EngineState
{
    public function startEngine()
    {
        echo "Started Engine\n";
        return new EngineTurnedOnState;
    }
    public function moveForward()
    {
        throw new LogicException('Have to start engine first');
    }
}

class EngineTurnedOnState implements EngineState
{
    public function startEngine()
    {
        throw new LogicException('Engine already started');
    }
    public function moveForward()
    {
        echo "Moved Car forward";
        return $this;
    }
}

After you defined the states, you just have to apply them to your main object:

class Car implements EngineState
{
    protected $state;
    public function __construct()
    {
        $this->state = new EngineTurnedOffState;
    }
    public function startEngine()
    {
        $this->state = $this->state->startEngine();
    }
    public function moveForward()
    {
        $this->state = $this->state->moveForward();
    }
}

And then you can do

$car = new Car;
try {
    $car->moveForward(); // throws Exception
} catch(LogicException $e) {
    echo $e->getMessage();
}

$car = new Car;
$car->startEngine();
$car->moveForward();

For reducing overly large if/else statements, this should be sufficient. Note that returning a new state instance on each transition is somewhat inefficient. Like I said, it's a naive implementation to illustrate the point.

查看更多
虎瘦雄心在
6楼-- · 2019-01-30 20:47

I've written a state machine for php. I'm sure you've figured out a solution long ago for this. But for people visiting this page, you are welcome to try out this state machine.

https://github.com/definitely246/state-machine

To use it, you define transition event handlers as classes. Then you need to define transitions. The finite state machine can be configured to do other stuff, but here are the basics.

class Event1ChangedState1ToState2
{
    public function allow($context)
    {
        return true;
    }

    public function handle($context)
    {
        if (!$context->statesChanged) $context->statesChanged = 0;
        print "state1 -> state2\n";
        return $context->statesChanged++;
    }
}

class Event1ChangedState2ToState1
{
    public function allow($context)
    {
        return true;
    }

    public function handle($context)
    {
        print "state2 -> state1\n";
        return $context->statesChanged++;
    }
}

You can then define transitions that change states when an event is triggered.

$transitions = [
   [ 'event' => 'event1', 'from' => 'state1', 'to' => 'state2', 'start' => true],
   [ 'event' => 'event1', 'from' => 'state2', 'to' => 'state1' ],
];

$fsm = new StateMachine\FSM($transitions);

print $fsm->state() . PHP_EOL; // 'state1'

$fsm->event1(); // returns 1, prints 'state1 -> state2'

print $fsm->state() . PHP_EOL; // 'state2'

$fsm->event1(); // returns 2, prints 'state2 -> state1'

print $fsm->state() . PHP_EOL; // 'state1'

You can install with composer

composer require definitely246/state-machine
查看更多
爷的心禁止访问
7楼-- · 2019-01-30 20:49

I've created Smalldb framework to implement model layer of a web application using state machines. It is designed to work directly on SQL database, where each row of the SQL table represents an instance of a state machine (so the state machines are persistent).

It has role-based access control, so you can specify in state machine definition which transitions are allowed to which users (it also can deal with owners of the state machine instance or components of more complex entities).

To make development faster Smalldb can load state diagrams in GraphML created with yEd editor, so you can draw a state chart and then directly use it in your application as well as in your documentation. Also there is a native editor (jQuery widget + desktop wrapper) under development. For debugging purposes and online in-app documentation the Smalldb can generate state diagrams using Graphviz.

查看更多
登录 后发表回答