tmilos/light-fsm

轻量级且简单的有限状态机,具有优秀的API,支持自动工作流程和外部状态,可导出DOT图

1.0.1 2016-12-21 11:49 UTC

This package is not auto-updated.

Last update: 2024-09-14 19:03:55 UTC


README

License Build Status Coverage Status HHVM Status Scrutinizer Code Quality SensioLabsInsight

有限状态机(FSM)PHP库。在PHP代码中直接创建状态机和基于轻量级状态机的流程。

$phoneCall = new StateMachine(State::OFF_HOOK);

$phoneCall->configure(State::OFF_HOOK)
    ->permit(Event::CALL_DIALED, State::RINGING);

$phoneCall->configure(State::RINGING)
    ->permit(Event::HUNG_UP, State::OFF_HOOK)
    ->permit(Event::CALL_CONNECTED, State::CONNECTED);

$phoneCall->configure(State::CONNECTED)
    ->onEntry([$this, 'startTimer'])
    ->onExit([$this, 'stopTimer'])
    ->permit(Event::HUNG_UP, State::OFF_HOOK)
    ->permit(Event::PLACE_ON_HOLD, State::ON_HOLD);

$phoneCall->fire(Event::CALL_DIALED);
$this->assertEquals(State::RINGING, $phoneCall->getCurrentState());

此项目以及上面的示例,受到了stateless的启发。

特性

  • 字符串或整型的状态和触发事件
  • 带附加数据的触发事件
  • 分层状态
  • 状态进入/退出事件
  • 检查
  • 守卫回调以支持条件转换
  • 能够在外部存储状态(例如,在ORM跟踪的属性中)
  • 导出为DOT图

带附加数据的触发事件

事件可以通过StateMachine::fire($event, $data)带附加数据触发,这些数据将被传递并可用于进入/退出和守卫监听器,以便它们可以根据它来建立逻辑。

分层状态

在下面的示例中,ON_HOLD状态是CONNECTED状态的子状态。这意味着ON_HOLD调用仍然连接。

$phoneCall->configure(State::ON_HOLD)
    ->subStateOf(State::CONNECTED)
    ->permit(Event::CALL_CONNECTED, State::CONNECTED);

除了StateMachine::getCurrentState()方法将报告精确的当前状态外,还提供了一个isInState($state)方法。isInState($state)将考虑子状态,所以如果上述示例处于ON_HOLD状态,isInState(State::CONNECTED)也会评估为true

进入/退出事件

在示例中,startTimer()方法将在通话连接时执行。当通话完成时,将执行stopTimer()

当通话在CONNECTEDON_HOLD状态之间移动时,由于ON_HOLD状态是CONNECTED状态的子状态,这些监听器可以区分子状态,并根据第一个$isSubState参数确定通话仍然连接。

外部状态存储

为了持久性目的监听状态变化,例如使用某些ORM工具,将监听器回调传递给StateMachine构造函数。

$stateObject = $orm-find();

$stateMachine = new StateMachine(
    function () use ($stateObject) {
        return $stateObject->getValue();
    },
    function ($state) use ($stateObject) {
        $stateObject->setValue($state);
        $orm->persist($stateObject);
    }
);

在这种情况下,当StateMachine用两个回调构建时,状态完全外部化,并且每次StateMachine需要当前状态时,第一个回调将被调用,每次状态变化时,第二个回调将被调用。

检查

状态机可以通过StateMachine::getPermittedTriggers()方法提供可以在当前状态下成功触发的触发事件的列表。

守卫子句

状态机将根据守卫子句在多个转换之间进行选择,例如。

$phoneCall->configure(State::OFF_HOOK)
    .permit(Trigger::CALL_DIALLED, State::RINGING, function ($data) { return IsValidNumber($data); })
    .permit(Trigger::CALL_DIALLED, State::BEEPING, function ($data) { return !IsValidNumber($data); });

导出为DOT图

在运行时可视化状态机可能很有用。使用这种方法,代码是权威源,状态图是副产品,始终是最新的。

$phoneCall->configure(State::OFF_HOOK)
    .permit(Trigger::CALL_DIALED, State::RINGING, 'IsValidNumber');
$graph = phoneCall->toDotGraph();

StateMachine::toDotGraph()方法返回状态机的DOT图语言字符串表示,例如。

digraph {
 "off-hook" -> "ringing" [label="call-dialed [IsValidNumber]"];
}

这可以通过支持DOT图形语言的工具进行渲染,例如来自graphviz.org的dot命令行工具viz.js。查看(http://www.webgraphviz.com)以获得即时效果。生成PDF文件的命令行示例

> dot -T pdf -o phoneCall.pdf phoneCall.dot