From a1aba68129d7d973a3d126285e28a190a7894579 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Wed, 14 Sep 2016 18:35:18 +0200 Subject: [PATCH 1/5] Initial source import (from POC) --- Exception/NoResult.php | 6 ++ Exception/RuleDoesNotValidate.php | 6 ++ Result.php | 72 +++++++++++++++++++++++ Rule.php | 21 +++++++ Rules.php | 97 +++++++++++++++++++++++++++++++ Rules/IfElseRule.php | 57 ++++++++++++++++++ Rules/IfRule.php | 57 ++++++++++++++++++ 7 files changed, 316 insertions(+) create mode 100644 Exception/NoResult.php create mode 100644 Exception/RuleDoesNotValidate.php create mode 100644 Result.php create mode 100644 Rule.php create mode 100644 Rules.php create mode 100644 Rules/IfElseRule.php create mode 100644 Rules/IfRule.php diff --git a/Exception/NoResult.php b/Exception/NoResult.php new file mode 100644 index 0000000..6d411f3 --- /dev/null +++ b/Exception/NoResult.php @@ -0,0 +1,6 @@ +name = $name; + $this->rule = $rule; + $this->result = $result; + } + + /** + * @param string $name + * + * @throws \Error + * + * @return mixed + */ + public function __get($name) + { + switch (strtolower($name)) { + case 'name': + return $name; + + case 'result': + if (is_object($this->result)) { + if ($this->result instanceof \Closure) { + return $this->result; + } + + return clone $this->result; + } + + return $this->result; + + case 'rule': + return clone $this->rule; + + default: + throw new Exception('Unable to get property ' . $name); + } + } +} diff --git a/Rule.php b/Rule.php new file mode 100644 index 0000000..11806c0 --- /dev/null +++ b/Rule.php @@ -0,0 +1,21 @@ +queue = new \SplPriorityQueue(); + } + + /** + * @param string $name + * @param Rule $rule + * @param int $priority + * + * @return Rules + */ + public function add($name, Rule $rule, $priority = -1) + { + $rules = clone $this; + + $rules->queue->insert([$name, $rule], $priority); + + return $rules; + } + + /** + * @param Ruler $ruler + * @param Context $context + * + * @throws NoResult + * + * @return Result + */ + public function getBestResult(Ruler $ruler, Context $context) + { + $queue = clone $this->queue; + + foreach ($queue as $nameAndRule) { + /** + * @var string $name + * @var Rule $rule + */ + list($name, $rule) = $nameAndRule; + + try { + $context[$name] = $rule->execute($ruler, $context); + } catch (RuleDoesNotValidate $exception) { + $context[$name] = null; + } + + if ($rule->valid($ruler, $context)) { + return new Result($name, $rule, $context[$name]); + } + } + + throw new NoResult(); + } + + /** + * @param Ruler $ruler + * @param array $context + * + * @return Result[] + */ + public function getAllResults(Ruler $ruler, Context $context) + { + $queue = clone $this->queue; + $outcomes = []; + + foreach ($queue as $nameAndRule) { + /** + * @var string $name + * @var Rule $rule + */ + list($name, $rule) = $nameAndRule; + + try { + $context[$name] = $rule->execute($ruler, $context); + } catch (RuleDoesNotValidate $exception) { + $context[$name] = null; + } + + $outcomes[] = new Result($name, $rule, $context[$name]); + } + + return $outcomes; + } +} diff --git a/Rules/IfElseRule.php b/Rules/IfElseRule.php new file mode 100644 index 0000000..c4109cc --- /dev/null +++ b/Rules/IfElseRule.php @@ -0,0 +1,57 @@ +rule = new IfRule($rule, $result); + $this->otherwise = $otherwise; + } + + /** + * @param Ruler $ruler + * @param Context $context + * + * @return bool + */ + public function valid(Ruler $ruler, Context $context) + { + return $this->rule->valid($ruler, $context); + } + + /** + * @param Ruler $ruler + * @param Context $context + * + * @return mixed + */ + public function execute(Ruler $ruler, Context $context) + { + try { + return $this->rule->execute($ruler, $context); + } catch (RuleDoesNotValidate $e) { + return $this->otherwise; + } + } +} diff --git a/Rules/IfRule.php b/Rules/IfRule.php new file mode 100644 index 0000000..2daea28 --- /dev/null +++ b/Rules/IfRule.php @@ -0,0 +1,57 @@ +rule = $rule; + $this->result = $result; + } + + /** + * @param Ruler $ruler + * @param Context $context + * + * @return bool + */ + public function valid(Ruler $ruler, Context $context) + { + return $ruler->assert($this->rule, $context); + } + + /** + * @param Ruler $ruler + * @param Context $context + * + * @return mixed + */ + public function execute(Ruler $ruler, Context $context) + { + if ($this->valid($ruler, $context)) { + return $this->result; + } + + throw new RuleDoesNotValidate('Rule ' . $this->rule . ' does not validate'); + } +} From c8b2ef51ae6ef8ba0fe824dd114728565d20c80f Mon Sep 17 00:00:00 2001 From: jubianchi Date: Wed, 14 Sep 2016 19:23:27 +0200 Subject: [PATCH 2/5] Cleaning --- Result.php | 34 +++++++++++++++ Rule.php | 41 ++++++++++++++++++ Rules.php | 43 ++++++++++++++++++ Rules/IfElseRule.php | 57 ------------------------ Rules/IfRule.php | 57 ------------------------ Rules/Then.php | 100 ++++++++++++++++++++++++++++++++++++++++++ Rules/ThenElse.php | 101 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 319 insertions(+), 114 deletions(-) delete mode 100644 Rules/IfElseRule.php delete mode 100644 Rules/IfRule.php create mode 100644 Rules/Then.php create mode 100644 Rules/ThenElse.php diff --git a/Result.php b/Result.php index e06c58d..62fec5a 100644 --- a/Result.php +++ b/Result.php @@ -1,5 +1,39 @@ rule = new IfRule($rule, $result); - $this->otherwise = $otherwise; - } - - /** - * @param Ruler $ruler - * @param Context $context - * - * @return bool - */ - public function valid(Ruler $ruler, Context $context) - { - return $this->rule->valid($ruler, $context); - } - - /** - * @param Ruler $ruler - * @param Context $context - * - * @return mixed - */ - public function execute(Ruler $ruler, Context $context) - { - try { - return $this->rule->execute($ruler, $context); - } catch (RuleDoesNotValidate $e) { - return $this->otherwise; - } - } -} diff --git a/Rules/IfRule.php b/Rules/IfRule.php deleted file mode 100644 index 2daea28..0000000 --- a/Rules/IfRule.php +++ /dev/null @@ -1,57 +0,0 @@ -rule = $rule; - $this->result = $result; - } - - /** - * @param Ruler $ruler - * @param Context $context - * - * @return bool - */ - public function valid(Ruler $ruler, Context $context) - { - return $ruler->assert($this->rule, $context); - } - - /** - * @param Ruler $ruler - * @param Context $context - * - * @return mixed - */ - public function execute(Ruler $ruler, Context $context) - { - if ($this->valid($ruler, $context)) { - return $this->result; - } - - throw new RuleDoesNotValidate('Rule ' . $this->rule . ' does not validate'); - } -} diff --git a/Rules/Then.php b/Rules/Then.php new file mode 100644 index 0000000..2dd2b11 --- /dev/null +++ b/Rules/Then.php @@ -0,0 +1,100 @@ +rule = $rule; + $this->result = $result; + } + + /** + * @param Ruler $ruler + * @param Context $context + * + * @return bool + */ + public function valid(Ruler $ruler, Context $context) + { + return $ruler->assert($this->rule, $context); + } + + /** + * @param Ruler $ruler + * @param Context $context + * + * @return mixed + */ + public function execute(Ruler $ruler, Context $context) + { + if ($this->valid($ruler, $context)) { + return $this->result; + } + + throw new RuleDoesNotValidate('Rule ' . $this->rule . ' does not validate'); + } +} diff --git a/Rules/ThenElse.php b/Rules/ThenElse.php new file mode 100644 index 0000000..159347e --- /dev/null +++ b/Rules/ThenElse.php @@ -0,0 +1,101 @@ +rule = new Then($rule, $result); + $this->otherwise = $otherwise; + } + + /** + * @param Ruler $ruler + * @param Context $context + * + * @return bool + */ + public function valid(Ruler $ruler, Context $context) + { + return $this->rule->valid($ruler, $context); + } + + /** + * @param Ruler $ruler + * @param Context $context + * + * @return mixed + */ + public function execute(Ruler $ruler, Context $context) + { + try { + return $this->rule->execute($ruler, $context); + } catch (RuleDoesNotValidate $e) { + return $this->otherwise; + } + } +} From 0dfee6bc45b5e829e7ec63d55db3c78c5c370910 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Wed, 14 Sep 2016 22:30:37 +0200 Subject: [PATCH 3/5] Implement a `rule()` function to reference the result of one rule from another rule --- Result.php | 5 ++--- Rules.php | 45 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/Result.php b/Result.php index 62fec5a..4245d3c 100644 --- a/Result.php +++ b/Result.php @@ -36,7 +36,6 @@ namespace Hoa\Ruler; -use Hoa\Ruler\Exception\Exception; /** * @property string name @@ -58,7 +57,7 @@ class Result /** * @var mixed */ - private $outcome; + private $result; /** * @param string $name @@ -100,7 +99,7 @@ public function __get($name) return clone $this->rule; default: - throw new Exception('Unable to get property ' . $name); + throw new Exception\Exception('Unable to get property ' . $name); } } } diff --git a/Rules.php b/Rules.php index 34d121f..f5f9da7 100644 --- a/Rules.php +++ b/Rules.php @@ -85,6 +85,7 @@ public function add($name, Rule $rule, $priority = -1) */ public function getBestResult(Ruler $ruler, Context $context) { + $ruler = self::initializeRuler($ruler, $context); $queue = clone $this->queue; foreach ($queue as $nameAndRule) { @@ -95,13 +96,13 @@ public function getBestResult(Ruler $ruler, Context $context) list($name, $rule) = $nameAndRule; try { - $context[$name] = $rule->execute($ruler, $context); + $context['#' . $name] = $rule->execute($ruler, $context); } catch (RuleDoesNotValidate $exception) { - $context[$name] = null; + $context['#' . $name] = null; } if ($rule->valid($ruler, $context)) { - return new Result($name, $rule, $context[$name]); + return new Result($name, $rule, $context['#' . $name]); } } @@ -109,15 +110,16 @@ public function getBestResult(Ruler $ruler, Context $context) } /** - * @param Ruler $ruler - * @param array $context + * @param Ruler $ruler + * @param Context $context * * @return Result[] */ public function getAllResults(Ruler $ruler, Context $context) { - $queue = clone $this->queue; - $outcomes = []; + $ruler = self::initializeRuler($ruler, $context); + $queue = clone $this->queue; + $results = []; foreach ($queue as $nameAndRule) { /** @@ -127,14 +129,35 @@ public function getAllResults(Ruler $ruler, Context $context) list($name, $rule) = $nameAndRule; try { - $context[$name] = $rule->execute($ruler, $context); + $context['#' . $name] = $rule->execute($ruler, $context); } catch (RuleDoesNotValidate $exception) { - $context[$name] = null; + $context['#' . $name] = null; } - $outcomes[] = new Result($name, $rule, $context[$name]); + $results[] = new Result($name, $rule, $context['#' . $name]); + } + + return $results; + } + + /** + * @param Ruler $ruler + * @param array $context + * + * @return Ruler + */ + private static function initializeRuler(Ruler $ruler, Context $context) + { + if ($ruler->getDefaultAsserter()->operatorExists('rule')) { + return $ruler; } - return $outcomes; + $ruler = clone $ruler; + + $ruler->getDefaultAsserter()->setOperator('rule', function ($id) use ($context) { + return $context['#' . $id]; + }); + + return $ruler; } } From 0219b662bae1c6c886d505e3645abde844c1e016 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Wed, 14 Sep 2016 22:36:59 +0200 Subject: [PATCH 4/5] Ensure there is not conflict between rule names and identifiers in context --- Rules.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Rules.php b/Rules.php index f5f9da7..aed5166 100644 --- a/Rules.php +++ b/Rules.php @@ -96,13 +96,13 @@ public function getBestResult(Ruler $ruler, Context $context) list($name, $rule) = $nameAndRule; try { - $context['#' . $name] = $rule->execute($ruler, $context); + $context['.' . $name] = $rule->execute($ruler, $context); } catch (RuleDoesNotValidate $exception) { - $context['#' . $name] = null; + $context['.' . $name] = null; } if ($rule->valid($ruler, $context)) { - return new Result($name, $rule, $context['#' . $name]); + return new Result($name, $rule, $context['.' . $name]); } } @@ -129,12 +129,12 @@ public function getAllResults(Ruler $ruler, Context $context) list($name, $rule) = $nameAndRule; try { - $context['#' . $name] = $rule->execute($ruler, $context); + $context['.' . $name] = $rule->execute($ruler, $context); } catch (RuleDoesNotValidate $exception) { - $context['#' . $name] = null; + $context['.' . $name] = null; } - $results[] = new Result($name, $rule, $context['#' . $name]); + $results[] = new Result($name, $rule, $context['.' . $name]); } return $results; @@ -154,8 +154,8 @@ private static function initializeRuler(Ruler $ruler, Context $context) $ruler = clone $ruler; - $ruler->getDefaultAsserter()->setOperator('rule', function ($id) use ($context) { - return $context['#' . $id]; + $ruler->getDefaultAsserter()->setOperator('rule', function($id) use ($context) { + return $context['.' . $id]; }); return $ruler; From 2e8f5138f702c4fe9bf0d75a555ffa8f6ad16962 Mon Sep 17 00:00:00 2001 From: jubianchi Date: Fri, 5 Jan 2018 21:51:05 +0100 Subject: [PATCH 5/5] Use hoa/heap instead of SplPriorityQueue --- Rules.php | 17 +++++++---------- composer.json | 1 + 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Rules.php b/Rules.php index aed5166..dc6f09a 100644 --- a/Rules.php +++ b/Rules.php @@ -36,6 +36,7 @@ namespace Hoa\Ruler; +use Hoa\Heap\Max; use Hoa\Ruler\Exception\NoResult; use Hoa\Ruler\Exception\RuleDoesNotValidate; @@ -50,13 +51,13 @@ class Rules { /** - * @var \SplPriorityQueue + * @var Max */ private $queue; public function __construct() { - $this->queue = new \SplPriorityQueue(); + $this->queue = new Max(); } /** @@ -68,11 +69,9 @@ public function __construct() */ public function add($name, Rule $rule, $priority = -1) { - $rules = clone $this; + $this->queue->insert([$name, $rule], $priority); - $rules->queue->insert([$name, $rule], $priority); - - return $rules; + return $this; } /** @@ -86,9 +85,8 @@ public function add($name, Rule $rule, $priority = -1) public function getBestResult(Ruler $ruler, Context $context) { $ruler = self::initializeRuler($ruler, $context); - $queue = clone $this->queue; - foreach ($queue as $nameAndRule) { + foreach ($this->queue as $nameAndRule) { /** * @var string $name * @var Rule $rule @@ -118,10 +116,9 @@ public function getBestResult(Ruler $ruler, Context $context) public function getAllResults(Ruler $ruler, Context $context) { $ruler = self::initializeRuler($ruler, $context); - $queue = clone $this->queue; $results = []; - foreach ($queue as $nameAndRule) { + foreach ($this->queue as $nameAndRule) { /** * @var string $name * @var Rule $rule diff --git a/composer.json b/composer.json index 2a393fc..ec155ec 100644 --- a/composer.json +++ b/composer.json @@ -28,6 +28,7 @@ "hoa/consistency": "~1.0", "hoa/exception" : "~1.0", "hoa/file" : "~1.0", + "hoa/heap" : "~0.0@dev", "hoa/protocol" : "~1.0", "hoa/visitor" : "~2.0" },