m1lt0n/in_great_state

易于使用的状态机

v1.0.0 2017-07-26 13:16 UTC

This package is not auto-updated.

Last update: 2024-09-20 20:26:21 UTC


README

一个易于使用且灵活的状态机。

InGreatState 是一个简单且非常可扩展的状态机。它不偏不倚,可以与 ORM、Web 框架或独立脚本结合使用。

示例

假设你有一个问题跟踪器。问题可以处于几种状态之一(例如:打开、不会修复、已解决、重新打开等)。当从一个状态转换到另一个状态时,我们想做一些事情(例如:向问题的所有者发送电子邮件等)。通过使用状态机可以轻松组织这样的操作。上述示例的一个例子可以是

<?php

// The minimum requirements of a StateFul interface is to implement the
// currentState method (that returns the object's current state) and the
// setState method (that updates the state of the object).

class Issue implements InGreatState\StatefulInterface
{
    private $state;

    public function __construct()
    {
        $this->setState('open');
    }

    public function currentState()
    {
        return $this->state;
    }

    public function setState($state)
    {
        $this->state = $state;
        echo "Setting state to {$state}" . PHP_EOL;
    }
}

// The minimum requirements for a state machine that extends the InGreatState
// state machine is to implement the method states (which returns an array of
// all the states an object can be) and registerTransitions (which adds all
// the allowed state transitions and the actions they trigger).

class IssueStateMachine extends InGreatState\StateMachine
{
    public function states()
    {
        return ['open', 'wontfix', 'resolved', 'reopened'];
    }

    public function registerTransitions()
    {
        $this->addTransition()->from('open')->to('resolved')->actions(function ($owner) {
            echo "Resolving transition from {$owner->currentState()} to resolved" . PHP_EOL;
        });

        $this->addTransition()->from('open')->to('wontfix')->actions(function ($owner) {
            echo "Resolving transition from {$owner->currentState()} to wontfix" . PHP_EOL;
        });
    }
}

// Now, we can use the state machine :-)

$p = new Issue();
$sm = new IssueStateMachine($p);
$sm->transitionTo('resolved');

此代码将问题状态设置为 '已解决',并输出以下短语

  • 从打开到已解决的状态转换(由 open->resolved 的转换触发)
  • 设置状态为已解决(由 Issue 的 setState 触发)

过渡 API 流畅,允许方法链式调用。如果您想从任何初始状态添加过渡,只需指定最终状态和操作即可

<?php

$this->addTransition()->to('wontfix')->actions(function ($owner) {
    echo "Resolving transition to wontfix" . PHP_EOL;
});

上述代码将匹配从任何初始状态到 wontfix 的任何过渡。请注意,所有匹配的过渡操作都将被触发(即找到匹配的过渡后,执行不会停止,因此您可以从 open->closed 等添加多个过渡注册)。

一个重要的注意事项是,在您的闭包中抛出异常被认为是一个好的做法,以停止过渡的执行并避免不一致的数据悄悄进入您的应用程序。另外,如果您在过渡期间(以及您的闭包和/或您的 setState 方法中)在数据库中持久化数据,那么将过渡包装在数据库事务中是一个好主意,以便回滚所有更改并避免不一致的数据结构。

该架构足够灵活,可以适应任何 Web 框架或 ORM 集成,并且很容易添加功能,如状态记录(您可以通过在状态对象的 setState 方法中实现功能将状态转换存储在数据库中或甚至平面文件)。您完全掌控一切!