chippyash / finite-state-machine
有限状态机实现
Requires
- php: >=7.2
- chippyash/assembly-builder: ^2.1
- chippyash/monad: ~2.1
- graphp/graphviz: ~0.2
- myclabs/php-enum: ^1.7
- psr/event-dispatcher: ^1.0
Requires (Dev)
- ext-dom: *
- chippyash/testdox-converter: ^1.0
- mikey179/vfsstream: ~1.6
- phpunit/phpunit: ~6.5
Suggests
- ext-dom: To build StateGraphs from an XML Definition file
This package is auto-updated.
Last update: 2024-09-08 09:32:21 UTC
README
chippyash/finite-state-machine
质量保证
上面的徽章代表当前的开发分支。通常,除非测试、覆盖率和使用性符合标准,否则我不会推送到GitHub。在休假期间,需要为其他下游项目编写代码等情况除外。如果您需要稳定代码,请使用标记版本。
是什么
提供了一种感知事件的状态机(FSM)实现
为什么
状态机允许我们将对象状态变化的逻辑与对象本身分离出来。这也意味着我们可以将状态配置在DIC或某些其他外部存储中,因此可以轻松地对其进行调整以适应特定情况,而不会破坏对象本身。
状态机是实现工作流解决方案的完美工具。
如何
从概念上讲,状态机是一种形式有向图:一组节点(状态),通过只有单向的边(转换)连接。在图论中,有向图不一定有起点和终点。
我们可以使用有限状态机(FSM)来实现这个理论图。另一个术语是‘确定性有限自动机’。FSM始终有一个起点或起始状态
,但可能没有终点状态,即状态可以不断变化。一个对象根据其可用的转换和任何附加条件从一种状态转换到另一种状态。转换由一个事件
引发。
被状态机控制的物体或对象一次只能处于一个状态。这与多状态机或非确定性有限自动机非常不同,虽然它们在计算模式中有其用途,但并不用于典型的人机工作流程(人们很难同时做两件事!)。然而,一个对象可以参与两个或更多并行活动是完全可能的,例如,如果对象有一个“通信”状态,那么作为并行活动,它可以通过电子邮件和短信进行通信。当两个活动都完成时,它可以从“通信”状态转换到其他状态。
我在“由状态机控制的物体”这个术语上使用了一个精确的意义。在设计工作流系统时,常见的错误是将状态变化与相关对象绑定在一起。这不可避免地会导致将有关如何改变状态的东西污染到对象本身。这是错误的,并导致与对象的根本责任无关的复杂代码,例如,在问题跟踪系统中表示问题;在装配线上表示汽车等。
相反,最好让对象实现一个接口,允许它与外部有限状态机(FSM)进行通信。这将从对象中抽象出状态转换逻辑,让对象专注于自己的任务,由FSM负责为对象转换状态。从轻量级的角度来看,对象只需要知道它当前处于什么状态。它实际上不必关心它将去哪里,直到它到达那里。相反,FSM不关心对象是什么,只关心它是否被要求更改状态。如果对象真的想了解它可以选择哪些状态,它会询问FSM它的选项。这符合单一职责原则,这是SOLID原则之一。
此实现提供以下内容
- 状态图。状态和这些状态之间可能存在的转换的有向图。这利用了graphp/graphviz库。
- 可事件化的状态图。这支持PSR-14 事件调度器接口。这使得从一种状态到另一种状态的转换可以由外部力量引起。它还允许您创建事件监听器,这些监听器可以阻止转换并执行转换后的操作。
- 可以穿越状态的客户端对象。这是通过状态感知接口实现的。您需要将此接口实现到您希望状态图管理的对象中
开发中
请参阅测试合约
安装
composer require chippyash/finite-state-machine
创建状态图
use Chippyash\StateMachine\StateGraph; //add states and transitions $graph = new StateGraph('nameOfGraph'); $graph->addState(new State('state1')) ->addState(new State('state2')) ->addState(new State('state3')) ->addTransition(new State('state1'), new State('state2'), new Transition('pending')) ->addTransition(new State('state2'), new State('state3'), new Transition('complete'));
尝试添加重复的状态或转换名称将引发异常。
注意 - 您必须至少有一个初始状态,即没有传入转换的状态。您不需要最终状态,但在大多数应用程序中都会有。最终状态是没有传出转换的状态。
您可以使用getInitialStates(): array
检索图中的初始状态。确定使用哪个初始状态取决于您的客户端。对于大多数应用程序,只有一个初始状态。
您可以使用isValid(): bool
验证图。这将检查您没有不属于转换的状态,并且至少有一个初始状态。
您可以使用getGraph(): Graph
检索复合图对象的副本。这允许您在库范围之外的其他操作中使用底层图。此类操作之一可能是创建图的视觉表示。请参阅graphp/graphviz。这个库建立在Graphiz之上,所以您已经可以访问其功能。此外,您可以通过__call()方法将方法调用代理到底层图对象
创建可以穿越状态的对象
任何类都可以用于状态机。只需在您的类中实现状态感知接口即可。您可以使用HasState特性行此操作。
状态机不负责将状态保存到存储中。
这是对象的责任。
转换
调用$graph->transition()
方法。
$object = new MyStateAwareObject(); $transition = new Transition('t1'); try { $graph->transition($object, $transition); } catch (StateMachineException $e) { //process the error - see `Exceptions` directory }
如果转换不存在于图中或对象当前状态不存在等,则transition()
将抛出异常。
事件驱动的状态转换
StateMachine\Events\EventableStateGraph
类扩展了StateGraph
,以提供将状态图连接到PSR-14兼容事件管理器的方法。
use Chippyash\StateMachine\Events\EventableStateGraph; $graph = new EventableStateGraph('stategraphname'); $graph->setEventDispatcher($EventDispatcherInterfaceCompatible);
将EventableStateGraph::eventListener(StateGraphEventable $event)
方法注册为监听与`StateGraphEventable`兼容事件的监听器,事件类型为`StateGraphEventType::DO_TRANSITION()`。
《eventListener()`方法将为StateGraphEvent
类型的StateGraphEventType::START_TRANSITION()
和StateGraphEventType::END_TRANSITION()
事件类型触发事件。您可以注册这些事件的监听器。对于START_TRANSITION事件类型,如果您想停止事件的发生,可以将接收的事件的stopPropagation
属性设置为true。这允许您设置预条件等。
END_TRANSITION事件对于存储对象的新状态或记录更改等非常有用。
所有与StateGraphEventable
兼容的事件都会在事件消息类中获得转换和被操作的对象。
您可以在https://github.com/the-matrix/the-coffee-machine找到事件驱动状态机的示例代码。
构建状态图
XML
use Chippyash\StateMachine\Builder\XmlBuilder; use Chippyash\StateMachine\Exceptions\InvalidStateMachineFileException; try { $stateGraph = (new XmlBuilder())->build('path/to/graph.xml'); } catch (InvalidStateMachineFileException $e) { //process errors }
构建方法接受第二个参数。将其设置为true以验证源XML输入。
用于验证的XSD位于src/StateMachine/Builder/statemachine.xsd
。
示例XML文件位于docs/stategraph-example.xml
。
更改库
- fork它
- 编写测试
- 修改它
- 发起pull request
发现了一个您无法解决的bug吗?
- fork它
- 编写测试
- 发起pull request
注意:在发起pull request之前,请确保您已经rebase到HEAD
或者 - 提交一个issue ticket。
对于生产
将此库用于生产所需的任何特定说明
路线图
- 状态图持久化
- XML - 完成
- Json
- Yaml
许可
此软件库在BSD 3 Clause许可下发布
此软件库版权所有(c)2018-2019,Ashley Kitson,英国
历史
V0...预发布
V1.0.0初始发布