eftec/statemachineone

适用于业务流程的状态机库

2.26.1 2024-09-08 15:48 UTC

README

这是一个专门针对业务流程编写的PHP状态机库。
该库只有一个简单的外部依赖,是一个最小化(但完整)的库,仅包含3个类。

由于这个库是PHP原生,所以它可以在Laravel、Symfony以及其他任何框架中运行。

Packagist Total Downloads Maintenance composer php php CocoaPods

什么是状态机?

状态机(也称为 自动机)是基于 状态作业 的程序执行。每个作业在同一时间必须有一个单一的状态,例如“已启动”、“待处理”、“进行中”等,作业根据某些逻辑或条件改变状态(转换)。这些条件可以是字段、时间或自定义函数。

该库的目标是简化创建业务状态机的流程。

目录

注意

  • 作业:它是运行的过程。作业在同一时间可以只有一个状态。
  • 状态:它是作业的当前条件。
  • 转换:它是从一个 状态 到另一个状态的转换。转换受一组值、时间或函数的限制。
    此外,每个转换都可以有超时。如果达到超时,则执行转换,无论值或条件如何(即使有活动的状态暂停)。转换可以有3种结果
    • 改变 转换改变状态,作业保持活跃。只有当作业有活动的状态 = 活跃时,才能执行转换。
    • 暂停 转换改变状态,作业暂停。只有当作业有活动的状态 = 活跃时,才能执行转换。
    • 继续 转换改变状态,作业从暂停中恢复。只有当作业有活动的状态 = 暂停或活跃时,才能执行转换。
    • 停止 转换改变状态,作业停止。只有当作业有活动的状态 = 活跃或暂停时,才能执行转换。
    • 停留 转换不改变状态,但执行由集合定义的操作。
    • 停留一次 它与停留类似,但只执行一次操作。
    • 事件:(可选)。事件是特殊操作,可以改变一个或多个状态。
  • 活跃状态:每个任务都有一个活跃状态。共有4种:无、停止、活跃、非活跃、暂停。它不同于状态。例如,一个任务可能的状态是:进行中,而活跃状态是:暂停。
    • = 任务不存在。它不能改变状态,也不能默认加载(从数据库中)。
    • 停止 = 任务已停止(完成),可能是成功、中止或取消。它不能改变状态,也不能默认加载。
    • 暂停 = 任务处于暂停状态,它不会改变状态(除非被强制),但可以继续。
    • 活跃 = 任务正常运行,它可以改变状态。
    • 非活跃 = 任务计划在某个日期运行。除非激活(由计划),否则不能改变状态。
  • 参考:每个任务都有一些相关字段。例如,如果任务是关于发票的,那么参考必须是发票编号。状态机不使用任何参考,但它保留这些值以与其他系统集成。
  • 字段:每个任务都有一些字段或值,它可以触发转换。

例如,ChopSuey中国外卖食品。

我们需要创建一个过程来提供送餐服务。这容易吗?好吧,让我们来看看。

ChopSuey

字段(ChopSuey练习)

字段是用于状态机的值。在这个例子中,我没有包括其他可能有用的值(如金钱、客户姓名、地址等),因为它们不是状态机的一部分或未被使用。

  • customerpresent = 1 如果客户在家,0 = 如果不在,= null 如果尚未定义。
  • addressnotfound = 1 如果配送员找不到地址,= 0 如果找到,= null 如果尚未定义。
  • signeddeliver = 1 如果客户签收了配送,= 0 如果没有,= null 如果未定义。
  • abort = 1 如果配送必须中止(例如,发生事故),= 0 如果不是。
  • instock = 1 如果产品有库存,= 0 如果没有,= null 如果未定义。
  • picked = 1 如果配送员已经取货并打包产品,= 0 如果还没有。

状态(ChopSuey练习)

必须包含所有可能的情况。现实世界并不像:销售和金钱那么简单。

  • STATE_PICK 配送员将取任何食物。例如,如果客户点了北京烤鸭(带橙酱)而餐馆没有怎么办?

cooker

  • STATE_CANCEL 订单被取消。
  • STATE_TRANSPORT 配送员正在路上送食物。

cooker

  • STATE_ABORTTRANSPORT 发生了一些事情,配送必须中止。
  • STATE_HELP 配送员准备好送餐,但他找不到地址或可能没有人,所以他呼叫求助。
  • STATE_DELIVERED 食物已送达。我们的英雄返回基地(中国餐馆)。

cooker

  • STATE_ABORTED 交易被中止,无人在家或地址错误。

转换(ChopSuey练习)

  • STATE_PICK -> STATE_CANCEL (END) 当?instock=0(任务结束)
  • STATE_PICK -> STATE_TRANSPORT 当?instock=1 和 picked=1
  • STATE_TRANSPORT -> STATE_ABORTTRANSPORT (END) 当?abort=1(由于某种原因,我们的男孩中止了运输,是不是下雨了?)
  • STATE_TRANSPORT -> STATE_DELIVERED (END) 当?addressnotfound=0,customerpresent=1 和 signeddeliver=1。它已送达,客户在场,并签收了配送(希望还能得到小费)
  • STATE_TRANSPORT -> STATE_HELP 当?addressnotfound=1,customerpresent=0 和 signeddeliver≠1。我们的配送员打电话回家寻求新的指示。是不是假的街道1234这个地址正确?。
  • STATE_HELP -> STATE_ABORTED (结束) 何时?(15分钟期限)或如果abort=1。
    我们的送货上门,是的,地址是假的(这是一个令人震惊的惊喜)
  • STATE_HELP -> STATE_DELIVERED (结束) 何时?addressnotfound=0,customerpresent=1和signeddeliver=1。已经送达,客户在场,并已签收(希望还有小费)

最终代码(ChopSuey示例)

示例/ChopSuey.php

其他示例

示例/BuyMilk.php(购买牛奶)

示例/Car.php(汽车停车)

<?php
use eftec\statemachineone\StateMachineOne;

include "vendor/autoload.php";

define("STATE_PICK",1);
define("STATE_CANCEL",2);
define("STATE_TRANSPORT",3);
define("STATE_ABORTTRANSPORT",4);
define("STATE_TODELIVER",5);
define("STATE_HELP",6);
define("STATE_DELIVERED",7);
define("STATE_ABORTED",8);

$smachine=new StateMachineOne();
$smachine->setDebug(true);
$smachine->tableJobs="chopsuey_jobs";
$smachine->tableJobLogs="chopsuey_logs";

$smachine->setDefaultInitState(STATE_PICK);
$smachine->setAutoGarbage(false); // we don't want to delete automatically a stopped job.
$smachine->setStates([STATE_PICK=>'Pick order'
	,STATE_CANCEL=>'Cancel order'
	,STATE_TRANSPORT=>'Transport order'
	,STATE_ABORTTRANSPORT=>'Abort the delivery'
	,STATE_TODELIVER=>'Pending to deliver'
	,STATE_HELP=>'Request assistance'
	,STATE_DELIVERED=>'Delivered'
	,STATE_ABORTED=>'Aborted']);

$smachine->fieldDefault=[
	'customerpresent'=>-1
	,'addressnotfound'=>-1
	,'signeddeliver'=>-1
	,'abort'=>-1
	,'instock'=>-1
	,'picked'=>-1
	,'message'=>''];
$smachine->setDB('mysql','localhost',"root","abc.123","statemachinedb");
$smachine->createDbTable(false); // you don't need to create this table every time.

$smachine->loadDBAllJob(); // we load all jobs, including finished ones.

// business rules
$smachine->addTransition(STATE_PICK,STATE_PICK
	,'when instock = 0 set message="without stock"','stay'); // it stays in the same state
$smachine->addTransition(STATE_PICK,STATE_CANCEL
	,'when instock = 0 set abort = 1','stop'); // ends the process
$smachine->addTransition(STATE_PICK,STATE_TRANSPORT
	,'when instock = 1','change'); // changes transition
$smachine->addTransition(STATE_TRANSPORT,STATE_ABORTTRANSPORT
	,'when abort = 1','stop'); // ends the process
$smachine->addTransition(STATE_TRANSPORT,STATE_DELIVERED
	,'when addressnotfound = 0 and customerpresent = 1 and signeddeliver = 1 timeout 3600','stop'); // 1 hour max.
$smachine->addTransition(STATE_TRANSPORT,STATE_HELP
	,'when addressnotfound = 1 or customerpresent = 0 timeout 3600','change'); // 1 hour max
$smachine->addTransition(STATE_HELP,STATE_ABORTED
	,'when wait timeout 900','change'); // it waits 15 minutes max.
$smachine->addTransition(STATE_HELP,STATE_DELIVERED
	,'when addressnotfound = 0 and customerpresent = 1 and signeddeliver = 1','change');


$msg=$smachine->fetchUI();
$smachine->checkAllJobs();

$smachine->viewUI(null,$msg); // null means it takes the current job

转换语言

假设下一个转换

$smachine->addTransition(STATE_PICK,STATE_CANCEL
	,'when instock = 0 set abort = 1','stop');

转换被写成如下

  • 初始状态(或初始状态)
  • 结束状态
  • 转换语言
  • 结果,可能是 change(默认值)stoppausecontinuestay
    • change 意味着如果满足条件(或超时),状态将从 初始状态 变更到 结束状态。只有在状态活动时才会进行变更。
    • stop 意味着状态将变更并且工作将停止(工作结束)
    • pause 意味着状态将变更并且工作将暂停。一个暂停的工作不能变更状态,即使它满足条件。
    • continue 意味着状态将变更并且工作将从暂停处继续。
    • stay 意味着状态不会变更(但如果有的话,将执行任何其他指令)。

转换语言使用以下语法编写。

"_when_ var1 = var2 and var3 = var4 or var4 = var5"
"_set_ var1 = var2 , var3 = var4"
"_timeout_ var1"
"_fulltimeout_ var2"
  • 我们可以在 when 和/或 set 和/或 else 中执行三个操作

转换当

它添加了一个条件,表示转换必须执行的位置。例如

$smachine->addTransition(STATE_ONE,STATE_TWO,'when field = 0');

在这种情况下,当字段为零时,状态从 STATE_ONE 变更到 STATE_TWO。

也可以存在多个初始状态

$smachine->addTransition([STATE_ONE,STATE_EXTRA],STATE_TWO,'when field = 0');
// its the same than to add multiples transitions:
// $smachine->addTransition(STATE_ONE,STATE_TWO,'when field = 0');
// $smachine->addTransition(STATE_EXTRA,STATE_TWO,'when field = 0');

转换类型

"when field=0"  // it happens when the field is zero.   
"when $var='hi'" // it happens when the global variable is 'hi'   
"when fn()=44" // the transition is triggered when the function fn() returns 44  
"when always" // its always true. It is the same than "when 1=1". The transition is always executed

它比较一个常量。比较的二进制运算符是

  • = 等于
  • <> 不等于
  • < <= “小于”和“小于等于”
  • > >= “大于”和“大于等于”
  • contain 如果一个文本包含其他文本。
"when field contain 'text'"

字段的值可以是以下之一

  • field = 它是作业的字段。
"when field = field2"  // when field (of the job) is equals to field2
  • _idjob = 它是当前作业的编号。每次评估作业时都会计算。
"set id=_idjob"
  • _time = 它是当前时间戳。每次评估作业时都会计算。注意:它使用getTime()函数,并且可以修改。
"set ts=_time"
  • _state0 = 它是作业的初始(当前)状态(作为状态的ID)
"set currentstate=_state0"
  • _state1 = 它是作业的下一个状态(作为ID)。仅在作业状态转换时评估,否则为null。不能在“when”中使用,因为转换尚未发生。
"set nextstate=_state1"
  • _result = 它是转换的结果('change','pause','continue','stop','stay','stayonce')。仅在作业状态转换时评估,否则为null。不能在“when”中使用,因为转换尚未发生。
"set transition_happened=_result"
  • $var = 它是PHP的全局变量。请注意
$v1=20;
$smachine->addTransition(S1,S2,"when $v1=1"); // WRONG: the variable v1 is evaluated when it is defined, i.e equals to write "when 20=1"
$smachine->addTransition(S1,S2,"when \$v1=1"); // RIGHT: the variable v1 is evaluated when the transition is checked
$smachine->addTransition(S1,S2,'when $v1=1'); // RIGHT:(') the variable v1 is evaluated when the transition is checked
  • 777 = 它是一个数字常量
"when field = 777" // when field is equals to 777
  • "AAA"'aaa' = 它是一个文本
    当field = 'hello' // 当字段等于文本hello时

  • function() = 它是一个全局函数。每个函数都必须有$job参数。

"when field = somefunc()" // function somefunc(Job $job) {...} 
  • null() 它是空值
"when field = null() "
  • true() 它是真值(true)
"when field = true()" // when field is equals to true
  • false() 它是假值(false)
"when field = true()"  // when field is equals to false
  • on() 它是开值(1)

  • off() 它是关值(0)

  • undef() 它是未定义值(-1)

  • flip() 表示值将被反转(1=>0和0=>1)。例如(x=1)x = flip(),现在(x=0)。如果值不为零,则将其反转为零。

"set field=flip()" // it is only valid for set.
  • now() 定义当前时间戳(以秒为单位)。
  • interval() 返回当前状态与上次状态之间的间隔。
  • fullinterval() 返回当前状态与作业开始之间的间隔。
  • timestate() 返回当前状态的时间流逝。

例如

$smachine=new StateMachineOne();
function someFunction($job) {
    // we could do something here we should return a value
    return 0;
}
$var=222;
$smachine->fieldDefault=[
	'field2'=>0
	,'field3'=>0
	,'field4'=>0];
$smachine->addTransition(STATE_ONE,STATE_TWO
                         ,'when field2 = 2 and field3 > someFunction() and  field4=$var');

转换设置

在状态转换时,我们可以添加一个或多个变量变化。

$smachine->addTransition(STATE_ONE,STATE_TWO,'when field = 0 set field=1');

在这种情况下,当我们处于 "STATE_ONE" 状态且 field=0 时,我们转换为状态 "STATE_TWO",并将 field=1 赋值。

示例

"set field = 0 , field2 = 3"

它设置作业的字段。

  • 操作的第一个值不能是常数。
"set 0 = 20"  // is invalid
  • 我们也可以设置一个函数。
"set myfunc() = 20"  

其中函数(全局)必须定义为 myfunc(Job $job,$input) {}

  • 第一个值可以是字段或(全局)变量。
"set field=20"  
"set $variable=20"  

"set field = 0"  

它将字段设置为 0。

"set field + 1"

它将字段的值增加 1(field=field+1)。

"set field - 1"

它将字段的值减少 1(field=field-1)。

转换否则

它与 "set" 类似,但它在状态转换未完成时执行。

$smachine->addTransition(STATE_ONE,STATE_TWO,'when field = 0 set field2="done" else field2="not done"');

在这种情况下,它将 field2="未完成" 设置为状态转换完成之前。

注意:此操作在每次表达式评估时都会调用。因此,它可能被评估多次。

转换超时(秒)

$smachine->addTransition(STATE_ONE,STATE_TWO,'when field = 0 timeout 3600');

当字段为零或状态 ONE 中的 3600 秒已过时,状态从 STATE_ONE 转换到 STATE_TWO。

它设置当前状态和当前时间之间的超时时间。如果发生超时,则执行转换。

"timeout 3600"   // 1 hour timeout  
"timeout field" // timeout by field, it is calculated each time.

转换完全超时(秒)

$smachine->addTransition(STATE_ONE,STATE_TWO,'when field = 0 fulltimeout  3600');

当字段为零或自作业开始以来已过 3600 秒时,状态从 STATE_ONE 转换到 STATE_TWO。

它设置初始状态和当前时间之间的超时时间。如果发生超时,则执行转换。

"fulltimeout 3600"   // 1 hour timeout  
"fulltimeout field" // timeout by field, the field is evaluated each time.    

JOB

什么是作业?

假设我们有一个蓝图来建造房屋。这里的 job 是建造房屋的动作,而蓝图是 transitions
因此,作业是我们工作流程的一个操作部分。

一个 job 保存值,包括当前 state,并且它有一个生命周期,而工作流程(转换)不保存任何单个值。我们可以创建一个短生命周期作业,它在单个网络线程中工作。然而,如果我们需要保存值,则可以使用数据库或文件系统(平面文件)。

创建作业

有几种创建作业的方法。其中之一是使用 GUI,另一种是通过代码。

通过代码创建作业

// $smachine is our state machine with transitions, events and such.
$job=$smachine->createJob(['value1'=>'hi','value2'=>'world']); // we create a job with the values value1 and value2
// or we could create the job the with the default values
$job=$smachine->createJob();

$smachine->checkJob($job); // then we executed the job.

var_dump($job->state); // the id of the current state. 
var_dump($job->fields); // And we could see the result of the job

在作业中运行状态机

$smachine->checkJob($job); // then we executed the job.
$smachine->checkAllJob(); // We execute all jobs stored in our state machine.

获取作业

$smachine->getLastJob(); // we load the last job (if we already has a job into a memory);
$smachine->getJob($idJob); // we get a job from the queue.
$smachine->getJob($idJob); // we get a job from the queue.
$smachine->getJobQueue(); // we get all the jobs from the queue
$smachine->loadDbJob($idJob); // we read a job with the id $idJob and we store into the queue
$smachine->loadDbAllJob(); // We load all jobs (including inactive jobs) from the database and we store into the queue
$smachine->loadDBActiveJobs(); // We load all active jobs from the database and we store into the queue.

数据库和作业。

$smachine->loadDbJob($idJob); // we read a job with the id $idJob
$smachine->loadDbAllJob(); // We load all jobs (including inactive jobs) from the database.
$smachine->loadDBActiveJobs(); // We load all active jobs from the database.
$smachine->saveDBAllJob(); // we save all the jobs in memory.
$smachine->saveDBJob($job); // we save a specific job.
$smachine->saveDBJobLog($job,$texto); // we save a log of a job.
$smachine->deleteJobDB($job); // we delete a specific job.

作业中使用的字段

  • $idJob int 作业在队列中的编号或位置
  • $idParentJob int|null 父作业的编号
  • $dateInit int 初始日期(时间戳)
  • $dateLastChange int 最后更改的日期(时间戳)
  • $dateEnd int 结束日期(时间戳)
  • $dateExpired int 过期日期(时间戳)
  • $state string|int 当前状态的 ID
  • $fields array 每个作业的字段或值。它必须是一个关联数组
  • $stateFlow array 指示状态流
  • $transitions bool[] 用于确定转换是否已执行
  • $isNew bool 如果作业是新作业。用于存储到数据库中(插入)
  • $isUpdate bool 如果作业已更新。用于存储到数据库中(更新)
  • $log string[]

标志

可以在作业中存储一个或多个标志。
标志是一个状态或值,

  • 它可以有名称。
  • 它可以有一个级别,例如严重程度级别。
  • 它可以有一个过期时间。
  • 可以有一个可交换的值,例如开关。
  • 它与 StateMachineOne 实例和作业相关联。

创建标志数组的示例

$flags=new Flags('flag1', true, $smachine); // the flags is called flag1, $smachine is a state machine 
$flags->setParent($job); // it is also associate with a job.

图形用户界面

此库具有内置的 GUI 用于测试。

GUID

StateMachineOne 这是主类。Job 这是作业的模型类。
Transition 这是转换的模型类。

缓存配置

可以缓存所有配置。

保存配置

$stateMachine=new StateMachineOne();
// configuration goes here.
file_put_contents("mycode.php", "<?php\n".$stateMachine->cacheMachine("FumMachineCache"));  

加载配置

$stateMachine=new StateMachineOne();
include 'mycode.php';
FumMachineCache($stateMachine);

日志格式

通常,日志格式可以是 info 或 error 类型。标志可以显示不同类型。

[info]

state,,changed,,Parked,,1,,Idling,,2,,0,,change

  • state = 操作的动词。

  • changed = 执行的操作。可以是 changed,stop,continue。

  • Parked = 第一个状态(在变化之前)

  • 1 = 第一个状态的编号(在变化之前)

  • Idling = 它变化到的状态。

  • 2 = 它变化到的状态的编号。

  • 0 = 触发变化的交易。

  • change = 变化的描述。

[error]

state,,transition,,text,,1,,message // 当作业的检查失败时

savejob,,message // 保存作业失败时。

changestate,,idjob,,idstate,newstate // 当状态变化失败时

许可

双许可(LGPL 3.0 和商业)。请参阅 LICENSE 文件。

版本

  • 2.26 2024-07-20

    • 添加了函数 setRecordEventState(),replayRecordInit() 和 replayRecordInsideLoop()
  • 2.25 2024-07-19

    • 添加了函数 setTime()
  • 2.24 2024-07-18

    • 升级到 PHP 7.4
    • 更新注释
  • 2.23.3 2023-06-29

    • 修复了 flag::pull() 中的问题,其中 msg 为 null。
  • 2.23.2 2023-06-29

    • 修复了 flag::push() 中的问题,其中值为 null。
  • 2.23.1 23-03-11

    • 更新了依赖项。
  • 2.23 22-09-11

    • 为转换添加了描述(注释)。
    • 修复了 GUI 中的问题(作业必须是整数)
  • 2.22 22-09-11

    • 添加了变量 _state0,_state1,_time,_result 并修复了 _idjob
  • 2.21.1 2022-09-03

    • 修复了 Flags 中 Job 或 Parent 为 null 的问题。
    • 接口 StateSerializable 参数允许 Job 或 null
  • 2.21 2022-09-03

    • 更新了依赖项。
    • 大多数方法添加了类型提示/验证。
  • 2.20 2022-08-26

    • 修复了一些错别字。
    • 修复了 gettime() 的一个错误,它可能返回浮点数或整数。
  • 2.18 2022-06-28

    • 更新依赖项。
  • 2.17 2021-10-01

    • [核心] 添加了方法 $zeroDate,用于在数据库中设置默认的日期时间值。
  • 2.17 2021-09-26

    • [核心] 添加了方法 duringState2(),以便添加一个保持状态的转换,因此可以将逻辑保存到类中。
  • 2.16 2021-09-26

    • [核心] 更新了类 MiniLang 的依赖项 2.20.1
    • [核心] 解决了方法 SaveDbJob() 的问题。
    • [核心] 新方法 addMethodTransition2() 可以用于创建一个使用 MiniLang 新版本逻辑的类。
    • [转换] 现在类 Transition 可以在运行时使用 Minilang(正常行为)或使用预编译生成的类来计算转换。这个特性将大大提高性能,但需要创建这个类。
  • 2.15 2021-09-18

    • 为类 Flag 添加了方法 messages()
  • 2.14 2021-09-17

    • 添加了 duringState(),允许在作业处于某些状态时执行一个操作。
    • 添加了内部函数 timestate(),它返回当前状态的当前时间(当前作业的状态)。
  • 2.13 2021-07-03

    • addTransition() 现在允许多个初始状态。
    • 在 composer.json 中更新了依赖项。
  • 2.12 2021-01-16

    • 对代码进行了一些清理。
    • 更新了依赖项。
  • 2.11 2020-10-16

    • 作业有一个额外的字段称为 idParentJob。 作业表必须重建 或添加列:idparentjob int。
  • 2.10.1 2020-10-15

    • 在 saveDbJob 中有一个小错误,其中 $backup 字段为 null,而我们正在更新。
  • 2.10 2020-10-15

    • 日志现在由 ',,' 而不是 '|' 分隔。这是因为某些消息可能使用 "|"。
    • 在日志状态中,我们添加了交易编号。
  • saveDbJob(): 在数据库中更新:库不会更新未更改的字段。为此,
    它每次加载作业时都会创建一个备份变量,并将备份与要保存的作业进行比较。

  • 2.9.2 2020-09-29 saveDbJob() 更新了主键字段。现在,它跳过更新。

  • 2.9.1 2020-09-22 cacheMachine() 现在正确工作。

  • 2.9 2020-09-20 标志的显示方式不同。此外,text_job 的序列化现在使用 serialize 而不是 JSON。之前的任务必须刷新,可以使用 $stateMachine->createDbTable(false); 来刷新。

  • 2.8 2020-09-15 添加了 $fieldUI 字段以指定视觉组件。

  • 2.7 2020-08-11 依赖项的小更新。

  • 2.6 2020-04-23

    • 简化了安装过程。现在“文档”不包括在安装中
  • 2.5 2020-04-13

    • 更新依赖项 eftec/pdoone 1.15 到 1.32.1
    • 更新依赖项 etec/minilang 2.14 到 2.15
  • 2.4 2019-12-26

    • 服务对象现在可以在事件上工作。
    • 更新库 eftec/PdoOne 到 1.15
  • 2.3 2019-12-26

    • Createdbtable() 方法现在设置一个有效的默认值
    • 一些清理。
    • Bootstrap 更新到 4.4.1。此外,它现在使用 https 而不是 http
  • 2.2 2019-10-22

    • 更新 eftec/MiniLang (依赖项) 从 2.9 => 2.12
    • 更新 eftec/pdoone (依赖项) 从 1.11 => 1.12
    • 添加依赖项/documentstoreone (依赖项) 到 1.11(允许使用文件系统作为数据库)
    • 添加了新的方法 setDocOne(),getDocOne()
    • 现在库允许使用 pdo(MySQL 数据库)或文件系统(documentOne)进行持久化。
    • 修复了 UI 的问题(它只执行了最后一个任务)
    • 添加了 UI 的更改。现在,可以查看和更改当前任务。
    • 修复了创建表的问题。现在 TEXT_JOB 列始终被创建。
  • 2.1 2019-08-28

    • 更新 eftec/minilang 到 2.9。
    • 允许在每字段中存储数组
    • 如果任务的字段是对象或数组,则它将存储在 MEDIUMTEXT 字段(序列化)中
    • 方法 Flags::flagexist()
    • 方法 StateMachineOne::removetransition()
  • 2.0 2019-08-24

    • 更改了标志。推送的定义被反转。现在 push('msg','id'..) 变成 push('id','msg'..)
    • 添加了设置时间的方法。
  • 1.12 2019-08-16

    • 更新 MiniLang
    • 添加了 viewJson() 方法
    • 现在如果任务为 null,事件不会崩溃。
    • 删除了私有方法 CreateColTable()。
    • Flag() 现在具有过期时间(可选)
  • 1.11 2019-08-04 一些修复。

  • 1.10 2019-08-04

    • 更新到 "eftec/minilang": "^2.7"
    • 解决了在 callEvent() 中如果没有任务则不会失败的问题。
    • 添加了 cacheMachine() 方法以缓存结果。
    • 代码格式化到 PSR-2
  • 1.9 2019-08-03

    • 一些修复。现在 UI 不显示“停留”在同一状态的事件。
    • 现在它使用 eftec/minilang 2.6,允许使用 "else"。
    • UI 更简化。
    • 添加了 createColsTable() 方法。
  • 1.8 2019-07-29 一些清理和 setPdoOne(),getPdoOne() 方法;

  • 1.7 2019-06-16 许多更改。

  • 1.6 2018-12-26 现在 MiniLang 是一个独立的依赖项。

  • 1.5 2018-12-23 圣诞更新(顺便说一句,真是个糟糕的日子)。

    • 现在语言解析方式不同。空格不再是必需的。
    • "when timeout" 没有被弃用。现在它被称为 "when always"。
  • 1.4 2018-12-12

    • 一些修复。
  • 1.3 2018-12-11

    • 添加了 addEvent() 和 callEvent() 方法
    • 将 timeout 和 fulltimeout 添加到转换语言中
    • 现在转换不需要 timeout。
    • 不再使用 idRef。
  • 1.2 2018-12-09 更新依赖项

  • 1.1 2018-12-09 一些修正。

  • 1.0 2018-12-08 第一个(非测试版)版本。

缺少什么

  • 事件和超时
  • 大多数单元测试,现在它只是基本的。单元测试是真实的,但仍然很基础。
  • 增加日志功能。