定制 byjg/statemachine 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

byjg/statemachine

Composer 安装命令:

composer require byjg/statemachine

包简介

Finite State Machine implementation with conditional transitions and state actions

README 文档

README

sidebar_key statemachine
tags
php

State Machine

This component implements a Finite State Machine, which can define several states and group them in a collection of transitions (from one state to another state). In addition, each state can have a conditional allowing move to another state.

Sponsor Build Status Opensource ByJG GitHub source GitHub license GitHub release

Differently from other State machines, this implementation doesn't have an initial or final state.

Documentation

Basic Example

Let's use the following example.

flowchart LR
    A[State A] --> B[State B]
    A --> C[State C]
    B -- Some Event --> D[State D]
Loading

We have the states A, B, C, and D, and it's their possible transitions.

First, we create the states:

$stA = new State("A");
$stB = new State("B");
$stC = new State("C");
$stD = new State("D");

Then, we define the transitions. Each transition can optionally have a condition that implements TransitionConditionInterface. The canTransition() method receives the data array and returns true or false to allow or deny the transition.

use ByJG\StateMachine\TransitionConditionInterface;

$transitionA_B = new Transition($stA, $stB);
$transitionA_C = new Transition($stA, $stC);

$condition = new class implements TransitionConditionInterface {
    public function canTransition(?array $data): bool {
        return !is_null($data);
    }
};
$transitionB_D = new Transition($stB, $stD, $condition);

After creating the states and the transition, we can create the State Machine:

$stateMachine = FiniteStateMachine::createMachine()
    ->addTransition($transitionA_B)
    ->addTransition($transitionA_C)
    ->addTransition($transitionB_D);

We can validate the transition using the method canTransition($from, $to). Some examples:

$stateMachine->canTransition($stA, $stB);  // returns true
$stateMachine->canTransition($stA, $stC);  // returns true
$stateMachine->canTransition($stA, $stD);  // returns false
$stateMachine->canTransition($stB, $stA);  // returns false
$stateMachine->canTransition($stB, $stD);  // returns false
$stateMachine->canTransition($stB, $stD, ["some_info"]); // returns true
$stateMachine->canTransition($stC, $stD); //returns false

We can also check if a state is initial or final:

$stateMachine->isInitialState($stA); // returns true
$stateMachine->isInitialState($stB); // returns false
$stateMachine->isFinalState($stA); // returns false
$stateMachine->isFinalState($stC); // returns true
$stateMachine->isFinalState($stD); // returns true

Other ways to create the State Machine

Alternatively, you can create the state machine using the createMachine factory method with arguments as follows:

$condition = new class implements TransitionConditionInterface {
    public function canTransition(?array $data): bool {
        return !is_null($data);
    }
};

$stateMachine = FiniteStateMachine::createMachine(
    [
        ['A', 'B'],
        ['A', 'C'],
        ['B', 'D', $condition]
    ]
);

Using the Auto Transition

Another feature of this component is that depending on the state you are in and the data you pass to the state machine, it can decide what is the next state you can be.

Let's analyze the following states.

flowchart LR
    .[Initial State] -- qty == 0 --> A[Out of stock]
    . -- 0 < qty < min_stock --> B[Last Units]
    . -- qty >= min_stock --> C[In Stock]
Loading

The transition is only possible if some conditions are satisfied. So, let's create the state, the possible transitions and its conditions.

use ByJG\StateMachine\TransitionConditionInterface;

// States:
$stInitial = new State("__VOID__");
$stInStock = new State("IN_STOCK");
$stLastUnits = new State("LAST_UNITS");
$stOutOfStock = new State("OUT_OF_STOCK");

// Transition conditions:
$inStockCondition = new class implements TransitionConditionInterface {
    public function canTransition(?array $data): bool {
        return $data["qty"] >= $data["min_stock"];
    }
};

$lastUnitsCondition = new class implements TransitionConditionInterface {
    public function canTransition(?array $data): bool {
        return $data["qty"] > 0 && $data["qty"] < $data["min_stock"];
    }
};

$outOfStockCondition = new class implements TransitionConditionInterface {
    public function canTransition(?array $data): bool {
        return $data["qty"] == 0;
    }
};

// Transitions:
$transitionInStock = Transition::create($stInitial, $stInStock, $inStockCondition);
$transitionLastUnits = Transition::create($stInitial, $stLastUnits, $lastUnitsCondition);
$transitionOutOfStock = Transition::create($stInitial, $stOutOfStock, $outOfStockCondition);

// Create the Machine:
$stateMachine = FiniteStateMachine::createMachine()
    ->addTransition($transitionInStock)
    ->addTransition($transitionLastUnits)
    ->addTransition($transitionOutOfStock);

The method autoTransitionFrom will check if is possible to do the transition with the actual data and to what state.

$stateMachine->autoTransitionFrom($stInitial, ["qty" => 10, "min_stock" => 20]); // returns LAST_UNITS
$stateMachine->autoTransitionFrom($stInitial, ["qty" => 30, "min_stock" => 20]); // returns IN_STOCK
$stateMachine->autoTransitionFrom($stInitial, ["qty" => 0, "min_stock" => 20]); // returns OUT_OF_STOCK

When auto transitioned, the state object returned has the ->getData() method with the data used to validate it.

Processing State with Actions

You can also create a state with an action that will execute when the state is reached.

e.g.

use ByJG\StateMachine\StateActionInterface;

$action = new class implements StateActionInterface {
    public function execute(?array $data): void {
        // Execute some operation with the data
        // This is the STATE action, not the transition condition
        echo "Processing state with: " . json_encode($data);
    }
};

$stN = new State('SOMESTATE', $action);

// After autoTransition returns the $stN state object
// You can execute its action:

$resultState = $stateMachine->autoTransitionFrom('STATE', [... data ...]);
$resultState->process(); // This will run the state's action with the data

Note: The TransitionConditionInterface validates transitions (returns true/false to allow/deny). The StateActionInterface executes actions when the state is processed.

Other Methods

Create multiple transitions

$condition = new class implements TransitionConditionInterface {
    public function canTransition(?array $data): bool {
        return isset($data["approved"]) && $data["approved"] === true;
    }
};

$transitions = Transition::createMultiple([$from1, $from2], $to, $condition);

$machine = FiniteStateMachine::createMachine()
    ->addTransitions($transitions);

Get possible states from a specific state

$stateMachine->possibleTransitions($stA);

Get the State object

// Return null if doesn't exist, otherwise return the object State
$state = $stateMachine->state('OUT_OF_STOCK');

Install

composer require "byjg/statemachine"

Dependencies

flowchart TD
    byjg/statemachine
Loading

Open source ByJG

byjg/statemachine 适用场景与选型建议

byjg/statemachine 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 9.83k 次下载、GitHub Stars 达 2, 最近一次更新时间为 2022 年 10 月 12 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 byjg/statemachine 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 byjg/statemachine 我们能提供哪些服务?
定制开发 / 二次开发

基于 byjg/statemachine 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 9.83k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 25
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 2
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2022-10-12