uuf6429/state-engine
Composer 安装命令:
composer require uuf6429/state-engine
包简介
A library providing interfaces and basic implementation of a State Engine or Machine
README 文档
README
This library provides some interfaces and a basic implementation of a State Engine or State Machine.
✨ Highlights:
- Dual functionality:
- Either as a basic state engine; switching to a desired state as long the transition is defined (see "JiraIssueTest")
- Or a more sophisticated state machine; same as above but matching data for any state (see "TurnstileTest")
- Highly composable - everything can be replaced as desired
- PSR-14 (Event Dispatcher) compatible
- Fluent builder interface (see "From Scratch")
- Generates Mermaid or PlantUML markup (see "Examples & Testing")
🔌 Installation
The recommended and easiest way to install this library is through Composer:
composer require "uuf6429/state-engine-php"
🧐 Why?
In principle such an engine is easy to implement, but in practice it is typically implemented badly or forgotten.
For instance, one might have an is_active field thinking there will not be other states and then later on an
is_pending field is needed, at which point refactoring flags to state is too late.
In any case, this library abstracts away that situation or at least decreases the amount of code.
🤔 How?
There are a few key parts to how this works:
- State - an object representing a single state of a model. So models may have different state levels, for example a door can have open and closed states, but it can also be locked and unlocked. In such a case, either consider the door lock as a separate model (with a separate engine instance) or merge all the states: open, closed-unlocked and closed-locked.
- Transition - an object representing a transition from one state to another. This is how you define the various state flows that your model can go through.
- TransitionRepository - an object that is aware of and provides all possible allowed transitions.
- Engine - an object that performs the transition of a model from one state to another. Usually you would have an engine instance for each stateful model in your application.
🚀 Usage
You have the possibility to use it from scratch or plug it into your existing. There are basically three parts to it:
- configuring the engine (creating states and transitions)
- using the engine (eg, in a web controller or service)
- (optionally) handling events (with the same event dispatcher provided to the engine)
A slightly different situation would be when you need to provide a list of valid transitions, for example to the user.
In this case, having the
StateTraversion
trait on the repository would be useful.
From Scratch
Here's a quick & dirty example with the provided implementation (that assumes that there is a "door" model):
use App\Models\Door; // example model that implements StateAwareInterface use uuf6429\StateEngine\Implementation\Builder; use uuf6429\StateEngine\Implementation\Entities\State; $doorStateManager = Builder::create() ->defState('open', 'Open') ->defState('closed', 'Closed') ->defState('locked', 'Locked') ->defTransition('open', 'closed', 'Close the door') ->defTransition('closed', 'locked', 'Lock the door') ->defTransition('locked', 'closed', 'Unlock the door') ->defTransition('closed', 'open', 'Open the door') ->getEngine(); // you can pass an event dispatcher to the engine here // find Door 123 (laravel-style repository-model) $door = Door::find(123); // close the door :) $doorStateManager->changeState($door, new State('closed'));
From Scratch (Custom)
You don't like how the Engine works? Or you feel that State could have more details?
Then you're in luck! With the whole library based on interfaces, you can easily replace parts of the implementation.
For example, you could store states or transitions in a database, in which case you can have your own
TransitionRepository that accesses the database.
Existing Code
The library provides some flexibility so that you can connect your existing code with it. In more complicated scenarios, you may have to build a small layer to bridge the gap. The example below illustrates how one can handle models with flags instead of a single state.
use App\Models\Door; // example model use uuf6429\StateEngine\Implementation\Builder; use uuf6429\StateEngine\Implementation\Entities\State; $door = Door::find(123); $doorStateMutator = Builder::makeStateMutator( // define how we get the state static function () use ($door): State { if ($door->is_locked) { return new State('locked'); } return $door->is_open ? new State('open') : new State('closed'); }, // define how we set the state static function (State $newState) use ($door): void { $door->update([ 'is_locked' => $newState->getName() === 'locked', 'is_open' => $newState->getName() === 'open', ]); } ); // assumes engine $doorStateManager was already defined $doorStateManager->changeState($doorStateMutator, new State('closed'));
😎 Examples & Testing
You can find some examples in this readme as well as the tests, some of which are explained below.
JiraIssueTest State Engine
This test provides a realistic example of how Jira Issue states could be set up.
The test also generates the Mermaid diagram below, thanks to the Mermaidable trait:
stateDiagram
s1_backlog: Backlog
s2_analysis: Analysis
s3_in_dev: In Dev
s4_ready_for_dev: Ready for Dev
s5_ready_for_qa: Ready for QA
s6_ready_for_release: Ready for Release
s7_in_qa: In QA
s8_resolved: Resolved
s1_backlog --> s2_analysis: Begin analysis
s1_backlog --> s3_in_dev: Fast-track for development
s2_analysis --> s4_ready_for_dev: Analysis complete
s2_analysis --> s1_backlog: Return to backlog
s4_ready_for_dev --> s2_analysis: Need more details
s4_ready_for_dev --> s3_in_dev: Begin development
s3_in_dev --> s5_ready_for_qa: Send to QA
s3_in_dev --> s6_ready_for_release: Fast-track for release
s3_in_dev --> s4_ready_for_dev: Stop development
s5_ready_for_qa --> s7_in_qa: Begin testing
s7_in_qa --> s4_ready_for_dev: QA Failed
s7_in_qa --> s6_ready_for_release: QA Passed
s6_ready_for_release --> s8_resolved: Released
s8_resolved --> s1_backlog: Reopen
Loading
TurnstileTest State Machine
This test illustrates how a state machine can be used to model a turnstile gate. As before, here's the generated diagram:
stateDiagram
s1_locked: Impassable
s2_open: Passable
s1_locked --> s2_open: Coin placed
s2_open --> s1_locked: Person walks through
Loading
Here's how the state machine definition looks like and how it could be used:
use App\Models\Turnstile; // example model that implements StateAwareInterface use uuf6429\StateEngine\Implementation\Builder; $turnstileStateMachine = Builder::create() // make states ->defState('locked', 'Impassable') ->defState('open', 'Passable') // make transitions ->defDataTransition('locked', ['insert_coin'], 'open', 'Coin placed') ->defDataTransition('open', ['walk_through'], 'locked', 'Person walks through') ->getMachine(); $turnstile = Turnstile::find(123); // put coin in turnstile (notice that the final state is not mentioned) $turnstileStateMachine->processInput($turnstile, ['insert_coin']); // now $turnstile will be in "open" state
uuf6429/state-engine 适用场景与选型建议
uuf6429/state-engine 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 17 次下载、GitHub Stars 达 1, 最近一次更新时间为 2021 年 07 月 21 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「state」 「workflow」 「engine」 「machine」 「state-machine」 「uuf6429」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 uuf6429/state-engine 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 uuf6429/state-engine 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 uuf6429/state-engine 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Workflow for NetCommons Plugin
Doctrine implementation of the MetaborStd (Statemachine) for PHP 8.2+
A simple state dropdown field for SilverStripe forms
A SilverStripe module to optimise the Meta, crawling, indexing, and sharing of your website content (forked from Cyber-Duck/Silverstripe-SEO)
Workflow logger
A state Machine library for business processes
统计信息
- 总下载量: 17
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 1
- 点击次数: 3
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2021-07-21