标记/规则

一个简单的无状态生产规则引擎,适用于PHP 5.3。

dev-master 2013-10-14 11:15 UTC

This package is auto-updated.

Last update: 2024-09-10 05:20:52 UTC


README

Ruler的版本是从bobthecow/Ruler分叉的。它移除了规则存储回调的能力,该回调在规则通过时执行(这应该委托给另一个机制)。

此外,当规则未通过时,将存储失败的原因。

Ruler是一个简单的无状态生产规则引擎,适用于PHP 5.3+。

Ruler有一个简单直接的领域特定语言(DSL)

... 由RuleBuilder提供

<?php

$rb = new RuleBuilder;
$rule = $rb->create(
    $rb->logicalAnd(
        $rb['minNumPeople']->lessThanOrEqualTo($rb['actualNumPeople']),
        $rb['maxNumPeople']->greaterThanOrEqualTo($rb['actualNumPeople'])
    )
);

$context = new Context(array(
    'minNumPeople' => 5,
    'maxNumPeople' => 25,
    'actualNumPeople' => function() {
        return 6;
    },
));

$rule->evaluate($context); // "true"

当然,如果你不喜欢简洁

... 你也可以不使用RuleBuilder来使用它

<?php

$actualNumPeople = new Variable('actualNumPeople');
$rule = new Rule(
    new Operator\LogicalAnd(array(
        new Operator\LessThanOrEqualTo(new Variable('minNumPeople'), $actualNumPeople),
        new Operator\GreaterThanOrEqualTo(new Variable('maxNumPeople'), $actualNumPeople)
    ))
);

$context = new Context(array(
    'minNumPeople' => 5,
    'maxNumPeople' => 25,
    'actualNumPeople' => function() {
        return 6;
    },
));

$rule->evaluate($context); // "true"

但这听起来不太有趣,不是吗?

你可以用你的Ruler做些什么

比较事物

<?php

// These are Variables. They'll be replaced by terminal values during Rule evaluation.

$a = $rb['a'];
$b = $rb['b'];

// Here are bunch of Propositions. They're not too useful by themselves, but they
// are the building blocks of Rules, so you'll need 'em in a bit.

$a->greaterThan($b);          // true if $a > $b
$a->greaterThanOrEqualTo($b); // true if $a >= $b
$a->lessThan($b);             // true if $a < $b
$a->lessThanOrEqualTo($b);    // true if $a <= $b
$a->equalTo($b);              // true if $a == $b
$a->notEqualTo($b);           // true if $a != $b
$a->contains($b);             // true if in_array($b, $a) || strpos($b, $a) !== false
$a->doesNotContain($b);       // true if !in_array($b, $a) || strpos($b, $a) === false
$a->sameAs($b);               // true if $a === $b
$a->notSameAs($b);            // true if $a !== $b

组合事物

<?php

// Create a Rule with an $a == $b condition
$aEqualsB = $rb->create($a->equalTo($b));

// Create another Rule with an $a != $b condition
$aDoesNotEqualB = $rb->create($a->notEqualTo($b));

// Now combine them for a tautology!
// (Because Rules are also Propositions, they can be combined to make MEGARULES)
$eitherOne = $rb->create($rb->logicalOr($aEqualsB, $aDoesNotEqualB));

// Just to mix things up, we'll populate our evaluation context with completely
// random values...
$context = new Context(array(
    'a' => rand(),
    'b' => rand(),
));

// Hint: this is always true!
$eitherOne->evaluate($context);

组合更多事物

<?php

$rb->logicalNot($aEqualsB);                  // The same as $aDoesNotEqualB :)
$rb->logicalAnd($aEqualsB, $aDoesNotEqualB); // True if both conditions are true
$rb->logicalOr($aEqualsB, $aDoesNotEqualB);  // True if either condition is true
$rb->logicalXor($aEqualsB, $aDoesNotEqualB); // True if only one condition is true

evaluate 规则

evaluate() 规则与上下文一起使用,以确定它是否为真。

<?php

$context = new Context(array('userName', function() {
    return isset($_SESSION['userName']) ? $_SESSION['userName'] : null;
}));

$userIsLoggedIn = $rb->create($rb['userName']->notEqualTo(null));

if ($userIsLoggedIn->evaluate($context)) {
    // Do something special for logged in users!
}

动态填充你的评估上下文

我们上面的许多示例都使用静态值作为上下文变量。虽然这对于示例来说很好,但在现实生活中并不那么有用。你可能会希望根据所有 sorts of things... 评估规则...

你可以将上下文视为规则评估的ViewModel。你提供静态值,甚至为你的规则需要的变量提供懒惰评估的代码。

<?php

$context = new Context;

// Some static values...
$context['reallyAnnoyingUsers'] = array('bobthecow', 'jwage');

// You'll remember this one from before
$context['userName'] = function() {
    return isset($_SESSION['userName']) ? $_SESSION['userName'] : null;
};

// Let's pretend you have an EntityManager named `$em`...
$context['user'] = function() use ($em, $context) {
    if ($userName = $context['userName']) {
        return $em->getRepository('Users')->findByUserName($userName);
    }
};

$context['orderCount'] = function() use ($em, $context) {
    if ($user = $context['user']) {
        return $em->getRepository('Orders')->findByUser($user)->count();
    }

    return 0;
};

现在你有了所有你需要的信息,可以根据订单数量或当前用户来制定规则,或者任何其他疯狂的事情。我不知道,也许这是一个运费计算器?

如果当前用户已经下了5个或更多的订单,但并不是“真正令人讨厌”,给他们免费送货。

访问变量属性

作为额外的奖励,Ruler让你可以访问上下文变量值上的属性、方法和偏移量。这非常有用。

比如说我们想记录当前用户的姓名,如果他们是管理员

// Reusing our $context from the last example...

// We'll define a few context variables for determining what roles a user has,
// and their full name:

$context['userRoles'] = function() use ($em, $context) {
    if ($user = $context['user']) {
        return $user->roles();
    } else {
        // return a default "anonymous" role if there is no current user
        return array('anonymous');
    }
};

$context['userFullName'] = function() use ($em, $context) {
    if ($user = $context['user']) {
        return $user->fullName;
    }
};


// Now we'll create a rule to write the log message

$rb->create(
    $rb->logicalAnd(
        $userIsLoggedIn,
        $rb['userRoles']->contains('admin')
    ),
    function() use ($context, $logger) {
        $logger->info(sprintf("Admin user %s did a thing!", $context['userFullName']));
    }
);

这有点长。我们不必为可能在规则中访问的每一件事创建上下文变量,我们可以使用VariableProperties,以及它们方便的RuleBuilder接口

// We can skip over the Context Variable building above. We'll simply set our, 
// default roles on the VariableProperty itself, then go right to writing rules:

$rb['user']['roles'] = array('anonymous');

$rb->create(
    $rb->logicalAnd(
        $userIsLoggedIn,
        $rb['user']['roles']->contains('admin')
    ),
    function() use ($context, $logger) {
        $logger->info(sprintf("Admin user %s did a thing!", $context['user']['fullName']);
    }
);

如果父变量解析为一个对象,并且这个VariableProperty的名称是“bar”,它将执行优先查找

  1. 名为bar的方法
  2. 名为bar的公共属性
  3. ArrayAccess + offsetExists名为bar

如果变量解析为一个数组,它将返回

  1. 数组索引bar

如果上述所有都不正确,它将返回此VariableProperty的默认值。

但这不仅仅是...

查看测试套件以获取更多示例(和一些热烈的CS 320组合逻辑动作)。

Ruler是管道。带你的瓷器来。

Ruler不关心规则从哪里来。也许你有一个围绕ORM或ODM的RuleManager。也许你编写了一个简单的DSL并解析静态文件。

无论你的口味如何,Ruler都会处理逻辑。