noem/state-machine-loader

从各种数据源(数组、JSON、YAML)构建可用的状态机实例

dev-master 2024-02-03 09:53 UTC

This package is auto-updated.

Last update: 2024-09-04 23:28:25 UTC


README

CI

从各种来源创建状态机实例。

安装

使用 composer 安装此包

composer require noem/state-machine-loader

模式

所有输入数据都使用 justinrainbow/json-schema 与 JSON 模式进行验证。原始模式文件可在 src/schema.json 找到。以下是所有相关实体的描述

状态

转换

作为替代的简写,您可以仅定义一个表示目标状态的 string。这将导致一个简单的转换,它不会由任何事件或守卫启用,因此将在状态机被任何事件触发时立即启用。这对于链接转换很有用,例如,当您对一系列 enEntry/onExit 事件比中间状态更感兴趣时。转换的完整定义是一个 object

回调

事件处理程序(守卫、动作、进入/退出处理程序)使用灵活的语法定义

简写方法

在最简单的形式中,这只是一个 string,它被检查 is_callable()(允许您传递 PHP 函数或静态方法的名称)。

my_state:
    action: var_dump

但是,也可以从容器中获取回调

您可以可选地将 PSR-11 ContainerInterface 传递给加载器对象。当回调以 "@" 开头时,它将被使用。例如,如果您定义 onEntry: "@onEnterFoo",则将导致 $callback = $container->get('onEnterFoo')。您可以使用此方法将您的框架的 DI 容器集成到 FSM 的事件处理中。

my_state:
    action: @onPostRequest

扩展语法

简写对于原型设计和/或非常 功能 的代码库很有用,但它们由于缺少参数化机制而难以重用代码。为了获得更大的灵活性,您可以选择将处理程序定义为对象。

工厂

工厂允许您使用您定义的参数配置一个“返回函数的函数”。

my_state:
    action:
        type: factory
        factory: createMyStateAction
        arguments:
            - Hello world
            - false

这假设您的代码中有以下函数

function createMyStateAction(string $greeting, bool $isError){

    return function(object $input) use ($greeting, $echo){
        $input->greeting = $greeting;
        $input->isError
        return $input;
    }

}

此模式允许您重用相同的处理程序并从 YAML 中自定义它们的操作。这对于编写守卫尤其有用:假设您想确保一个状态至少活跃了5秒钟。没有工厂,您几乎不得不创建一个版本的 hasBeenInStateForFiveSeconds 函数。如果您想在应用程序的另一个地方使用相同的逻辑等待10秒钟,您现在就陷入了困境。有了工厂定义,您可以写一个 createTimeoutGuard,它可以返回任何间隔的守卫。

内联

内联处理程序是将您的处理程序直接写入 YAML 的方法,而不是将它们作为函数定义在服务容器中。

my_state:
    onEntry:
        type: inline
        # language=injectablephp
        callback: |
            $context = $machine->context($transition->source());
            $context['greeting'] = 'Hello World!';

由于 actionguard 回调利用 PSR-14 风格的参数检查,因此必须为它们指定一个额外的参数

baz:
    action: 
      # Note the additional 'trigger'
      type: 'inline'
      callback: |
          echo 'Hello ' . $trigger;
      trigger: '\Stringable'

如果只想简单起见,并且不需要在代码库的其他地方重用处理程序,这将很有用。这也是快速原型设计的好方法。由于这旨在用于基本脚本,您只需提供 函数体本身。您可以在内联回调中使用以下变量

类型 onEntry & onExit
  • $state - 处理程序注册的状态对象
  • $from - 正在从该状态过渡出去的状态。(在简单机器中,这可能与 $state 相同,但机器同时处于多种状态是完全可能的)
  • $machine - 状态机对象
类型 action
  • $trigger - 触发动作的对象
  • $state - 处理程序注册的状态对象
  • $machine - 状态机对象
类型 guard
  • $trigger - 触发过渡的对象
  • $transition - 过渡对象
  • $machine - 状态机对象

完整示例

假设所有事件处理器都在服务容器中配置。

off:
    transitions:
        - on # Shorthand used

on:
    parallel: true
    context:
        hello: "world"
    onEntry: '@onBooted'
    children:
        foo:
            action: '@sayMyName'

        bar:
            action: '@sayMyName'
            parallel: true
            children:
                bar_1:
                    action: '@sayMyName'
                    children:
                        bar_1_1:
                            transitions:
                            - target: 'bar_1_2'
                              guard: '@guardBar_1_2' 
                        bar_1_2:
                            transitions:
                            - target: 'bar_1_1'
                              guard: 
                                  type: factory
                                  # Executes a function that creates the actual guard based on the given parameters
                                  factory: '@myGuardFactory'
                                  arguments:
                                      - 'hello'
                                      - 'world'
                bar_2:
                    action: '@sayMyName'

        baz:
            initial: 'substate2' # if not specified, it would use the first child, 'substate1'
            action: 
              - '@sayMyName'
              - type: factory
                factory: '@myActionFactory'
                arguments:
                    - 'lorem'
                    - '%getIpsum%'
              # Write raw PHP directly!
              # Note the additional 'trigger'
              - type: 'inline'
                callback: |
                    echo 'Hello World';
                trigger: '\stdClass'
            children:
                substate1:
                    action: '@sayMyName'

                substate2:
                    action: '@sayMyName'
                    transitions:
                        -   target: 'substate3'
                            guard: 
                            # Multiple guards for one transition are possible. Any of them can allow the transition
                              - '@someOtherGuard'
                              - '@guardSubstate3'
                            # Write raw PHP directly!
                              - type: 'inline'
                                callback: |
                                    return false;
                                trigger: '\stdClass'
                substate3:
                    action: '@sayMyName'

    transitions:
        # If an exception is used as a trigger,
        # it can be used to perform a graceful shutdown
        -   target: error
            guard: Throwable

error:
    onEntry: 
      - '@onException'
      - '@anotherErrorHandler'
    context: '@helloWorldService'
    transitions:
        - off