noem / state-machine-loader
从各种数据源(数组、JSON、YAML)构建可用的状态机实例
Requires
- justinrainbow/json-schema: ^5.2
- nette/schema: ^1.2
- noem/state-machine-interface: dev-master
- psr/container: ^1.0 || ^2.0
- symfony/yaml: ^5.3
Requires (Dev)
- mockery/mockery: ^1.4
- noem/composer-file-embed: dev-master
- noem/state-machine: dev-master
- phpunit/phpunit: ^9.1
- squizlabs/php_codesniffer: ^3.6
This package is auto-updated.
Last update: 2024-09-04 23:28:25 UTC
README
从各种来源创建状态机实例。
安装
使用 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!';
由于 action
和 guard
回调利用 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