jeroen-g/statinator

新一代 PHP 状态机

0.2 2020-12-18 18:23 UTC

This package is auto-updated.

Last update: 2024-09-19 02:46:56 UTC


README

CI/CD

新一代 PHP 状态机和状态图。

安装

composer require jeroen-g/statinator

使用方法

想象一下,您想为可以处于开启或关闭状态的灯开关创建一个状态机。其图表看起来如下所示

diagram

来源: statecharts.github.io

此状态机的 Statinator 配置看起来如下所示

$config = [
    'states' => [
      'LIGHTS_ON',
      'LIGHTS_OFF',
    ],
    'transitions' => [
        'FLICK_ON' => [
            'from' => ['LIGHTS_OFF'],
            'to' => ['LIGHTS_ON'],
        ],
        'FLICK_OFF' => [
            'from' => ['LIGHTS_ON'],
            'to' => ['LIGHTS_OFF'],
        ],
    ],
];

然后在您的应用程序中,您应该使用此配置实例化 Statinator。

use JeroenG\Statinator\Statinator;
$statinator = new Statinator($config);

Statinator 接受第二个参数,该参数是实现 ActionRepositoryInterface 的存储库,这(可选)存储成功和失败的操作。

您想在状态机中使用的任何对象都应该实现 StatableInterface 接口。对于灯开关的示例,可能是这样的

use JeroenG\Statinator\StatableInterface;

class LightSwitch implements StatableInterface {
    private string $currentState;

    public function __construct(string $initialState)
    {
        $this->currentState = $initialState;
    }

    public function getState(): string
    {
        return $this->currentState;
    }

    public function setState(string $to): void
    {
        $this->currentState = $to;
    }
}

在您的代码的任何地方,您都可以使用 Statinator 实例来获取状态机并与其状态和转换交互。

$lightSwitch = new LightSwitch('LIGHTS_OFF');
$sm = $statinator->get($lightSwitch);

$sm->getState(); // LIGHTS_OFF
$sm->can('FLICK_ON'); // true
$sm->can('FLICK_OFF'); // false

$sm->apply('FLICK_ON');

$sm->getState(); // LIGHTS_ON
$sm->can('FLICK_ON'); // false
$sm->can('FLICK_OFF'); // true

虽然看到对象从一个状态移动到另一个状态相当酷,但真正的力量在于您可以定义的转换操作。

在状态改变之前和之后,状态机执行任何可用的操作。您可以在全局配置中定义操作,或通过在 Statinator 上使用设置器。

// Using the configuration
$config = [
    // ...
    'transitions' => [
        'FLICK_ON' => [
            'from' => ['LIGHTS_OFF'],
            'to' => ['LIGHTS_ON'],
            'on' => [
                'entry' => NotifyLightsAreOn::class,
                'exit' => NotifyLightsAreOff::class,
            ],
        ],
    // ...
    ],
];

// Or using a setter
$statinator->onEntry('FLICK_ON', NotifyLightsAreOn::class);
$statinator->onExit('FLICK_ON', NotifyLightsAreOff::class);

操作可以是 callable 或实现 ActionableInterface 的类。

$statinator->onEntry('FLICK_OFF', fn(StateMachineInterface $stateMachine, string $transition) => var_dump('Called it!'));
use JeroenG\Statinator\ActionableInterface;
use JeroenG\Statinator\StateMachineInterface;

class NotifyLightsAreOn implements ActionableInterface {
    private StateMachineInterface $stateMachine;
    private string $transition;

    public function getState(): string
    {
        return $this->stateMachine->getState();
    }

    public function execute(StateMachineInterface $stateMachine, string $transition): void
    {
        $this->stateMachine = $stateMachine;
        $this->transition = $transition;

        $notifier = new MyNotifier();
        $notifier->send('Lights are turned on');
    }

    public function getTransition(): string
    {
        return $this->transition;
    }
}

如果您的操作实现 ActionableInterface,则可以使用在实例化 Statinator 时传递的存储库进行保存。此软件包附带一个默认使用的 ArrayActionRepository,该存储库不持久化数据。另一种可能性是 LogActionRepository,它需要一个符合 PSR 的记录器,数据将在此持久化。当然,您也可以创建自己的存储库(可能是一个使用数据库的存储库),只要它实现了 ActionRepositoryInterface

贡献

项目包括一个 Makefile,用于运行安装、运行测试和检查代码风格。