咨询/流畅状态机

1.0.2 2023-01-19 21:20 UTC

This package is auto-updated.

Last update: 2024-09-20 01:24:02 UTC


README

这是一个简单的流畅状态机实现。如果你曾经在你的PHP项目中试图控制并报告对象的状态——状态机将帮助你。

已经有几个值得注意的PHP状态机库——但它们并不完全适合,尽管我们以前在项目中使用过它们。这不是一个详尽的列表。

安装

composer require konsulting/state-machine

简单示例

我们可以很容易地构建一个基本的状态机,以门为例

    use Konsulting\StateMachine\StateMachine;

    $door = new StateMachine(['closed', 'open'])
        ->addTransition('open')->from('closed')->to('open')
        ->addTransition('close')->from('open')->to('closed');

在构建状态机时,第一个状态被假定为默认状态。

然后我们可以尝试将状态机转换到新的状态

    $door->transition('open'); // will complete successfully

    $door->transition('close'); // will throw a TransitionFailed Exception

我们还可以检查转换是否可行

    $door->can('open'); // returns true

    $door->can('close'); // returns false

实际使用

在实际使用中,我们将有一个对象(模型),状态机负责控制可应用(因此控制模型行为)的转换

这个库可以通过两种方式实现。

  1. 我们可以将模型附加到状态机上,状态机可以操作模型。在非常简单的情况下,这可能是足够的。

  2. 我们可以将状态机附加到模型上,模型的方法使用状态机来确定它是否能够执行一个操作。

实际使用 1 - 将模型附加到状态机上

正如你所看到的,只有状态机保留状态信息,我们用它来控制脚本中的流程。

    use Konsulting\StateMachine\StateMachine;

    $simpleDoor = new SimpleDoor();

    $sm = new StateMachine(['closed', 'open'])
        ->setModel($state)
        ->addTransition('open')->from('closed')->to('open')
        ->addTransition('close')->from('open')->to('closed');

    $sm->transition('open');     // outputs opening
    echo $sm->getCurrentState(); // outputs open
    $sm->transition('close');    // outputs closing
    echo $sm->getCurrentState(); // outputs closed
    class SimpleDoor
    {
        public function open()
        {
            echo "opening";
        }

        public function close()
        {
            echo "closing";
        }
    }

侧注:此示例使用自动连接来使用与转换同名的模型方法(大驼峰式)。我们还可以通过传递字符串或任何其他 可调用 来定义特定方法。

实际使用 2 - 将状态机附加到模型

为此,我们扩展了AttachableStateMachine,该状态机已设置好以允许我们以编程方式定义状态机,并接受一个模型作为其构造函数。

最终结果是,我们以我们想要的方式使用模型。

    $door = new Door('closed');

    $door->close(); // throws TransitionFailed Exception.

    $door->open();  // outputs "I am opening"
    $door->close(); // outputs "I am closing"

在门类的方 法中,我们传递一个回调作为转换的一部分执行。我们还可以传递一个回调,如果转换失败则执行(而不是抛出异常)。

class Door
{
    public    $state;
    protected $stateMachine;

    public function __construct($state)
    {
        $this->state = $state;
        $this->stateMachine = new AttachedStateMachine($this);
    }

    public function open()
    {
        $this->stateMachine->transition('open', function () {
            echo "I am opening";
        });
    }

    public function close()
    {
        $this->stateMachine->transition('close', function () {
             echo "I am closing";
        });
    }
}

AttachedStateMachine在构造时定义自己。它从模型中获取当前状态,并在设置当前状态时确保将其标记回。

我们还停止了自动连接,这样状态机就不会在无限循环中尝试调用其调用方法。

use Konsulting\StateMachine\AttachableStateMachine;
use Konsulting\StateMachine\StateMachine;
use Konsulting\StateMachine\TransitionFactory;
use Konsulting\StateMachine\Transitions;

class AttachedStateMachine extends AttachableStateMachine
{
    protected function define()
    {
        $transitionFactory = (new TransitionFactory)->useDefaultCall(false);
        $transitions = new Transitions($transitionFactory);

        $this->setTransitions($transitions)
            ->setStates(['closed', 'open'])
            ->setCurrentState($this->model->state ?? 'closed')
            ->addTransition('open')->from('closed')->to('open')
            ->addTransition('close')->from('open')->to('closed');
    }

    public function setCurrentState($state)
    {
        if ($this->model) {
            $this->model->state = $state;
        }

        return parent::setCurrentState($state);
    }
}

贡献

欢迎贡献,并将得到充分认可。我们将通过Pull Request接受贡献。

  • 使用PSR-2编码标准
  • 添加测试,如果你不确定如何,请提问。
  • 记录行为的变化,包括readme.md。

测试

我们使用 PHPUnit

使用PHPUnit运行测试: vendor/bin/phpunit