遵循条件 -> 动作范式的规则引擎

1.0.1 2015-03-02 16:51 UTC

This package is not auto-updated.

Last update: 2024-09-24 02:42:20 UTC


README

Build Status

遵循条件 -> 动作范式的规则引擎

什么是规则?

规则由三个元素组成

一个条件

每个规则都有一个布尔条件。如果条件满足(即,其值不是false),则处理该规则。

一些动作

每个规则都有一些关联的动作。这些动作可以在不同的规则阶段执行

  • 在评估规则的条件之前(无论是否满足)
  • 在其条件满足之后但在其子规则执行之前
  • 在其条件满足并且其子规则执行之后。
  • 在离开规则之前,无论条件是否满足。

零个或多个子规则

每个规则可以包含子规则。只有当父规则的条件满足时,每个子规则才会执行。

当执行时,规则遵循的流程可以描述为

Perform pre-rule actions
If the rule's condition is satisfied then
    Perform pre-subrules actions
    Execute subrules
    Perform post-subrules actions
Perform post-rule actions

快速示例

$ctx = new Context();
$ctx['name'] = 'Philip J.';
$ctx['surname'] = 'Fry';

// If the context's name is 'Philip J.' AND the context's surname is 'Fry'...
$condition = new AndOp(
    new EqualTo('{{ name }}', 'Philip J.'),
    new EqualTo('{{ surname }}', 'Fry')
);

// Then print the message
$action = new RunCallback(function (Context $c) {
    echo 'Hello, my name is ' . $c['name'] . $c['surname'];
});

$rule = new Rule();
$rule
    ->setCondition($condition)
    ->setAction($action)
    ->execute($ctx);
    
// Hello, my name is Philip J. Fry

条件

条件使用布尔表达式来检查是否必须处理规则。默认情况下,规则的条件是 true

原始数据类型

有四种预定义的原始数据类型

  • 字符串
  • 整数
  • 浮点数
  • 布尔值

运算符

运算符接受一个、两个或更多值(原始数据类型或其他运算符的输出)并返回其他值

逻辑运算符

AndOp($expr [, $expr]*)

AndOp() 运算符接受一个或多个参数并返回 true,如果 所有 参数都被评估为 true

$and = new AndOp(true, false, false, true);
echo $and->evaluate(); // <-- false

OrOp($expr [, $expr]*)

OrOp() 运算符接受一个或多个参数并返回 true,如果 任何 参数被评估为 true

$or = new OrOp(true, false, false, true);
echo $or->evaluate(); // <-- true

NotOp($expr [, $expr]*)

NotOp() 运算符接受一个或多个参数并返回 true,如果 所有 参数都被评估为 false。这实际上是一个 Nand 操作。

$not = new Not(true, false, false, true);
echo $not->evaluate(); // <-- false

比较条件

EqualTo($expr, $expr [, $expr]*)

EqualTo() 运算符接受两个或多个参数并返回 true,如果 所有 参数都是相同的

$equalTo = new EqualTo("foo", "bar", "baz");
echo $equalTo->evaluate(); // <-- false

NotEqualTo($expr, $expr [, $expr]*)

EqualTo() 运算符接受两个或多个参数并返回 true,如果 任何 参数与另一个不同

$notEqualTo = new EqualTo("foo", "bar", "baz");
echo $notEqualTo->evaluate(); // <-- true

动作

覆盖上下文

此动作更新/扩展当前上下文

$ctx = new Context();
$ctx['name'] = 'Philip J.';

$overrideCtx['surname'] = 'Fry';
$action = new OverrideContext($overrideCtx);
$action->perform($ctx);

var_dump($ctx);
// array(
//     'name' => 'Philip J.',
//     'surname' => 'Fry'
// );

插值上下文

此动作允许通过将自身作为变量替换来将当前上下文作为模板进行渲染

$ctx = new Context();
$ctx['name'] = 'Philip J.';
$ctx['surname'] = 'Fry';
$ctx['fullname'] = '{{ name }} {{ surname }}';

$action = new InterpolateContext();
$action->perform($ctx);

var_dump($ctx);
// array(
//     'name' => 'Philip J.',
//     'surname' => 'Fry'
//     'fullname' => 'Philip J. Fry'
// );

过滤上下文

此动作通过保留或丢弃上下文中的条目进行过滤

$ctx = new Context();
$ctx['name'] = 'Philip J.';
$ctx['surname'] = 'Fry';
$ctx['fullname'] = 'Philip J. Fry';

$action = new FilterContext();
$action
    ->setKeys('fullname')
    ->setMode(FilterContext::ALLOW_KEYS)
    ->perform($ctx);

var_dump($ctx);
// array(
//     'fullname' => 'Philip J. Fry'
// );

运行回调

此动作调用一个自定义用户函数,将当前上下文作为参数传递

$ctx = new Context();
$ctx['name'] = 'Philip J.';
$ctx['surname'] = 'Fry';

$callback = function(Context $ctx) {
    $ctx['fullname'] = $ctx['name'] . ' ' . $ctx['surname'];
    unset($ctx['surname'];
}

$action = new RunCallback($callback);
$action->perform($ctx);

var_dump($ctx);
// array(
//     'name' => 'Philip J.',
//     'fullname' => 'Philip J. Fry'
// );

无动作

此动作什么都不做 :)

$ctx = new Context();
$ctx['name'] = 'Philip J.';
$ctx['surname'] = 'Fry';

$action = new NoAction($callback);
$action->perform($ctx);

var_dump($ctx);
// array(
//     'name' => 'Philip J.',
//     'surname' => 'Fry'
// );

序列

此动作允许定义要按顺序执行的动作列表

$ctx = new Context();
$ctx['fullname'] = '{{ name }} {{ surname }}';

$override = new OverrideContext(new Context(array('name' => 'Philip J.')));     // Adds name
$callback = new RunCallback(function (Context $c) { $c['surname'] = 'Fry'; } ); // Adds surname
$interpolate = new InterpolateContext();                                        // Interpolates fullname
$filter = new FilterContext(FilterContext::ALLOW_KEYS, 'fullname');             // Filters all except fullname

$sequence = new Sequence($override, $callback);
$sequence->appendAction($interpolate)->appendAction($filter);
$sequence->perform($ctx);

var_dump($ctx);
// array(
//     'fullname' => 'Philip J. Fry'
// );