Browse files

Merge branch 'release/0.3.0'

  • Loading branch information...
2 parents 6809668 + 3ba7ec9 commit 7076f4bbea5606750e18ab15632da2ace3784b44 @bobthecow committed Sep 2, 2014
Showing with 4,923 additions and 463 deletions.
  1. +6 −0 .travis.yml
  2. +135 −34 README.md
  3. +2 −3 composer.json
  4. +213 −7 src/Ruler/Context.php
  5. +60 −0 src/Ruler/Operator.php
  6. +38 −0 src/Ruler/Operator/Addition.php
  7. +37 −0 src/Ruler/Operator/Ceil.php
  8. +0 −40 src/Ruler/Operator/ComparisonOperator.php
  9. +45 −0 src/Ruler/Operator/Complement.php
  10. +27 −7 src/Ruler/Operator/Contains.php
  11. +44 −0 src/Ruler/Operator/ContainsSubset.php
  12. +38 −0 src/Ruler/Operator/Division.php
  13. +0 −35 src/Ruler/Operator/DoesNotContain.php
  14. +44 −0 src/Ruler/Operator/DoesNotContainSubset.php
  15. +43 −0 src/Ruler/Operator/EndsWith.php
  16. +43 −0 src/Ruler/Operator/EndsWithInsensitive.php
  17. +16 −8 src/Ruler/Operator/EqualTo.php
  18. +38 −0 src/Ruler/Operator/Exponentiate.php
  19. +37 −0 src/Ruler/Operator/Floor.php
  20. +16 −8 src/Ruler/Operator/GreaterThan.php
  21. +17 −9 src/Ruler/Operator/GreaterThanOrEqualTo.php
  22. +45 −0 src/Ruler/Operator/Intersect.php
  23. +16 −8 src/Ruler/Operator/LessThan.php
  24. +16 −8 src/Ruler/Operator/LessThanOrEqualTo.php
  25. +12 −12 src/Ruler/Operator/LogicalAnd.php
  26. +10 −51 src/Ruler/Operator/LogicalNot.php
  27. +8 −26 src/Ruler/Operator/LogicalOperator.php
  28. +12 −12 src/Ruler/Operator/LogicalOr.php
  29. +12 −12 src/Ruler/Operator/LogicalXor.php
  30. +37 −0 src/Ruler/Operator/Max.php
  31. +37 −0 src/Ruler/Operator/Min.php
  32. +38 −0 src/Ruler/Operator/Modulo.php
  33. +38 −0 src/Ruler/Operator/Multiplication.php
  34. +37 −0 src/Ruler/Operator/Negation.php
  35. +16 −8 src/Ruler/Operator/NotEqualTo.php
  36. +16 −9 src/Ruler/Operator/NotSameAs.php
  37. +36 −0 src/Ruler/Operator/PropositionOperator.php
  38. +16 −9 src/Ruler/Operator/SameAs.php
  39. +43 −0 src/Ruler/Operator/SetContains.php
  40. +43 −0 src/Ruler/Operator/SetDoesNotContain.php
  41. +43 −0 src/Ruler/Operator/StartsWith.php
  42. +43 −0 src/Ruler/Operator/StartsWithInsensitive.php
  43. +43 −0 src/Ruler/Operator/StringContains.php
  44. +43 −0 src/Ruler/Operator/StringContainsInsensitive.php
  45. +43 −0 src/Ruler/Operator/StringDoesNotContain.php
  46. +43 −0 src/Ruler/Operator/StringDoesNotContainInsensitive.php
  47. +38 −0 src/Ruler/Operator/Subtraction.php
  48. +39 −0 src/Ruler/Operator/SymmetricDifference.php
  49. +41 −0 src/Ruler/Operator/Union.php
  50. +36 −0 src/Ruler/Operator/VariableOperator.php
  51. +2 −4 src/Ruler/Proposition.php
  52. +4 −7 src/Ruler/Rule.php
  53. +55 −24 src/Ruler/RuleBuilder.php
  54. +339 −12 src/Ruler/RuleBuilder/Variable.php
  55. +2 −3 src/Ruler/RuleBuilder/VariableProperty.php
  56. +1 −1 src/Ruler/RuleBuilder/VariablePropertyTrait.php
  57. +2 −4 src/Ruler/RuleSet.php
  58. +241 −0 src/Ruler/Set.php
  59. +152 −9 src/Ruler/Value.php
  60. +4 −5 src/Ruler/Variable.php
  61. +25 −0 src/Ruler/VariableOperand.php
  62. +3 −6 src/Ruler/VariableProperty.php
  63. +272 −2 tests/Ruler/Test/ContextTest.php
  64. +48 −0 tests/Ruler/Test/Fixtures/ALotGreaterThan.php
  65. +1 −1 tests/Ruler/Test/Fixtures/CallbackProposition.php
  66. +13 −0 tests/Ruler/Test/Fixtures/Fact.php
  67. +1 −1 tests/Ruler/Test/Fixtures/FalseProposition.php
  68. +13 −0 tests/Ruler/Test/Fixtures/Invokable.php
  69. +1 −1 tests/Ruler/Test/Fixtures/TrueProposition.php
  70. +265 −0 tests/Ruler/Test/Functional/SetTest.php
  71. +54 −0 tests/Ruler/Test/Operator/AdditionTest.php
  72. +54 −0 tests/Ruler/Test/Operator/CeilTest.php
  73. +105 −0 tests/Ruler/Test/Operator/ComplementTest.php
  74. +64 −0 tests/Ruler/Test/Operator/ContainsSubsetTest.php
  75. +68 −0 tests/Ruler/Test/Operator/DivisionTest.php
  76. +43 −0 tests/Ruler/Test/Operator/EndsWithInsensitiveTest.php
  77. +43 −0 tests/Ruler/Test/Operator/EndsWithTest.php
  78. +1 −2 tests/Ruler/Test/Operator/EqualToTest.php
  79. +54 −0 tests/Ruler/Test/Operator/ExponentiateTest.php
  80. +29 −0 tests/Ruler/Test/Operator/ExtraOperatorTest.php
  81. +54 −0 tests/Ruler/Test/Operator/FloorTest.php
  82. +1 −2 tests/Ruler/Test/Operator/GreaterThanOrEqualToTest.php
  83. +1 −2 tests/Ruler/Test/Operator/GreaterThanTest.php
  84. +95 −0 tests/Ruler/Test/Operator/IntersectTest.php
  85. +1 −2 tests/Ruler/Test/Operator/LessThanOrEqualToTest.php
  86. +1 −2 tests/Ruler/Test/Operator/LessThanTest.php
  87. +2 −3 tests/Ruler/Test/Operator/LogicalAndTest.php
  88. +3 −4 tests/Ruler/Test/Operator/LogicalNotTest.php
  89. +2 −3 tests/Ruler/Test/Operator/LogicalOrTest.php
  90. +3 −4 tests/Ruler/Test/Operator/LogicalXorTest.php
  91. +68 −0 tests/Ruler/Test/Operator/MaxTest.php
  92. +68 −0 tests/Ruler/Test/Operator/MinTest.php
  93. +68 −0 tests/Ruler/Test/Operator/ModuloTest.php
  94. +55 −0 tests/Ruler/Test/Operator/MultiplicationTest.php
  95. +53 −0 tests/Ruler/Test/Operator/NegationTest.php
  96. +1 −2 tests/Ruler/Test/Operator/NotEqualToTest.php
  97. +1 −2 tests/Ruler/Test/Operator/NotSameAsTest.php
  98. +1 −2 tests/Ruler/Test/Operator/SameAsTest.php
  99. +4 −9 tests/Ruler/Test/Operator/{ContainsTest.php → SetContainsTest.php}
  100. +43 −0 tests/Ruler/Test/Operator/StartsWithInsensitiveTest.php
  101. +43 −0 tests/Ruler/Test/Operator/StartsWithTest.php
  102. +65 −0 tests/Ruler/Test/Operator/StringContainsInsensitiveTest.php
  103. +57 −0 tests/Ruler/Test/Operator/StringContainsTest.php
  104. +55 −0 tests/Ruler/Test/Operator/SubtractionTest.php
  105. +105 −0 tests/Ruler/Test/Operator/SymmetricDifferenceTest.php
  106. +95 −0 tests/Ruler/Test/Operator/UnionTest.php
  107. +63 −10 tests/Ruler/Test/RuleBuilder/VariablePropertyTest.php
  108. +67 −18 tests/Ruler/Test/RuleBuilder/VariableTest.php
  109. +80 −2 tests/Ruler/Test/RuleBuilderTest.php
  110. +3 −3 tests/Ruler/Test/RuleSetTest.php
  111. +5 −3 tests/Ruler/Test/RuleTest.php
  112. +1 −1 tests/Ruler/Test/ValueTest.php
  113. +1 −1 tests/Ruler/Test/VariableTest.php
View
6 .travis.yml
@@ -4,6 +4,12 @@ php:
- 5.3
- 5.4
- 5.5
+ - 5.6
+ - hhvm
before_script:
- composer install
+
+matrix:
+ allow_failures:
+ - php: hhvm # this should be the default, eh?
View
169 README.md
@@ -3,7 +3,8 @@ Ruler
Ruler is a simple stateless production rules engine for PHP 5.3+.
-[![Build Status](https://secure.travis-ci.org/bobthecow/Ruler.png?branch=master)](http://travis-ci.org/bobthecow/Ruler)
+[![Package version](http://img.shields.io/packagist/v/ruler/ruler.svg)](https://packagist.org/packages/ruler/ruler)
+[![Build status](http://img.shields.io/travis/bobthecow/Ruler/develop.svg)](http://travis-ci.org/bobthecow/Ruler)
Ruler has an easy, straightforward DSL
@@ -12,8 +13,6 @@ Ruler has an easy, straightforward DSL
... provided by the RuleBuilder:
```php
-<?php
-
$rb = new RuleBuilder;
$rule = $rb->create(
$rb->logicalAnd(
@@ -42,8 +41,6 @@ $rule->execute($context); // "Yay!"
... you can use it without a RuleBuilder:
```php
-<?php
-
$actualNumPeople = new Variable('actualNumPeople');
$rule = new Rule(
new Operator\LogicalAnd(array(
@@ -75,8 +72,6 @@ Things you can do with your Ruler
### Compare things
```php
-<?php
-
// These are Variables. They'll be replaced by terminal values during Rule evaluation.
$a = $rb['a'];
@@ -85,23 +80,80 @@ $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
+$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->stringContains($b); // true if strpos($b, $a) !== false
+$a->stringDoesNotContain($b); // true if strpos($b, $a) === false
+$a->stringContainsInsensitive($b); // true if stripos($b, $a) !== false
+$a->stringDoesNotContainInsensitive($b); // true if stripos($b, $a) === false
+$a->startsWith($b); // true if strpos($b, $a) === 0
+$a->startsWithInsensitive($b); // true if stripos($b, $a) === 0
+$a->endsWith($b); // true if strpos($b, $a) === len($a) - len($b)
+$a->endsWithInsensitive($b); // true if stripos($b, $a) === len($a) - len($b)
+$a->sameAs($b); // true if $a === $b
+$a->notSameAs($b); // true if $a !== $b
```
-### Combine things
+
+### Math even more things
```php
-<?php
+$c = $rb['c'];
+$d = $rb['d'];
+
+// Mathematical operators are a bit different. They're not Propositions, so
+// they don't belong in rules all by themselves, but they can be combined
+// with Propositions for great justice.
+
+$rb['price']
+ ->add($rb['shipping'])
+ ->greaterThanOrEqualTo(50)
+
+// Of course, there are more.
+
+$c->add($d); // $c + $d
+$c->subtract($d); // $c - $d
+$c->multiply($d); // $c * $d
+$c->divide($d); // $c / $d
+$c->modulo($d); // $c % $d
+$c->exponentiate($d); // $c ** $d
+$c->negate(); // -$c
+$c->ceil(); // ceil($c)
+$c->floor(); // floor($c)
+```
+
+
+### Reason about sets
+
+```php
+$e = $rb['e']; // These should both be arrays
+$f = $rb['f'];
+
+// Manipulate sets with set operators
+
+$e->union($f);
+$e->intersect($f);
+$e->complement($f);
+$e->symmetricDifference($f);
+$e->min();
+$e->max();
+
+// And use set Propositions to include them in Rules.
+
+$e->containsSubset($f);
+$e->doesNotContainSubset($f);
+$e->setContains($a);
+$e->setDoesNotContain($a);
+```
+
+### Combine Rules
+
+```php
// Create a Rule with an $a == $b condition
$aEqualsB = $rb->create($a->equalTo($b));
@@ -123,27 +175,27 @@ $context = new Context(array(
$eitherOne->evaluate($context);
```
-### Combine more things
-```php
-<?php
+### Combine more Rules
+```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` and `execute` Rules
`evaluate()` a Rule with Context to figure out whether it is true.
```php
-<?php
-
-$context = new Context(array('userName', function() {
- return isset($_SESSION['userName']) ? $_SESSION['userName'] : null;
-}));
+$context = new Context(array(
+ 'userName' => function() {
+ return isset($_SESSION['userName']) ? $_SESSION['userName'] : null;
+ }
+));
$userIsLoggedIn = $rb->create($rb['userName']->notEqualTo(null));
@@ -155,10 +207,7 @@ if ($userIsLoggedIn->evaluate($context)) {
If a Rule has an action, you can `execute()` it directly and save yourself a
couple of lines of code.
-
```php
-<?php
-
$hiJustin = $rb->create(
$rb['userName']->equalTo('bobthecow'),
function() {
@@ -169,6 +218,7 @@ $hiJustin = $rb->create(
$hiJustin->execute($context); // "Hi, Justin!"
```
+
### Even `execute` a whole grip of Rules at once
```php
@@ -219,8 +269,6 @@ static values, or even code for lazily evaluating the Variables needed by your
Rules.
```php
-<?php
-
$context = new Context;
// Some static values...
@@ -254,6 +302,18 @@ for a shipping price calculator?
> If the current User has placed 5 or more orders, but isn't "really annoying",
> give 'em free shipping.
+```php
+$rb->create(
+ $rb->logicalAnd(
+ $rb['orderCount']->greaterThanOrEqualTo(5),
+ $rb['reallyAnnoyingUsers']->doesNotContain($rb['userName'])
+ ),
+ function() use ($shipManager, $context) {
+ $shipManager->giveFreeShippingTo($context['user']);
+ }
+);
+```
+
Access variable properties
--------------------------
@@ -264,7 +324,6 @@ Context Variable values. This can come in really handy.
Say we wanted to log the current user's name if they are an administrator:
```php
-
// Reusing our $context from the last example...
// We'll define a few context variables for determining what roles a user has,
@@ -304,7 +363,7 @@ everything we might need to access in a rule, we can use VariableProperties, and
their convenient RuleBuilder interface:
```php
-// We can skip over the Context Variable building above. We'll simply set our,
+// 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');
@@ -335,6 +394,48 @@ If none of the above are true, it will return the default value for this
VariableProperty.
+Add your own Operators
+----------------------
+
+If none of the default Ruler Operators fit your needs, you can write your own! Just define
+additional operators like this:
+
+```php
+
+namespace My\Ruler\Operators;
+
+use Ruler\Context;
+use Ruler\Operator\VariableOperator;
+use Ruler\Proposition;
+use Ruler\Value;
+
+class ALotGreaterThan extends VariableOperator implements Proposition
+{
+ public function evaluate(Context $context)
+ {
+ list($left, $right) = $this->getOperands();
+ $value = $right->prepareValue($context)->getValue() * 10;
+
+ return $left->prepareValue($context)->greaterThan(new Value($value));
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
+ }
+}
+```
+
+Then you can use them with RuleBuilder like this:
+
+```php
+$rb->registerOperatorNamespace('My\Ruler\Operators');
+$rb->create(
+ $rb['a']->aLotGreaterThan(10);
+);
+```
+
+
But that's not all...
---------------------
View
5 composer.json
@@ -5,14 +5,13 @@
"homepage": "https://github.com/bobthecow/Ruler",
"license": "MIT",
"require": {
- "php": ">=5.3.0",
- "pimple/pimple": "1.0.*"
+ "php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"autoload": {
- "psr-0": {
+ "psr-0": {
"Ruler": "src",
"Ruler\\Test": "tests"
}
View
220 src/Ruler/Context.php
@@ -3,10 +3,25 @@
/*
* This file is part of the Ruler package, an OpenSky project.
*
- * (c) 2011 OpenSky Project Inc
+ * Copyright (c) 2009 Fabien Potencier
*
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished
+ * to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
*/
namespace Ruler;
@@ -16,10 +31,22 @@
*
* The Context contains facts with which to evaluate a Rule or other Proposition.
*
- * @author Justin Hileman <justin@shopopensky.com>
+ * Derived from Pimple, by Fabien Potencier:
+ *
+ * https://github.com/fabpot/Pimple
+ *
+ * @author Fabien Potencier
+ * @author Justin Hileman <justin@justinhileman.info>
*/
-class Context extends \Pimple
+class Context implements \ArrayAccess
{
+ private $keys = array();
+ private $values = array();
+ private $frozen = array();
+ private $raw = array();
+
+ private $shared;
+ private $protected;
/**
* Context constructor.
@@ -31,6 +58,185 @@ class Context extends \Pimple
*/
public function __construct(array $values = array())
{
- parent::__construct($values);
+ $this->shared = new \SplObjectStorage;
+ $this->protected = new \SplObjectStorage;
+
+ foreach ($values as $key => $value) {
+ $this->offsetSet($key, $value);
+ }
+ }
+
+ /**
+ * Check if a fact is defined.
+ *
+ * @param string $name The unique name for the fact
+ *
+ * @return boolean
+ */
+ public function offsetExists($name)
+ {
+ return isset($this->keys[$name]);
+ }
+
+ /**
+ * Get the value of a fact.
+ *
+ * @param string $name The unique name for the fact
+ *
+ * @return mixed The resolved value of the fact
+ *
+ * @throws InvalidArgumentException if the name is not defined
+ */
+ public function offsetGet($name)
+ {
+ if (!$this->offsetExists($name)) {
+ throw new \InvalidArgumentException(sprintf('Fact "%s" is not defined.', $name));
+ }
+
+ $value = $this->values[$name];
+
+ // If the value is already frozen, or if it's not callable, or if it's protected, return the raw value
+ if (isset($this->frozen[$name]) || !is_object($value) || $this->protected->contains($value) || !$this->isCallable($value)) {
+ return $value;
+ }
+
+ // If this is a shared value, resolve, freeze, and return the result
+ if ($this->shared->contains($value)) {
+ $this->frozen[$name] = true;
+ $this->raw[$name] = $value;
+
+ return $this->values[$name] = $value($this);
+ }
+
+ // Otherwise, resolve and return the result
+ return $value($this);
+ }
+
+ /**
+ * Set a fact name and value.
+ *
+ * A fact will be lazily evaluated if it is a Closure or invokable object.
+ * To define a fact as a literal callable, use Context::protect.
+ *
+ * @param string $name The unique name for the fact
+ * @param mixed $value The value or a closure to lazily define the value
+ *
+ * @throws RuntimeException if a frozen fact overridden
+ */
+ public function offsetSet($name, $value)
+ {
+ if (isset($this->frozen[$name])) {
+ throw new \RuntimeException(sprintf('Cannot override frozen fact "%s".', $name));
+ }
+
+ $this->keys[$name] = true;
+ $this->values[$name] = $value;
+ }
+
+ /**
+ * Unset a fact
+ *
+ * @param string $name The unique name for the fact
+ */
+ public function offsetUnset($name)
+ {
+ if ($this->offsetExists($name)) {
+ $value = $this->values[$name];
+
+ if (is_object($value)) {
+ $this->shared->detach($value);
+ $this->protected->detach($value);
+ }
+
+ unset($this->keys[$name], $this->values[$name], $this->frozen[$name], $this->raw[$name]);
+ }
+ }
+
+ /**
+ * Define a fact as "shared". This lazily evaluates and stores the result
+ * of the callable for the scope of this Context instance.
+ *
+ * @param callable $callable A fact callable to share
+ *
+ * @return callable The passed callable
+ *
+ * @throws InvalidArgumentException if the callable is not a Closure or invokable object
+ */
+ public function share($callable)
+ {
+ if (!$this->isCallable($callable)) {
+ throw new \InvalidArgumentException('Value is not a Closure or invokable object.');
+ }
+
+ $this->shared->attach($callable);
+
+ return $callable;
+ }
+
+ /**
+ * Protect a callable from being interpreted as a lazy fact definition.
+ *
+ * This is useful when you want to store a callable as the literal value of
+ * a fact.
+ *
+ * @param callable $callable A callable to protect from being evaluated
+ *
+ * @return callable The passed callable
+ *
+ * @throws InvalidArgumentException if the callable is not a Closure or invokable object
+ */
+ public function protect($callable)
+ {
+ if (!$this->isCallable($callable)) {
+ throw new \InvalidArgumentException('Callable is not a Closure or invokable object.');
+ }
+
+ $this->protected->attach($callable);
+
+ return $callable;
+ }
+
+ /**
+ * Get a fact or the closure defining a fact.
+ *
+ * @param string $name The unique name for the fact
+ *
+ * @return mixed The value of the fact or the closure defining the fact
+ *
+ * @throws InvalidArgumentException if the name is not defined
+ */
+ public function raw($name)
+ {
+ if (!$this->offsetExists($name)) {
+ throw new \InvalidArgumentException(sprintf('Fact "%s" is not defined.', $name));
+ }
+
+ if (isset($this->frozen[$name])) {
+ return $this->raw[$name];
+ }
+
+ return $this->values[$name];
+ }
+
+ /**
+ * Get all defined fact names.
+ *
+ * @return array An array of fact names
+ */
+ public function keys()
+ {
+ return array_keys($this->keys);
+ }
+
+ /**
+ * Check whether a value is a Closure or invokable object.
+ *
+ * @param mixed $callable
+ *
+ * @return boolean
+ */
+ protected function isCallable($callable)
+ {
+ return is_object($callable) && is_callable($callable);
}
-}
+}
View
60 src/Ruler/Operator.php
@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler;
+
+/**
+ * @author Jordan Raub <jordan@raub.me>
+ */
+abstract class Operator
+{
+ const UNARY = 'UNARY';
+ const BINARY = 'BINARY';
+ const MULTIPLE = 'MULTIPLE';
+
+ protected $operands;
+
+ /**
+ * @param array $operands
+ */
+ public function __construct()
+ {
+ foreach (func_get_args() as $operand) {
+ $this->addOperand($operand);
+ }
+ }
+
+ public function getOperands()
+ {
+ switch ($this->getOperandCardinality()) {
+ case self::UNARY:
+ if (1 != count($this->operands)) {
+ throw new \LogicException(get_class($this) . ' takes only 1 operand');
+ }
+ break;
+ case self::BINARY:
+ if (2 != count($this->operands)) {
+ throw new \LogicException(get_class($this) . ' takes 2 operands');
+ }
+ break;
+ case self::MULTIPLE:
+ if (0 == count($this->operands)) {
+ throw new \LogicException(get_class($this) . ' takes at least 1 operand');
+ }
+ break;
+ }
+
+ return $this->operands;
+ }
+
+ abstract public function addOperand($operand);
+ abstract protected function getOperandCardinality();
+}
View
38 src/Ruler/Operator/Addition.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Value;
+use Ruler\VariableOperand;
+
+/**
+ * An Addition Arithmetic Operator
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class Addition extends VariableOperator implements VariableOperand
+{
+ public function prepareValue(Context $context)
+ {
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return new Value($left->prepareValue($context)->add($right->prepareValue($context)));
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
+ }
+}
View
37 src/Ruler/Operator/Ceil.php
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Value;
+use Ruler\VariableOperand;
+
+/**
+ * A Ceil Math Operator
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class Ceil extends VariableOperator implements VariableOperand
+{
+ public function prepareValue(Context $context)
+ {
+ /** @var VariableOperand $operand */
+ list($operand) = $this->getOperands();
+
+ return new Value($operand->prepareValue($context)->ceil());
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::UNARY;
+ }
+}
View
40 src/Ruler/Operator/ComparisonOperator.php
@@ -1,40 +0,0 @@
-<?php
-
-/*
- * This file is part of the Ruler package, an OpenSky project.
- *
- * (c) 2011 OpenSky Project Inc
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Ruler\Operator;
-
-use Ruler\Proposition;
-use Ruler\Variable;
-
-/**
- * Abstract Comparison Operator class.
- *
- * @abstract
- * @author Justin Hileman <justin@shopopensky.com>
- * @implements Proposition
- */
-abstract class ComparisonOperator implements Proposition
-{
- protected $left;
- protected $right;
-
- /**
- * Comparison Operator constructor.
- *
- * @param Variable $left Left side of comparison
- * @param Variable $right Right side of comparison
- */
- public function __construct(Variable $left, Variable $right)
- {
- $this->left = $left;
- $this->right = $right;
- }
-}
View
45 src/Ruler/Operator/Complement.php
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Set;
+use Ruler\VariableOperand;
+
+/**
+ * A Complement Set Operator
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class Complement extends VariableOperator implements VariableOperand
+{
+ public function prepareValue(Context $context)
+ {
+ $complement = null;
+ /** @var VariableOperand $operand */
+ foreach ($this->getOperands() as $operand) {
+ if (!$complement instanceof Set) {
+ $complement = $operand->prepareValue($context)->getSet();
+ } else {
+ $set = $operand->prepareValue($context)->getSet();
+ $complement = $complement->complement($set);
+ }
+ }
+
+ return $complement;
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::MULTIPLE;
+ }
+}
View
34 src/Ruler/Operator/Contains.php
@@ -12,24 +12,44 @@
namespace Ruler\Operator;
use Ruler\Context;
+use Ruler\Proposition;
+use Ruler\VariableOperand;
/**
* A Contains comparison operator.
*
- * @author Justin Hileman <justin@shopopensky.com>
- * @extends ComparisonOperator
+ * @deprecated Please use SetContains or StringContains operators instead.
+ *
+ * @author Justin Hileman <justin@justinhileman.info>
*/
-class Contains extends ComparisonOperator
+class Contains extends VariableOperator implements Proposition
{
/**
- * Evaluate whether the left variable is contained within the right in the current Context.
- *
- * @param Context $context Context with which to evaluate this ComparisonOperator
+ * @param Context $context Context with which to evaluate this Proposition
*
* @return boolean
*/
public function evaluate(Context $context)
{
- return $this->left->prepareValue($context)->contains($this->right->prepareValue($context));
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ $left = $left->prepareValue($context);
+
+ if (is_array($left->getValue())) {
+ trigger_error('Contains operator is deprecated, please use SetContains', E_USER_DEPRECATED);
+
+ return $left->getSet()->setContains($right->prepareValue($context));
+ } else {
+ trigger_error('Contains operator is deprecated, please use StringContains', E_USER_DEPRECATED);
+
+ return $left->stringContains($right->prepareValue($context));
+ }
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
}
}
View
44 src/Ruler/Operator/ContainsSubset.php
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Proposition;
+use Ruler\VariableOperand;
+
+/**
+ * A ContainsSubset comparison operator.
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class ContainsSubset extends VariableOperator implements Proposition
+{
+ /**
+ * @param Context $context Context with which to evaluate this Proposition
+ *
+ * @return boolean
+ */
+ public function evaluate(Context $context)
+ {
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return $left->prepareValue($context)->getSet()
+ ->containsSubset($right->prepareValue($context)->getSet());
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
+ }
+}
View
38 src/Ruler/Operator/Division.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Value;
+use Ruler\VariableOperand;
+
+/**
+ * A Division Arithmetic Operator
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class Division extends VariableOperator implements VariableOperand
+{
+ public function prepareValue(Context $context)
+ {
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return new Value($left->prepareValue($context)->divide($right->prepareValue($context)));
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
+ }
+}
View
35 src/Ruler/Operator/DoesNotContain.php
@@ -1,35 +0,0 @@
-<?php
-
-/*
- * This file is part of the Ruler package, an OpenSky project.
- *
- * (c) 2011 OpenSky Project Inc
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Ruler\Operator;
-
-use Ruler\Context;
-
-/**
- * A DoesNotContain comparison operator.
- *
- * @author Justin Hileman <justin@shopopensky.com>
- * @extends ComparisonOperator
- */
-class DoesNotContain extends ComparisonOperator
-{
- /**
- * Evaluate whether the left variable is not contained within the right in the current Context.
- *
- * @param Context $context Context with which to evaluate this ComparisonOperator
- *
- * @return boolean
- */
- public function evaluate(Context $context)
- {
- return $this->left->prepareValue($context)->contains($this->right->prepareValue($context)) === false;
- }
-}
View
44 src/Ruler/Operator/DoesNotContainSubset.php
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Proposition;
+use Ruler\VariableOperand;
+
+/**
+ * A DoesNotContainSubset comparison operator.
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class DoesNotContainSubset extends VariableOperator implements Proposition
+{
+ /**
+ * @param Context $context Context with which to evaluate this Proposition
+ *
+ * @return boolean
+ */
+ public function evaluate(Context $context)
+ {
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return $left->prepareValue($context)->getSet()
+ ->containsSubset($right->prepareValue($context)->getSet()) === false;
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
+ }
+}
View
43 src/Ruler/Operator/EndsWith.php
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Proposition;
+use Ruler\VariableOperand;
+
+/**
+ * A EndsWith comparison operator.
+ *
+ * @author Cornel Les <thebogu@gmail.com>
+ */
+class EndsWith extends VariableOperator implements Proposition
+{
+ /**
+ * @param Context $context Context with which to evaluate this Proposition
+ *
+ * @return boolean
+ */
+ public function evaluate(Context $context)
+ {
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return $left->prepareValue($context)->endsWith($right->prepareValue($context));
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
+ }
+}
View
43 src/Ruler/Operator/EndsWithInsensitive.php
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Proposition;
+use Ruler\VariableOperand;
+
+/**
+ * A EndsWith insensitive comparison operator.
+ *
+ * @author Cornel Les <thebogu@gmail.com>
+ */
+class EndsWithInsensitive extends VariableOperator implements Proposition
+{
+ /**
+ * @param Context $context Context with which to evaluate this Proposition
+ *
+ * @return boolean
+ */
+ public function evaluate(Context $context)
+ {
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return $left->prepareValue($context)->endsWith($right->prepareValue($context), true);
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
+ }
+}
View
24 src/Ruler/Operator/EqualTo.php
@@ -12,24 +12,32 @@
namespace Ruler\Operator;
use Ruler\Context;
+use Ruler\Proposition;
+use Ruler\VariableOperand;
/**
* An EqualTo comparison operator.
*
- * @author Justin Hileman <justin@shopopensky.com>
- * @extends ComparisonOperator
+ * @author Justin Hileman <justin@justinhileman.info>
*/
-class EqualTo extends ComparisonOperator
+class EqualTo extends VariableOperator implements Proposition
{
/**
- * Evaluate whether the given variables are equal in the current Context.
- *
- * @param Context $context Context with which to evaluate this ComparisonOperator
+ * @param Context $context Context with which to evaluate this Proposition
*
* @return boolean
*/
public function evaluate(Context $context)
{
- return $this->left->prepareValue($context)->equalTo($this->right->prepareValue($context));
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return $left->prepareValue($context)->equalTo($right->prepareValue($context));
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
}
-}
+}
View
38 src/Ruler/Operator/Exponentiate.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Value;
+use Ruler\VariableOperand;
+
+/**
+ * An Exponentiate Math Operator
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class Exponentiate extends VariableOperator implements VariableOperand
+{
+ public function prepareValue(Context $context)
+ {
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return new Value($left->prepareValue($context)->exponentiate($right->prepareValue($context)));
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
+ }
+}
View
37 src/Ruler/Operator/Floor.php
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Value;
+use Ruler\VariableOperand;
+
+/**
+ * A Floor Math Operator
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class Floor extends VariableOperator implements VariableOperand
+{
+ public function prepareValue(Context $context)
+ {
+ /** @var VariableOperand $operand */
+ list($operand) = $this->getOperands();
+
+ return new Value($operand->prepareValue($context)->floor());
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::UNARY;
+ }
+}
View
24 src/Ruler/Operator/GreaterThan.php
@@ -12,24 +12,32 @@
namespace Ruler\Operator;
use Ruler\Context;
+use Ruler\Proposition;
+use Ruler\VariableOperand;
/**
* A GreaterThan comparison operator.
*
- * @author Justin Hileman <justin@shopopensky.com>
- * @extends ComparisonOperator
+ * @author Justin Hileman <justin@justinhileman.info>
*/
-class GreaterThan extends ComparisonOperator
+class GreaterThan extends VariableOperator implements Proposition
{
/**
- * Evaluate whether the left variable is greater than the right in the current Context.
- *
- * @param Context $context Context with which to evaluate this ComparisonOperator
+ * @param Context $context Context with which to evaluate this Proposition
*
* @return boolean
*/
public function evaluate(Context $context)
{
- return $this->left->prepareValue($context)->greaterThan($this->right->prepareValue($context));
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return $left->prepareValue($context)->greaterThan($right->prepareValue($context));
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
}
-}
+}
View
26 src/Ruler/Operator/GreaterThanOrEqualTo.php
@@ -12,24 +12,32 @@
namespace Ruler\Operator;
use Ruler\Context;
+use Ruler\Proposition;
+use Ruler\VariableOperand;
/**
- * A GreaterThan comparison operator.
+ * A GreaterThanOrEqualTo comparison operator.
*
- * @author Justin Hileman <justin@shopopensky.com>
- * @extends ComparisonOperator
+ * @author Justin Hileman <justin@justinhileman.info>
*/
-class GreaterThanOrEqualTo extends ComparisonOperator
+class GreaterThanOrEqualTo extends VariableOperator implements Proposition
{
/**
- * Evaluate whether the left variable is greater than or equal to the right in the current Context.
- *
- * @param Context $context Context with which to evaluate this ComparisonOperator
+ * @param Context $context Context with which to evaluate this Proposition
*
* @return boolean
*/
public function evaluate(Context $context)
{
- return $this->left->prepareValue($context)->lessThan($this->right->prepareValue($context)) === false;
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return $left->prepareValue($context)->lessThan($right->prepareValue($context)) === false;
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
}
-}
+}
View
45 src/Ruler/Operator/Intersect.php
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Set;
+use Ruler\VariableOperand;
+
+/**
+ * A Set Intersection Operator
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class Intersect extends VariableOperator implements VariableOperand
+{
+ public function prepareValue(Context $context)
+ {
+ $intersect = null;
+ /** @var VariableOperand $operand */
+ foreach ($this->getOperands() as $operand) {
+ if (!$intersect instanceof Set) {
+ $intersect = $operand->prepareValue($context)->getSet();
+ } else {
+ $set = $operand->prepareValue($context)->getSet();
+ $intersect = $intersect->intersect($set);
+ }
+ }
+
+ return $intersect;
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::MULTIPLE;
+ }
+}
View
24 src/Ruler/Operator/LessThan.php
@@ -12,24 +12,32 @@
namespace Ruler\Operator;
use Ruler\Context;
+use Ruler\Proposition;
+use Ruler\VariableOperand;
/**
* A LessThan comparison operator.
*
- * @author Justin Hileman <justin@shopopensky.com>
- * @extends ComparisonOperator
+ * @author Justin Hileman <justin@justinhileman.info>
*/
-class LessThan extends ComparisonOperator
+class LessThan extends VariableOperator implements Proposition
{
/**
- * Evaluate whether the left variable is less than the right in the current Context.
- *
- * @param Context $context Context with which to evaluate this ComparisonOperator
+ * @param Context $context Context with which to evaluate this Proposition
*
* @return boolean
*/
public function evaluate(Context $context)
{
- return $this->left->prepareValue($context)->lessThan($this->right->prepareValue($context));
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return $left->prepareValue($context)->lessThan($right->prepareValue($context));
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
}
-}
+}
View
24 src/Ruler/Operator/LessThanOrEqualTo.php
@@ -12,24 +12,32 @@
namespace Ruler\Operator;
use Ruler\Context;
+use Ruler\Proposition;
+use Ruler\VariableOperand;
/**
* A LessThanOrEqualTo comparison operator.
*
- * @author Justin Hileman <justin@shopopensky.com>
- * @extends ComparisonOperator
+ * @author Justin Hileman <justin@justinhileman.info>
*/
-class LessThanOrEqualTo extends ComparisonOperator
+class LessThanOrEqualTo extends VariableOperator implements Proposition
{
/**
- * Evaluate whether the left variable is less than or equal to the right in the current Context.
- *
- * @param Context $context Context with which to evaluate this ComparisonOperator
+ * @param Context $context Context with which to evaluate this Proposition
*
* @return boolean
*/
public function evaluate(Context $context)
{
- return $this->left->prepareValue($context)->greaterThan($this->right->prepareValue($context)) === false;
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return $left->prepareValue($context)->greaterThan($right->prepareValue($context)) === false;
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
}
-}
+}
View
24 src/Ruler/Operator/LogicalAnd.php
@@ -12,34 +12,34 @@
namespace Ruler\Operator;
use Ruler\Context;
+use Ruler\Proposition;
/**
* A logical AND operator.
*
- * @author Justin Hileman <justin@shopopensky.com>
- * @extends LogicalOperator
+ * @author Justin Hileman <justin@justinhileman.info>
*/
class LogicalAnd extends LogicalOperator
{
/**
- * Evaluate whether all child Propositions evaluate to true given the current Context.
- *
- * @param Context $context Context with which to evaluate this LogicalOperator
+ * @param Context $context Context with which to evaluate this Proposition
*
* @return boolean
*/
public function evaluate(Context $context)
{
- if (empty($this->propositions)) {
- throw new \LogicException('Logical And requires at least one proposition');
- }
-
- foreach ($this->propositions as $prop) {
- if ($prop->evaluate($context) === false) {
+ /** @var Proposition $operand */
+ foreach ($this->getOperands() as $operand) {
+ if ($operand->evaluate($context) === false) {
return false;
}
}
return true;
}
-}
+
+ protected function getOperandCardinality()
+ {
+ return static::MULTIPLE;
+ }
+}
View
61 src/Ruler/Operator/LogicalNot.php
@@ -17,66 +17,25 @@
/**
* A logical NOT operator.
*
- * @author Justin Hileman <justin@shopopensky.com>
- * @extends LogicalOperator
+ * @author Justin Hileman <justin@justinhileman.info>
*/
class LogicalNot extends LogicalOperator
{
- protected $proposition;
-
/**
- * Logical NOT constructor
- *
- * Logical NOT is unable to process multiple child Propositions, so passing an array with
- * more than one Proposition will result in a LogicException.
- *
- * @param array $props Child Proposition (default:null)
- *
- * @throws LogicException
- */
- public function __construct(array $props = null)
- {
- if ($props !== null) {
- if (count($props) != 1) {
- throw new \LogicException('Logical Not requires exactly one proposition');
- }
-
- $this->proposition = array_pop($props);
- }
- }
-
- /**
- * Set the child Proposition.
- *
- * Logical NOT is unable to process multiple child Propositions, so calling addProposition
- * if a Proposition has already been set will result in a LogicException.
+ * @param Context $context Context with which to evaluate this Proposition
*
- * @param Proposition $prop Child Proposition
- *
- * @throws LogicException
+ * @return boolean
*/
- public function addProposition(Proposition $prop)
+ public function evaluate(Context $context)
{
- if (isset($this->proposition)) {
- throw new \LogicException('Logical Not requires exactly one proposition');
- }
+ /** @var Proposition $operand */
+ list($operand) = $this->getOperands();
- $this->proposition = $prop;
+ return !$operand->evaluate($context);
}
- /**
- * Evaluate whether the child Proposition evaluates to false given the current Context.
- *
- * @param Context $context Context with which to evaluate this LogicalOperator
- *
- * @return boolean
- */
- public function evaluate(Context $context)
+ protected function getOperandCardinality()
{
- if (!isset($this->proposition)) {
- throw new \LogicException('Logical Not requires exactly one proposition');
- }
-
- return !$this->proposition->evaluate($context);
+ return static::UNARY;
}
-}
+}
View
34 src/Ruler/Operator/LogicalOperator.php
@@ -14,39 +14,21 @@
use Ruler\Proposition;
/**
- * Abstract Logical Operator class.
+ * Logical operator base class
*
- * Logical Operators represent propositional operations: AND, OR, NOT and XOR.
- *
- * @abstract
- * @author Justin Hileman <justin@shopopensky.com>
- * @implements Proposition
+ * @author Justin Hileman <justin@justinhileman.info>
*/
-abstract class LogicalOperator implements Proposition
+abstract class LogicalOperator extends PropositionOperator implements Proposition
{
- protected $propositions = array();
-
/**
- * Logical Operator constructor.
+ * array of propositions
*
- * @param array $props Initial Propositions to add to the Operator (default: null)
+ * @param array $props
*/
- public function __construct(array $props = null)
+ public function __construct(array $props = array())
{
- if ($props !== null) {
- foreach ($props as $prop) {
- $this->addProposition($prop);
- }
+ foreach ($props as $operand) {
+ $this->addOperand($operand);
}
}
-
- /**
- * Add a Proposition to the Operator.
- *
- * @param Proposition $prop Proposition to add to this Operator
- */
- public function addProposition(Proposition $prop)
- {
- $this->propositions[] = $prop;
- }
}
View
24 src/Ruler/Operator/LogicalOr.php
@@ -12,34 +12,34 @@
namespace Ruler\Operator;
use Ruler\Context;
+use Ruler\Proposition;
/**
* A logical OR operator.
*
- * @author Justin Hileman <justin@shopopensky.com>
- * @extends LogicalOperator
+ * @author Justin Hileman <justin@justinhileman.info>
*/
class LogicalOr extends LogicalOperator
{
/**
- * Evaluate whether any child Proposition evaluates to true given the current Context.
- *
- * @param Context $context Context with which to evaluate this ComparisonOperator
+ * @param Context $context Context with which to evaluate this Proposition
*
* @return boolean
*/
public function evaluate(Context $context)
{
- if (empty($this->propositions)) {
- throw new \LogicException('Logical Or requires at least one proposition');
- }
-
- foreach ($this->propositions as $prop) {
- if ($prop->evaluate($context) === true) {
+ /** @var Proposition $operand */
+ foreach ($this->getOperands() as $operand) {
+ if ($operand->evaluate($context) === true) {
return true;
}
}
return false;
}
-}
+
+ protected function getOperandCardinality()
+ {
+ return static::MULTIPLE;
+ }
+}
View
24 src/Ruler/Operator/LogicalXor.php
@@ -12,31 +12,26 @@
namespace Ruler\Operator;
use Ruler\Context;
+use Ruler\Proposition;
/**
* A logical XOR operator.
*
- * @author Justin Hileman <justin@shopopensky.com>
- * @extends LogicalOperator
+ * @author Justin Hileman <justin@justinhileman.info>
*/
class LogicalXor extends LogicalOperator
{
/**
- * Evaluate whether exactly one child Proposition evaluates to true given the current Context.
- *
- * @param Context $context Context with which to evaluate this ComparisonOperator
+ * @param Context $context Context with which to evaluate this Proposition
*
* @return boolean
*/
public function evaluate(Context $context)
{
- if (empty($this->propositions)) {
- throw new \LogicException('Logical Xor requires at least one proposition');
- }
-
$true = 0;
- foreach ($this->propositions as $prop) {
- if ($prop->evaluate($context) === true) {
+ /** @var Proposition $operand */
+ foreach ($this->getOperands() as $operand) {
+ if (true === $operand->evaluate($context)) {
if (++$true > 1) {
return false;
}
@@ -45,4 +40,9 @@ public function evaluate(Context $context)
return $true === 1;
}
-}
+
+ protected function getOperandCardinality()
+ {
+ return static::MULTIPLE;
+ }
+}
View
37 src/Ruler/Operator/Max.php
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Value;
+use Ruler\VariableOperand;
+
+/**
+ * A set max operator
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class Max extends VariableOperator implements VariableOperand
+{
+ public function prepareValue(Context $context)
+ {
+ /** @var VariableOperand $operand */
+ list($operand) = $this->getOperands();
+
+ return new Value($operand->prepareValue($context)->getSet()->max());
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::UNARY;
+ }
+}
View
37 src/Ruler/Operator/Min.php
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Value;
+use Ruler\VariableOperand;
+
+/**
+ * A set min operator
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class Min extends VariableOperator implements VariableOperand
+{
+ public function prepareValue(Context $context)
+ {
+ /** @var VariableOperand $operand */
+ list($operand) = $this->getOperands();
+
+ return new Value($operand->prepareValue($context)->getSet()->min());
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::UNARY;
+ }
+}
View
38 src/Ruler/Operator/Modulo.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Value;
+use Ruler\VariableOperand;
+
+/**
+ * A Modulo Arithmetic Operator
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class Modulo extends VariableOperator implements VariableOperand
+{
+ public function prepareValue(Context $context)
+ {
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return new Value($left->prepareValue($context)->modulo($right->prepareValue($context)));
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
+ }
+}
View
38 src/Ruler/Operator/Multiplication.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\VariableOperand;
+use Ruler\Value;
+
+/**
+ * A Multiplication Arithmetic Operator
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class Multiplication extends VariableOperator implements VariableOperand
+{
+ public function prepareValue(Context $context)
+ {
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return new Value($left->prepareValue($context)->multiply($right->prepareValue($context)));
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
+ }
+}
View
37 src/Ruler/Operator/Negation.php
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Ruler package, an OpenSky project.
+ *
+ * (c) 2011 OpenSky Project Inc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Ruler\Operator;
+
+use Ruler\Context;
+use Ruler\Value;
+use Ruler\VariableOperand;
+
+/**
+ * A Negation Math Operator
+ *
+ * @author Jordan Raub <jordan@raub.me>
+ */
+class Negation extends VariableOperator implements VariableOperand
+{
+ public function prepareValue(Context $context)
+ {
+ /** @var VariableOperand $operand */
+ list($operand) = $this->getOperands();
+
+ return new Value($operand->prepareValue($context)->negate());
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::UNARY;
+ }
+}
View
24 src/Ruler/Operator/NotEqualTo.php
@@ -12,24 +12,32 @@
namespace Ruler\Operator;
use Ruler\Context;
+use Ruler\Proposition;
+use Ruler\VariableOperand;
/**
* A NotEqualTo comparison operator.
*
- * @author Justin Hileman <justin@shopopensky.com>
- * @extends ComparisonOperator
+ * @author Justin Hileman <justin@justinhileman.info>
*/
-class NotEqualTo extends ComparisonOperator
+class NotEqualTo extends VariableOperator implements Proposition
{
/**
- * Evaluate whether the given variables are not equal in the current Context.
- *
- * @param Context $context Context with which to evaluate this ComparisonOperator
+ * @param Context $context Context with which to evaluate this Proposition
*
* @return boolean
*/
public function evaluate(Context $context)
{
- return $this->left->prepareValue($context)->equalTo($this->right->prepareValue($context)) === false;
+ /** @var VariableOperand $left */
+ /** @var VariableOperand $right */
+ list($left, $right) = $this->getOperands();
+
+ return $left->prepareValue($context)->equalTo($right->prepareValue($context)) === false;
+ }
+
+ protected function getOperandCardinality()
+ {
+ return static::BINARY;
}
-}
+