cleverage/Ruler is a PHP 5.3 Library. Use it to implement your own business rules, and link them in order to check if you are satisfying the set.
Exemple :
You want to check if the current user can activate a feature. For this, you have to check that :
- he is connected,
- he has the premium suscription,
- he has enough money in his account.
<?php // test
// PHP 5.3
$rule = new IsConnectedRule($user);
$rule->andRule(new IsSuscriberRule($user, 'PREMIUM'))
->andRule(new HasMoneyRule($user, 300))
->orRule(new IsAdminRule($user));
// PHP 5.4
$rule = (new IsConnectedRule($user))
->andRule(new IsSuscriberRule($user, 'PREMIUM'))
->andRule(new HasMoneyRule($user, 300))
->orRule(new IsAdminRule($user));
try {
if ($rule->isSatisfied()) {
echo 'activated';
}
} catch (NotConnectedException $e) {
// show connection form
} catch (NotSuscriberException $e) {
// show subscription form
} catch (NotEnoughMoneyException $e) {
echo 'not enough Money';
} catch(\CleverAge\Ruler\Exception\Exception $e) {
echo 'Failed : '.$e->getMessage();
}
<?php // IsConnectedRule class
class IsConnectedRule extends \CleverAge\Ruler\RuleAbstract
{
protected $_user;
protected $_failure_exception_class = 'NotConnectedException';
protected $_failure_message = 'user is not connected';
public function __construct(\User $user)
{
$this->_user = $user;
}
public function doIsSatisfied()
{
return $this->_user->isLoggedOn();
}
}
Combination of rules can even be done in a single rule class, in order to simplify your application code and increase maintability.
// ActiveFeatureXRule class
class ActiveFeatureXRule extends \CleverAge\Ruler\RuleAbstract
{
public function __construct(\User $user)
{
$this->andRule(new IsSuscriberRule($user, 'PREMIUM'))
->andRule(new HasMoneyRule($user, 300))
->orRule(new IsAdminRule($user));
}
public function doIsSatisfied()
{
// method is abstract, and this container rule always satisfies.
return true;
}
}
Now, you can use this rule class everywhere you need it, and just change the construct to have th rules reverberated everewhere.
The order in which you set OR/AND/NAND rules is not important. At the end, they are grouped by type.
- You want your ruleset satisfied :
// A,B,C,D,G,Z are rules
$A->andRule($B)
->orRule($C->andRule($Z))
->andRule($D)
->nandRule($G)
->isSatisfied();
// PHP =>($A && $B && $D && !$G) || ($C && $Z)
// Binary => (A.B.D.!G)+(C.Z)
- You want your ruleset not satisfied :
// A,B,C,D,G,Z are rules
$A->andRule($B)
->orRule($C->andRule($Z))
->andRule($D)
->nandRule($G)
->isNotSatisfied()
// PHP => (!$A || !$B || !$D || $G) && (!$C || !$Z)
// Binary => (!A+!B+!D+G).(!C+!Z)
by default, isNotSatisfied() returns !isSatisfied(). But sometimes, you may want to personnalize the doIsNotSatisfied() method in order to optimize workflow (like SQL queries).
If you have one generic rule (e.g. : ObjectIsEqual), you may need to have different exception thrown when composing complex rules. Each Rule has a setter for this :
$A = new MyRule();
$A->setException('My\Name\Space\Ruler\Exceptions\MyException', 'my custom error message');
$A->isSatisfied();
// If rule is not satisfied
// it throws a new My\Name\Space\Ruler\Exceptions\MyException('my custom error message') exception