标记 / 规则
一个简单的无状态生产规则引擎,适用于PHP 5.3。
Requires
- php: >=5.3.0
- pimple/pimple: 1.0.*
Requires (Dev)
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”,它将执行优先查找
- 名为
bar
的方法 - 名为
bar
的公共属性 - ArrayAccess + offsetExists名为
bar
如果变量解析为一个数组,它将返回
- 数组索引
bar
如果上述所有都不正确,它将返回此VariableProperty的默认值。
但这不仅仅是...
查看测试套件以获取更多示例(和一些热烈的CS 320组合逻辑动作)。
Ruler是管道。带你的瓷器来。
Ruler不关心规则从哪里来。也许你有一个围绕ORM或ODM的RuleManager。也许你编写了一个简单的DSL并解析静态文件。
无论你的口味如何,Ruler都会处理逻辑。