咨询 / 流畅状态机
Requires
- php: ^7.0 | ^8.0
- danielstjules/stringy: ^3.1
- symfony/event-dispatcher: ^3.3 | ^4.0 | 5.0
Requires (Dev)
- dms/phpunit-arraysubset-asserts: ^0.4.0
- phpunit/phpunit: ^9.0
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 - 将模型附加到状态机上
正如你所看到的,只有状态机保留状态信息,我们用它来控制脚本中的流程。
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