PHP状态机框架

发布于 2024-10-04 12:00:44 字数 458 浏览 0 评论 0原文

我怀疑是否有像 PHP 的 https://github.com/pluginaweek/state_machine 这样的状态机框架。

我必须定义许多 if-else 逻辑子句,并且我希望通过定义以下内容来使其变得更有趣:

  1. 转换状态所需的条件
  2. 转换后

然后可以重用它来检查条件是否匹配,例如

$customer->transition('platinum');

我希望这行代码隐式检查客户是否可以转换。或明确检查:

$customer->canTransitTo('platinum');

提前致谢, 努姆兹

I doubt that is there any state machine framework like https://github.com/pluginaweek/state_machine for PHP.

I've had to define many if-else logical clauses, and I would like something to help make it more fun by just defining:

  1. Condition required to transition
  2. State after transition

Then this can be reused to check if conditions match or not, for example

$customer->transition('platinum');

I expect this line of code to implicitly check if the customer can transition or not. Or explicitly check by:

$customer->canTransitTo('platinum');

Thanks in advance,
noomz

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

淡笑忘祈一世凡恋 2024-10-11 12:00:44

我不知道这样的框架(这并不意味着它不存在)。虽然不像链接框架那样功能丰富,但状态模式实现起来相当简单。考虑下面这个简单的实现:

interface EngineState
{
    public function startEngine();
    public function moveForward();
}

class EngineTurnedOffState implements EngineState
{
    public function startEngine()
    {
        echo "Started Engine\n";
        return new EngineTurnedOnState;
    }
    public function moveForward()
    {
        throw new LogicException('Have to start engine first');
    }
}

class EngineTurnedOnState implements EngineState
{
    public function startEngine()
    {
        throw new LogicException('Engine already started');
    }
    public function moveForward()
    {
        echo "Moved Car forward";
        return $this;
    }
}

定义状态后,您只需将它们应用到您的主对象:

class Car implements EngineState
{
    protected $state;
    public function __construct()
    {
        $this->state = new EngineTurnedOffState;
    }
    public function startEngine()
    {
        $this->state = $this->state->startEngine();
    }
    public function moveForward()
    {
        $this->state = $this->state->moveForward();
    }
}

然后您可以执行以下操作

$car = new Car;
try {
    $car->moveForward(); // throws Exception
} catch(LogicException $e) {
    echo $e->getMessage();
}

$car = new Car;
$car->startEngine();
$car->moveForward();

:对于减少过大的 if/else 语句,这应该足够了。请注意,在每次转换时返回一个新的状态实例效率有些低。就像我说的,这是一个简单的实现来说明这一点。

I don't know a framework like this (which doesn't mean it does not exist). But while not as feature packed as the linked framework, the State pattern is rather simple to implement. Consider this naive implementation below:

interface EngineState
{
    public function startEngine();
    public function moveForward();
}

class EngineTurnedOffState implements EngineState
{
    public function startEngine()
    {
        echo "Started Engine\n";
        return new EngineTurnedOnState;
    }
    public function moveForward()
    {
        throw new LogicException('Have to start engine first');
    }
}

class EngineTurnedOnState implements EngineState
{
    public function startEngine()
    {
        throw new LogicException('Engine already started');
    }
    public function moveForward()
    {
        echo "Moved Car forward";
        return $this;
    }
}

After you defined the states, you just have to apply them to your main object:

class Car implements EngineState
{
    protected $state;
    public function __construct()
    {
        $this->state = new EngineTurnedOffState;
    }
    public function startEngine()
    {
        $this->state = $this->state->startEngine();
    }
    public function moveForward()
    {
        $this->state = $this->state->moveForward();
    }
}

And then you can do

$car = new Car;
try {
    $car->moveForward(); // throws Exception
} catch(LogicException $e) {
    echo $e->getMessage();
}

$car = new Car;
$car->startEngine();
$car->moveForward();

For reducing overly large if/else statements, this should be sufficient. Note that returning a new state instance on each transition is somewhat inefficient. Like I said, it's a naive implementation to illustrate the point.

相思故 2024-10-11 12:00:44

我一直在研究一个简单的 PHP 有限状态机库,有点类似于 Rails state_machine。代码在这里:https://github.com/chriswoodford/techne/tree/v0。 1

一个汽车示例,类似于所选的答案(上面),看起来像这样:

初始化

  $machine = new StateMachine\FiniteStateMachine();
  $machine->addEvent('start', array('parked' => 'idling'));
  $machine->addEvent('drive', array('idling' => 'driving'));
  $machine->addEvent('stop', array('driving' => 'idling'));
  $machine->addEvent('park', array('idling' => 'parked'));
  $machine->setInitialState('parked');

用法

  $machine->start();
  echo $machine->getCurrentStatus();
  // prints "idling"     

  $machine->drive();
  echo $machine->getCurrentStatus();
  // prints "driving"

  $machine->stop();
  echo $machine->getCurrentStatus();
  // prints "idling"

  $machine->park();
  echo $machine->getCurrentStatus();
  // prints "parked"

它缺少一个显式定义的接口,因为它使用 __call() 魔术方法来接受消息,但这很容易被使用适配器解决。

I've been working on a simple PHP finite state machine library, kind of similar to the rails state_machine. The code is here: https://github.com/chriswoodford/techne/tree/v0.1

A car example, similar to the selected answer (above) would look something like this:

Initialization

  $machine = new StateMachine\FiniteStateMachine();
  $machine->addEvent('start', array('parked' => 'idling'));
  $machine->addEvent('drive', array('idling' => 'driving'));
  $machine->addEvent('stop', array('driving' => 'idling'));
  $machine->addEvent('park', array('idling' => 'parked'));
  $machine->setInitialState('parked');

Usage

  $machine->start();
  echo $machine->getCurrentStatus();
  // prints "idling"     

  $machine->drive();
  echo $machine->getCurrentStatus();
  // prints "driving"

  $machine->stop();
  echo $machine->getCurrentStatus();
  // prints "idling"

  $machine->park();
  echo $machine->getCurrentStatus();
  // prints "parked"

It lacks an explicitly defined interface that since it uses the __call() magic method to accept messages, but that could easily be resolved with an adapter.

橙味迷妹 2024-10-11 12:00:44

Symfony 自 2016 年起就有一个 工作流组件,其中包括您可以想象到的所有状态机功能,现在看起来既灵活又成熟。它与 Symfony 绑定在一起,因此如果您使用 Symfony 和 Doctrine,那么集成起来非常容易(但您也可以独立使用它)。我也喜欢您如何转储工作流程的视觉表示以显示向人们展示或与团队讨论,并且在线或现实研讨会中有许多优秀的教程。

YouTube 上有一些关于这两种状态机以及如何将它们与工作流组件一起使用的精彩视频,例如 米歇尔·桑弗 (Michelle Sanver) 的这篇文章托比亚斯·尼霍姆 (Tobias Nyholm) 的这篇文章

话虽这么说,工作流组件确实假设了有关状态机的许多事情,并且如果您遵循这些原则并将其与其他相邻组件(Symfony Event Dispatcher 和 Doctrine)结合起来,效果会最好。如果您正在使用领域驱动设计或 CQRS 或事件存储或任何类似的高级功能,那么处理您自己的状态可能是有意义的。首先绘制状态机(这样您就知道状态和转换),然后实现类来处理更改或访问当前状态 - 状态机的复杂之处在于逻辑(无论如何您都应该提前考虑),而不一定是逻辑代码中的表示。充分掌握状态机的工作原理,受到 Symfony 工作流组件等库的启发(您甚至可以在这些库中进行原型设计或首先尝试使用它们),然后尝试使其适应您的情况并查看最重要的状态在哪里机器逻辑是什么以及如何执行它。

Symfony has a workflow component since 2016 which includes all the state machine features you can imagine and seems both flexible and mature by now. It is tied in to Symfony, so if you are using Symfony and Doctrine it is very easy to integrate (but you can use it standalone too). I also like how you can dump visual representations of your workflows to show it to people or discuss it with a team, and there are many excellent tutorials available online or in real-world workshops.

There are a few good videos on Youtube about both state machines and how you use them with the workflow component, like this one by Michelle Sanver or this one by Tobias Nyholm.

That being said, the workflow component does assume many things about your state machine and works best if you follow those principles and combine it with other adjacent components (Symfony Event Dispatcher and Doctrine). If you are using Domain Driven Design or CQRS or an event store or anything advanced like that it might make sense to just handle your own states. First draw your state machine (so you know the states and transitions) and then implement classes to handle changes or access the current state - the complex thing about state machines is the logic (which you should think about in advance anyway), not necessarily the representation in code. Get a good grasp of how state machines work, be inspired by libraries like the Symfony workflow component (you can even prototype in those or try it with them first), and then try to adapt it to your situation and see where your most important state machine logic is and how you can enforce it.

少钕鈤記 2024-10-11 12:00:44

我用过这个 https://github.com/yohang/Finite 它非常强大,但是文档不是那么详细。如果您熟悉状态机,那么您应该不会有任何问题。

I have used this one https://github.com/yohang/Finite which is quite powerfull, however the docs are not so detailed. If you are familliar with state machines then you shouldn't have any problem.

陪你到最终 2024-10-11 12:00:44

我已经为 php 编写了一个状态机。我相信您很早之前就已经找到了解决方案。但对于访问此页面的人来说,欢迎您尝试这个状态机。

https://github.com/definitely246/state-machine

要使用它,您需要定义转换事件处理程序作为类。然后您需要定义转换。有限状态机可以配置为执行其他操作,但这里是基础知识。

class Event1ChangedState1ToState2
{
    public function allow($context)
    {
        return true;
    }

    public function handle($context)
    {
        if (!$context->statesChanged) $context->statesChanged = 0;
        print "state1 -> state2\n";
        return $context->statesChanged++;
    }
}

class Event1ChangedState2ToState1
{
    public function allow($context)
    {
        return true;
    }

    public function handle($context)
    {
        print "state2 -> state1\n";
        return $context->statesChanged++;
    }
}

然后,您可以定义在触发事件时更改状态的转换。

$transitions = [
   [ 'event' => 'event1', 'from' => 'state1', 'to' => 'state2', 'start' => true],
   [ 'event' => 'event1', 'from' => 'state2', 'to' => 'state1' ],
];

$fsm = new StateMachine\FSM($transitions);

print $fsm->state() . PHP_EOL; // 'state1'

$fsm->event1(); // returns 1, prints 'state1 -> state2'

print $fsm->state() . PHP_EOL; // 'state2'

$fsm->event1(); // returns 2, prints 'state2 -> state1'

print $fsm->state() . PHP_EOL; // 'state1'

你可以用composer安装

composer require definitely246/state-machine

I've written a state machine for php. I'm sure you've figured out a solution long ago for this. But for people visiting this page, you are welcome to try out this state machine.

https://github.com/definitely246/state-machine

To use it, you define transition event handlers as classes. Then you need to define transitions. The finite state machine can be configured to do other stuff, but here are the basics.

class Event1ChangedState1ToState2
{
    public function allow($context)
    {
        return true;
    }

    public function handle($context)
    {
        if (!$context->statesChanged) $context->statesChanged = 0;
        print "state1 -> state2\n";
        return $context->statesChanged++;
    }
}

class Event1ChangedState2ToState1
{
    public function allow($context)
    {
        return true;
    }

    public function handle($context)
    {
        print "state2 -> state1\n";
        return $context->statesChanged++;
    }
}

You can then define transitions that change states when an event is triggered.

$transitions = [
   [ 'event' => 'event1', 'from' => 'state1', 'to' => 'state2', 'start' => true],
   [ 'event' => 'event1', 'from' => 'state2', 'to' => 'state1' ],
];

$fsm = new StateMachine\FSM($transitions);

print $fsm->state() . PHP_EOL; // 'state1'

$fsm->event1(); // returns 1, prints 'state1 -> state2'

print $fsm->state() . PHP_EOL; // 'state2'

$fsm->event1(); // returns 2, prints 'state2 -> state1'

print $fsm->state() . PHP_EOL; // 'state1'

You can install with composer

composer require definitely246/state-machine
澉约 2024-10-11 12:00:44

作为记录,我编写了(又一个)状态机

https://github.com/EFTEC/StateMachineOne

但是,它基于自动化而不是事件(但是可以使用字段作为事件)。状态转换由值手动或通过代码触发。

例如,停车示例:

其中踏板、交钥匙、油门、速度和制动是由代码定义和控制的字段。

$smachine->addTransition(PARKED,IDLING
    ,'when pedal = 1 and turnkey = 1 and gas > 0');
$smachine->addTransition(IDLING,DRIVING
    ,'when gas > 0 and speed > 0');
$smachine->addTransition(DRIVING,IDLING
    ,'when brake = 1 and speed = 0');
$smachine->addTransition(IDLING,PARKED
    ,'when turnkey = 0 and speed = 0');

因此,甚至可以对高级功能进行编程,例如汽车没油了怎么办,甚至可以修改数值。

特点:

  • 它将信息存储到数据库中(可选)。
  • 它有一个 UI(用于测试)参见图片

state machine

我还发表了一篇关于它的文章

https://medium.com/cook-php/creating-a-state-machine-using-php-ddef9395430e

For the record, I write (yet another) state machine

https://github.com/EFTEC/StateMachineOne

However, it's based in automation rather of event (however it is possible to use a field as event). The transition of state is triggered by values, manually or by code.

For example, the parking example:

where pedal, turnkey, gas, speed and brake are fields defined and controlled by code.

$smachine->addTransition(PARKED,IDLING
    ,'when pedal = 1 and turnkey = 1 and gas > 0');
$smachine->addTransition(IDLING,DRIVING
    ,'when gas > 0 and speed > 0');
$smachine->addTransition(DRIVING,IDLING
    ,'when brake = 1 and speed = 0');
$smachine->addTransition(IDLING,PARKED
    ,'when turnkey = 0 and speed = 0');

So, it is even possible to program advanced features such as what if the car runs out of gas or even modify values.

Features:

  • It stores the information into the database (optional).
  • It has a UI (for testing) see image

state machine

I also published an article about it

https://medium.com/cook-php/creating-a-state-machine-using-php-ddef9395430e

纵性 2024-10-11 12:00:44

我创建了 Smalldb 框架来使用状态机实现 Web 应用程序的模型层。它被设计为直接在 SQL 数据库上工作,其中 SQL 表的每一行代表一个状态机的实例(因此状态机是持久的)。

它具有基于角色的访问控制,因此您可以在状态机定义中指定允许哪些用户进行哪些转换(它还可以处理状态机实例的所有者或更复杂实体的组件)。

为了使开发更快,Smalldb 可以加载使用 yEd 编辑器创建的 GraphML 中的状态图,因此您可以绘制状态图,然后直接在应用程序和文档中使用它。还有一个正在开发的本机编辑器(jQuery 小部件 + 桌面包装器)。为了调试目的和在线应用内文档,Smalldb 可以使用 Graphviz 生成状态图。

I've created Smalldb framework to implement model layer of a web application using state machines. It is designed to work directly on SQL database, where each row of the SQL table represents an instance of a state machine (so the state machines are persistent).

It has role-based access control, so you can specify in state machine definition which transitions are allowed to which users (it also can deal with owners of the state machine instance or components of more complex entities).

To make development faster Smalldb can load state diagrams in GraphML created with yEd editor, so you can draw a state chart and then directly use it in your application as well as in your documentation. Also there is a native editor (jQuery widget + desktop wrapper) under development. For debugging purposes and online in-app documentation the Smalldb can generate state diagrams using Graphviz.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文