Showing preview only (327K chars total). Download the full file or copy to clipboard to get everything.
Repository: hoaproject/Ruler
Branch: master
Commit: 427d20077d6a
Files: 46
Total size: 310.5 KB
Directory structure:
gitextract_hj2vqy6x/
├── .State
├── .gitignore
├── .travis.yml
├── Bin/
│ └── Assert.php
├── CHANGELOG.md
├── Context.php
├── Documentation/
│ ├── En/
│ │ └── Index.xyl
│ └── Fr/
│ └── Index.xyl
├── DynamicCallable.php
├── Exception/
│ ├── Asserter.php
│ ├── Exception.php
│ └── Interpreter.php
├── Grammar.pp
├── Model/
│ ├── Bag/
│ │ ├── Bag.php
│ │ ├── Context.php
│ │ ├── RulerArray.php
│ │ └── Scalar.php
│ ├── Model.php
│ └── Operator.php
├── README.md
├── Ruler.php
├── Test/
│ ├── Integration/
│ │ ├── Documentation.php
│ │ └── Model/
│ │ └── Operator.php
│ └── Unit/
│ ├── Context.php
│ ├── DynamicCallable.php
│ ├── Exception/
│ │ ├── Asserter.php
│ │ ├── Exception.php
│ │ └── Interpreter.php
│ ├── Issue.php
│ ├── Model/
│ │ ├── Bag/
│ │ │ ├── Bag.php
│ │ │ ├── Context.php
│ │ │ ├── RulerArray.php
│ │ │ └── Scalar.php
│ │ ├── Model.php
│ │ └── Operator.php
│ ├── Ruler.php
│ └── Visitor/
│ ├── Asserter.php
│ ├── Compiler.php
│ ├── Disassembly.php
│ └── Interpreter.php
├── Visitor/
│ ├── Asserter.php
│ ├── Compiler.php
│ ├── Disassembly.php
│ └── Interpreter.php
├── bors.toml
└── composer.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .State
================================================
finalized
================================================
FILE: .gitignore
================================================
/vendor/
/composer.lock
================================================
FILE: .travis.yml
================================================
language: php
branches:
only:
- staging
- trying
- master
matrix:
include:
- php: 5.5
- php: 5.6
- php: 7.0
- php: 7.1
env:
- ENABLE_XDEBUG=true
- php: 7.1
env:
- ENABLE_DEVTOOLS=true
- php: nightly
allow_failures:
- php: nightly
fast_finish: true
os:
- linux
notifications:
irc: "chat.freenode.net#hoaproject"
sudo: false
env:
global:
- secure: "AAAAB3NzaC1yc2EAAAADAQABAAAAgQDzZax7/VFMmTnePlw4PQmD7pVaJQDbLaMXfIIuV/h51m0g8dYfWiGytsblv+/tW37b3TKaGVLMP3vL9jGU73V4P64Ytafj0UV3UKSzHR4atrAPjsCjsFMzlIvKLCZf2FmADRspv/pAg1loQWRzXAiZ9pqCSTxx32x20uLAJmLucQ=="
cache:
directories:
- vendor/
before_script:
- export PATH="$PATH:$HOME/.composer/vendor/bin"
- if [[ ! $ENABLE_XDEBUG ]]; then
phpenv config-rm xdebug.ini || echo "ext-xdebug is not available, cannot remove it.";
fi
script:
- composer install
- vendor/bin/hoa test:run
- if [[ $ENABLE_DEVTOOLS ]]; then
composer global require friendsofphp/php-cs-fixer;
vendor/bin/hoa devtools:cs --diff --dry-run .;
fi
================================================
FILE: Bin/Assert.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Bin;
use Hoa\Console;
use Hoa\Ruler;
/**
* Class \Hoa\Ruler\Bin\Assert.
*
* Assert rules.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Assert extends Console\Dispatcher\Kit
{
/**
* Options description.
*
* @var array
*/
protected $options = [
['help', Console\GetOption::NO_ARGUMENT, 'h'],
['help', Console\GetOption::NO_ARGUMENT, '?']
];
/**
* The entry method.
*
* @return int
*/
public function main()
{
$ruler = new Ruler();
$context = new Ruler\Context();
while (false !== $c = $this->getOption($v)) {
switch ($c) {
case '__ambiguous':
$context[$v['option']] = $v['value'];
break;
case 'h':
case '?':
return $this->usage();
}
}
$this->parser->listInputs($rule);
if (empty($rule)) {
return $this->usage();
}
return (int) (!$ruler->assert($rule, $context));
}
/**
* The command usage.
*
* @return int
*/
public function usage()
{
echo
'Usage : ruler:assert <options> rule', "\n",
'Options :', "\n",
$this->makeUsageOptionsList([
'help' => 'This help.'
]), "\n",
'Example : -x=2 -y=6 \'x in [1, 2, 4] and x < y\'.', "\n",
'See $? to see the result (0 for true, > 0 for false).', "\n";
return;
}
}
__halt_compiler();
Assert rules.
================================================
FILE: CHANGELOG.md
================================================
# 2.17.05.16
* Asserter: Can visit array-like dimensions. (Arne Groskurth, 2017-05-16T09:42:29+02:00)
# 2.17.04.26
* Grammar: Logical operators are left-associative. (Ivan Enderlin, 2017-03-24T14:39:19+01:00)
* CS: Fix copyright. (Ivan Enderlin, 2017-03-13T14:59:05+01:00)
* Test: Support PHP 5.x syntax. (Ivan Enderlin, 2017-03-13T14:44:33+01:00)
* CI: Set up Travis. (Ivan Enderlin, 2017-03-13T14:16:45+01:00)
# 2.17.01.13
* Quality: Happy new year! (Alexis von Glasow, 2017-01-09T21:37:11+01:00)
* Test: Add the `Decorrelated` interface. (Ivan Enderlin, 2016-10-25T07:57:09+02:00)
# 2.16.10.24
* Documentation: New `README.md` file. (Ivan Enderlin, 2016-10-14T23:10:14+02:00)
* Grammar: Chain dimensions on function. (Ivan Enderlin, 2016-10-14T08:37:12+02:00)
* Documentation: Update `support` properties. (Ivan Enderlin, 2016-10-11T11:54:40+02:00)
* Test: Update according to previous commit. (Ivan Enderlin, 2016-09-09T16:55:21+02:00)
* Disassembly: Always add parenthesis around operators. (Ivan Enderlin, 2016-01-16T08:09:28+01:00)
* Test: Add test cases for `…uler\Visitor\Asserter`. (Ivan Enderlin, 2016-09-09T08:04:49+02:00)
# 2.16.09.07
* Test: Write `…\Ruler\Visitor\Asserter` test suite. (Ivan Enderlin, 2016-09-07T15:06:46+02:00)
* Asserter: Fix an exception message. (Ivan Enderlin, 2016-09-07T15:04:19+02:00)
* Test: Write `…ler\Visitor\Interpreter` test suite. (Ivan Enderlin, 2016-09-06T08:54:00+02:00)
* Quality: Rename an internal variable. (Ivan Enderlin, 2016-09-06T08:02:30+02:00)
* Test: Parameterized cases are usually protected. (Ivan Enderlin, 2016-09-06T08:00:43+02:00)
* Disassembly: Escape the escaping symbol. (Ivan Enderlin, 2016-09-05T17:23:26+02:00)
* Test: Write `…ler\Visitor\Disassembly` test suite. (Ivan Enderlin, 2016-09-05T17:22:16+02:00)
* Test: Write `…\Ruler\Visitor\Compiler` test suite. (Ivan Enderlin, 2016-09-05T17:15:41+02:00)
* Test: Write `Hoa\Ruler\Ruler` test suite. (Ivan Enderlin, 2016-09-05T08:56:36+02:00)
* Ruler: Rename a namespace alias. (Ivan Enderlin, 2016-09-05T08:19:19+02:00)
* Ruler: Remove the `interprete` method. (Ivan Enderlin, 2016-09-05T08:14:21+02:00)
* Test: Update name for `Issue`. (Ivan Enderlin, 2016-09-05T08:10:18+02:00)
* Quality: Run `hoa devtools:cs`. (Ivan Enderlin, 2016-09-05T08:09:13+02:00)
* Test: Write `…r\Exception\Interpreter` test suite. (Ivan Enderlin, 2016-09-05T08:08:20+02:00)
* Test: Write `…uler\Exception\Asserter` test suite. (Ivan Enderlin, 2016-09-05T08:07:31+02:00)
* Test: Write `…ler\Exception\Exception` test suite. (Ivan Enderlin, 2016-09-05T08:05:36+02:00)
* Test: Format to standard vocabulary. (Ivan Enderlin, 2016-09-05T08:04:39+02:00)
* Test: Rename `CUT` to `SUT`. (Ivan Enderlin, 2016-09-05T08:02:49+02:00)
* Test: Move `Documentation` as integration suite. (Ivan Enderlin, 2016-09-05T08:01:25+02:00)
* Test: Write `Hoa\Ruler\Model\Model` test suite. (Ivan Enderlin, 2016-09-02T17:40:35+02:00)
* Visitor: If the model is empty, compile to `''`. (Ivan Enderlin, 2016-09-02T17:34:27+02:00)
* Test: Ensure recursivity applies onto array items. (Ivan Enderlin, 2016-09-02T17:19:51+02:00)
* Test: Write `Hoa\Ruler\Model\Operator` test suite. (Ivan Enderlin, 2016-09-02T17:09:31+02:00)
* Model: Use the public `getName` method. (Ivan Enderlin, 2016-09-02T17:08:48+02:00)
* Model: Move set auto laziness to `setName`. (Ivan Enderlin, 2016-09-02T17:08:02+02:00)
* Test: Move `…erator` as unit to integration suite. (Ivan Enderlin, 2016-09-02T07:49:40+02:00)
* Documentation: Fix API documentation. (Ivan Enderlin, 2016-09-02T07:47:03+02:00)
* Test: Write `…Ruler\Model\Bag\Context` test suite. (Ivan Enderlin, 2016-09-02T07:46:09+02:00)
* Quality: Remove an unnecessary namespace alias. (Ivan Enderlin, 2016-08-30T17:03:58+02:00)
* Test: Write `…er\Model\Bag\RulerArray` test suite. (Ivan Enderlin, 2016-08-30T17:03:38+02:00)
* Test: Write `…\Ruler\Model\Bag\Scalar` test suite. (Ivan Enderlin, 2016-08-29T16:29:37+02:00)
* Documentation: Fix API documentation. (Ivan Enderlin, 2016-08-29T16:29:16+02:00)
* Test: Write `Hoa\Ruler\Model\Bag` test suite. (Ivan Enderlin, 2016-08-29T15:51:28+02:00)
* Test: Use the `::class` constant. (Ivan Enderlin, 2016-08-29T15:49:09+02:00)
# 2.16.04.06
* Asserter: Fix a wrong namespace access. (jroenf, 2016-04-06T09:09:43+02:00)
# 2.16.03.15
* Composer: `hoa/protocol` is explicitly required. (Ivan Enderlin, 2016-01-18T22:14:18+01:00)
* Grammar: Update copyright. (Ivan Enderlin, 2016-01-17T14:22:07+01:00)
# 2.16.01.15
* Composer: Remove a useless dependency. (Ivan Enderlin, 2016-01-14T22:42:15+01:00)
# 2.16.01.14
* Composer: New stable libraries. (Ivan Enderlin, 2016-01-14T22:13:39+01:00)
# 2.16.01.11
* Quality: Drop PHP5.4. (Ivan Enderlin, 2016-01-11T09:15:26+01:00)
* Quality: Run devtools:cs. (Ivan Enderlin, 2016-01-09T09:08:44+01:00)
* Core: Remove `Hoa\Core`. (Ivan Enderlin, 2016-01-09T08:24:06+01:00)
* Consistency: Use `Hoa\Consistency`. (Ivan Enderlin, 2015-12-08T21:50:12+01:00)
* Exception: Use `Hoa\Exception`. (Ivan Enderlin, 2015-11-20T13:10:38+01:00)
# 1.15.11.09
* Fix CS. (Ivan Enderlin, 2015-09-23T11:31:44+02:00)
# 1.15.09.22
* Fix bad evaluation of `null` as an array key in the asserter. (Grummfy, 2015-09-22T15:47:23+02:00)
# 1.15.09.08
* Add a `.gitignore` file. (Stéphane HULARD, 2015-08-03T11:45:52+02:00)
* Add the `matches` operator. (Kévin Gomez, 2015-07-18T19:44:24+02:00)
# 1.15.07.28
* Auto-box the expression to its bag representation. (Ivan Enderlin, 2015-07-13T11:37:00+02:00)
* Fix a typo. (Ivan Enderlin, 2015-06-27T16:08:06+02:00)
# 1.15.05.29
* Move to PSR-1 and PSR-2. (Ivan Enderlin, 2015-04-20T10:21:13+02:00)
# 1.15.04.13
* Add the English documentation. (Ivan Enderlin, 2015-03-19T10:32:47+01:00)
* Add the French documentation. (Ivan Enderlin, 2015-03-19T10:29:59+01:00)
* Add the `CHANGELOG.md` file. (Ivan Enderlin, 2015-02-16T14:08:39+01:00)
* Fix CS and API documentation. (Ivan Enderlin, 2015-02-06T10:37:23+01:00)
* Add lazy operator support. (Alexis von Glasow, 2014-12-15T23:42:34+01:00)
* Add tests for the dynamic callable. (Ivan Enderlin, 2015-02-05T17:13:12+01:00)
# 1.15.02.05
* Sandbox function calls in the context. (Ivan Enderlin, 2015-02-05T16:50:13+01:00)
* Add tests for the context. (Ivan Enderlin, 2015-02-05T16:49:30+01:00)
# 1.15.02.02
* s/interprete/interpret/ (Ivan Enderlin, 2015-02-02T11:31:29+01:00)
* `Ruler::interprete` is an alias to `Ruler::interpret` (simkimsia, 2015-01-16T22:22:18+08:00)
* Improve type-hints in `Visitor\Asserter` (Alexis von Glasow, 2015-01-15T13:34:30+01:00)
* Happy new year! (Ivan Enderlin, 2015-01-05T14:47:59+01:00)
# 1.14.12.10
* Move to PSR-4. (Ivan Enderlin, 2014-12-09T18:45:18+01:00)
# 1.14.12.09
* Fix a bug in the `Visitor\Compiler` when function has no argument (Catalin Criste, 2014-12-09T18:25:25+01:00)
* Format namespace. (Ivan Enderlin, 2014-12-08T14:04:08+01:00)
* Require `hoa/test`. (Alexis von Glasow, 2014-11-26T13:21:41+01:00)
* `Hoa\Visitor` has been finalized. (Ivan Enderlin, 2014-11-15T22:28:07+01:00)
# 1.14.11.10
* Avoid collisions with user-defined operators… (Ivan Enderlin, 2014-11-10T15:43:04+01:00)
# 1.14.11.09
* Split the visitor into several methods (Stéphane PY, 2014-11-07T09:29:55+01:00)
* Add tests for the documentation. (Ivan Enderlin, 2014-09-26T09:23:44+02:00)
# 1.14.09.25
* Fix Fatal error. (Stéphane PY, 2014-09-25T12:22:18+02:00)
* Add `branch-alias` (Stéphane PY, 2014-09-23T16:06:06+02:00)
* `Hoa\Core` was missing (Ivan Enderlin, 2014-09-23T15:58:55+02:00)
# 1.14.09.23
* First tag :-). (Ivan Enderlin, 2014-09-23T15:41:11+02:00)
* Finalized! (Ivan Enderlin, 2014-09-23T15:37:04+02:00)
* Remove `from`/`import` and update to PHP5.4. (Ivan Enderlin, 2014-09-23T15:32:36+02:00)
* Declare array with […] and not (…). (Ivan Enderlin, 2014-09-23T14:58:18+02:00)
# 0.14.09.17
* Drop PHP5.3. (Ivan Enderlin, 2014-09-17T17:13:16+02:00)
* Add the installation section. (Ivan Enderlin, 2014-09-17T17:13:05+02:00)
(first snapshot)
================================================
FILE: Context.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler;
/**
* Class \Hoa\Ruler\Context.
*
* Context of a rule, it contains data to instanciate the rule.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Context implements \ArrayAccess
{
/**
* Ruler.
*
* @var \Hoa\Ruler
*/
protected $_ruler = null;
/**
* Data.
*
* @var array
*/
protected $_data = [];
/**
* Constructor.
*
* @param array $data Initial data.
*/
public function __construct(array $data = [])
{
$this->_data = $data;
return;
}
/**
* Set a data.
*
* @param string $id ID.
* @param mixed $value Value.
* @return void
*/
public function offsetSet($id, $value)
{
$this->_data[$id] = $value;
return;
}
/**
* Get a data.
*
* @param string $id ID.
* @return mixed
* @throws \Hoa\Ruler\Exception
*/
public function offsetGet($id)
{
if (false === array_key_exists($id, $this->_data)) {
throw new Exception(
'Identifier %s does not exist in the context.',
0,
$id
);
}
$value = $this->_data[$id];
if ($value instanceof DynamicCallable) {
return $value($this);
}
if (true === is_callable($value)) {
if (true === is_string($value) &&
false === in_array(strtolower($value), get_defined_functions()['user'])) {
return $value;
}
$value = $this->_data[$id] = $value($this);
}
return $value;
}
/**
* Check if a data exists.
*
* @return bool
*/
public function offsetExists($id)
{
return true === array_key_exists($id, $this->_data);
}
/**
* Unset a data.
*
* @param string $id ID.
* @return void
*/
public function offsetUnset($id)
{
unset($this->_data[$id]);
return;
}
/**
* Get a data as context property
*
* @param string $name
* @return mixed
* @throws \Hoa\Ruler\Exception
*/
public function __get($name)
{
return $this->offsetGet($name);
}
/**
* Set a data as context property
*
* @param string $id ID.
* @param mixed $value Value.
* @return void
*/
public function __set($id, $value)
{
$this->offsetSet($id, $value);
}
}
================================================
FILE: Documentation/En/Index.xyl
================================================
<?xml version="1.0" encoding="utf-8"?>
<overlay xmlns="http://hoa-project.net/xyl/xylophone">
<yield id="chapter">
<p><strong>Business rules</strong> (like “<q>all the customers that have
reached 100€ in one time receive a voucher of 10% on the next purchase</q>”)
are most of the time defined <strong>outside</strong> the application. They
are even often written in a <strong>different language</strong> than the used
programming language. The <code>Hoa\Ruler</code> library provides an engine
allowing to simply execute business rules while being
<strong>efficient</strong> and very <strong>extensible</strong>.</p>
<h2 id="Table_of_contents">Table of contents</h2>
<tableofcontents id="main-toc" />
<h2 id="Introduction" for="main-toc">Introduction</h2>
<p>The business logic is very different from the Computer logic. “<q>All the
customers that have reached 100€ in one time receive a voucher of 10% on the
next purchase</q>”. This rule allows to access to certain parts of the program
if <strong>valids</strong>. However, it can <strong>change</strong> at any
moment. Most of the time, in a team, it is not the role of the developer to
implement this rule. It will probably come from a <strong>business rules
repository</strong>, which have been written by other persons, either
<strong>manually</strong> or thanks to a <strong>third-party program</strong>.
This implies that the language used to express a rule is not the language used
to develop the program. An even more obvious example is the use of a rule to
filter elements: An element is accepted if “<q>its group is <em>customer</em>
or <em>guest</em> and its number of points is greater than 30</q>”. This rule
can be written by a user via a command line interface to filter results from a
database or logs.</p>
<p>This is important to understand that rules must be written in a
<strong>dedicated language</strong>. Nevertheless, the way we use rules is
very <strong>vast</strong> and <strong>unpredictable</strong>. This is why it
is primordial to have flexible and <strong>extensible</strong> rules in the
syntax. For instance, it should be allowed to add operators and functions:
“<q>All the customers from the hotel with a Gold pass will receive a voucher
of 10%”</q>. The “Gold pass” can be an operator or a function specific to the
current business.</p>
<p>The language the <code>Hoa\Ruler</code> library uses to describe rules
respects these constraints of extensibility. The rules will not be close to
the human language but they will stay <strong>natural</strong> when reading.
If we take the example of the “<q>its group is <em>customer</em> or
<em>guest</em> and its number of points is greater than 30</q>”, it will be
written: <code><em>group</em> in ["customer", "guest"] and <em>points</em> >
30</code>. The <code><em>group</em></code> and <code><em>points</em></code>
elements are variables of the rule. Their values will be defined in a
context.</p>
<p>From a more formal point of view, a rule is a predicate, it means that its
result is always a boolean: <code class="language-php">true</code> or
<code class="language-php">false</code>. Because these rules are likely to be
manipulated (modified) and executed, the <code>Hoa\Ruler</code> library
provides several tools to work efficiently with these constraints, presented
in the following sections.</p>
<h2 id="Global_workflow" for="main-toc">Global workflow</h2>
<p>The global workflow of the <code>Hoa\Ruler</code> library follows
3 steps:</p>
<ol>
<li>Defining a <strong>rule</strong>,</li>
<li>Defining a <strong>context</strong>,</li>
<li>Use of an <strong>asserter</strong> for the execution.</li>
</ol>
<p>The <strong>rule</strong> is a string matching a specific syntax, which is
described by the grammar of the language defined by the <code>Hoa\Ruler</code>
library (detailed hereinafter). This rule contains variables whose values are
defined by the <strong>context</strong>. The context can contain scalar
values, arrays or even functions or objects. Finally, the
<strong>asserter</strong> associates the context to the rule in order to
execute it and to obtain a result. We remind you about the result which is
necessarily a boolean. This is therefore a predicate.</p>
<p>The context is represented by the <code>Hoa\Ruler\Context</code> class. The
asserter is represented by the <code>Hoa\Ruler\Visitor\Asserter</code> class.
We can use the <code>Hoa\Ruler\Ruler::assert</code> method to ease its usage.
Thus:</p>
<pre><code class="language-php">$ruler = new Hoa\Ruler\Ruler();
// 1. Write a rule.
$rule = 'group in ["customer", "guest"] and points > 30';
// 2. Create a context.
$context = new Hoa\Ruler\Context();
$context['group'] = 'customer';
$context['points'] = function () {
return 42;
};
// 3. Assert!
var_dump(
$ruler->assert($rule, $context)
);
/**
* Will output:
* bool(true)
*/</code></pre>
<p>The rule is defined in the <code class="language-php">$rule</code>
variable. The context, as far as it is concerned, is defined in the
<code class="language-php">$context</code> variable. It contains 2 variables:
<code>group</code> and <code>point</code>, respectively with the
<code class="language-php">'customer'</code> and
<code class="language-php">42</code> (returned by a function) values. Finally,
the last step uses the <code>Hoa\Ruler\Ruler::assert</code> method to execute
the <code class="language-php">$rule</code> rule with the
<code class="language-php">$context</code> context (this latter is optional).
The result is <code class="language-php">true</code> because
<code>group</code> belongs to the list <code>customer</code> or
<code>guest</code>, and <code>point</code> is greater than 30. Change the
values in the context or the rule to observe a different result.</p>
<p>The following sections detail the behavior of each step but the classical
usage remains as simple as that!</p>
<h3 id="Grammar" for="main-toc">Grammar (through examples)</h3>
<p>The grammar of the rules language is described in
<a href="@central_resource:path=Library/Ruler/Grammar.pp">the
<code>hoa://Library/Ruler/Grammar.pp</code> file</a>. This grammar is
expressed with the PP language. To get more information, please see
<a href="@hack:chapter=Compiler">the <code>Hoa\Compiler</code> library</a>.
We clarify that the language supports Unicode. We are not going to explain the
language whilst the grammar provides all the necessary
<strong>details</strong>. Nevertheless, we are going to give several syntax
examples.</p>
<table>
<caption>Language of <code>Hoa\Ruler</code> through examples.</caption>
<thead>
<tr>
<th>syntax</th>
<th>semantics</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>'foo'</code>, <code>"foo"</code>, <code>'f\'oo'</code></td>
<td>strings</td>
</tr>
<tr>
<td><code>true</code>, <code>false</code>, <code>null</code></td>
<td>pre-defined constants</td>
</tr>
<tr>
<td><code>4.2</code></td>
<td>a real</td>
</tr>
<tr>
<td><code>42</code></td>
<td>an integer</td>
</tr>
<tr>
<td><code>['foo', true, 4.2, 42]</code></td>
<td>an array (heterogeneous)</td>
</tr>
<tr>
<td><code>sum(1, 2, 3)</code></td>
<td>a call to the <code>sum</code> function with 3 arguments</td>
</tr>
<tr>
<td><code><em>points</em></code></td>
<td>a variable</td>
</tr>
<tr>
<td><code><em>points</em>['x']</code></td>
<td>an array access</td>
</tr>
<tr>
<td><code><em>line</em>.pointA</code></td>
<td>an object access (attribute)</td>
</tr>
<tr>
<td><code><em>line</em>.length()</code></td>
<td>a call to a method</td>
</tr>
<tr>
<td><code>and</code>, <code>or</code>, <code>xor</code>, <code>not</code></td>
<td>logical operators</td>
</tr>
<tr>
<td><code>=</code>, <code>!=</code>, <code>></code>, <code>&lt;</code>,
<code>>=</code>, <code>&lt;=</code></td>
<td>comparison operators</td>
</tr>
<tr>
<td><code>is</code>, <code>in</code></td>
<td>membership operators</td>
</tr>
</tbody>
</table>
<p>Of course, these examples represent atomic parts of the grammar that we can
combine. Thus:
<code><em>userA</em>.allows(<em>groups</em>[<em>groupId</em>][<em>userB</em>])</code>
is valid. Just like <code>f(<em>user</em>, <em>points</em> > 7 and
<em>points</em> &lt; 42)</code> which is also valid.</p>
<p>In actual fact, functions, comparison operators and membership operators
are not defined by the grammar but by the asserter (detailed hereinafter).
<code>Hoa\Ruler</code> does not make any difference between an operator and a
function. <strong>Operators are considered as functions</strong>; an operator
only being a function with an arity of 1 or 2. Thus, we can write <code>2 =
2</code> or <code>=(2, 2)</code>, this will strictly produce the same result.
Just like the name of functions that is not defined in the grammar, the name
of operators is neither defined by the grammar, excepted for the logical
operators that have a particular processing (because of the operator
precedence). The immediate result is that we can <strong>create</strong> our
own operators or functions. We can imagine <code><em>a</em> ∈
<em>A</em></code>, <code>√(42)</code> or even <code><em>userA</em> allows
<em>userB</em></code> being valid expressions.</p>
<h3 id="Context" for="main-toc">Context</h3>
<p>The context defines values of variables present in rules. These values can
be of kind:</p>
<ul>
<li><strong>Constants</strong>, like <code class="language-php">42</code> or
<code class="language-php">'foo'</code> which are scalars, or
<code class="language-php">[1, 1, 2, 3, 5]</code> or an object which are
structured types,</li>
<li><strong>Computed values</strong>, it means returned by a function or a
method.</li>
</ul>
<p>By default, the computed values are computed only once. Indeed, they are
stored in a cache for performance reasons. If we would like to recompute them
at each read, an encapsulation in an object of kind
<code>Hoa\Ruler\DynamicCallable</code> is required.</p>
<p>Before detailing this part, let's present the context. A context is an
instance of the <code>Hoa\Ruler\Context</code> class which implements
<a href="http://php.net/arrayaccess">the <code>ArrayAccess</code>
interface</a>. Thus, we use the context like an array:</p>
<pre><code class="language-php">$context = new Hoa\Ruler\Context();
$context['key'] = 'value';
var_dump(
isset($context['key']),
$context['key']
);
/**
* Will output:
* bool(true)
* string(5) "value"
*/</code></pre>
<p>To register computed values, we can use a function; thus:</p>
<pre><code class="language-php">$context['computed'] = function () {
return 42;
};
var_dump($context['computed']);
/**
* Will output:
* int(42)
*/</code></pre>
<p>We said computed values are stored in a <strong>cache</strong> on the first
read. To illustrate this we will use a function incrementing an integer at
each call:</p>
<pre><code class="language-php">$i = 0;
$context['int'] = function () use (&amp;$i) {
return ++$i;
};
var_dump(
$context['int'],
$context['int'],
$context['int'],
$i
);
/**
* Will output:
* int(1)
* int(1)
* int(1)
* int(1)
*/</code></pre>
<p>The <code class="language-php">$i</code> variable has been incremented once
to go from 0 to 1, and then, has been stored in a cache. Now, if we
encapsulate this function in an instance of the
<code>Hoa\Ruler\DynamicCallable</code> class, let's observe what happens:</p>
<pre><code class="language-php">$i = 0;
$context['int'] = new Hoa\Ruler\DynamicCallable(
function () use (&amp;$i) {
return ++$i;
}
);
var_dump(
$context['int'],
$context['int'],
$context['int'],
$i
);
/**
* Will output:
* int(1)
* int(2)
* int(3)
* int(3)
*/</code></pre>
<p>The result is no longer stored in a cache.</p>
<p>We can also use a declared function thanks to its name. Attention, it is
not possible to call native PHP functions for security reasons. The context
has not such a <strong>scope</strong>. Thus:</p>
<pre><code class="language-php">function answer()
{
return 42;
}
$context['the_answer'] = 'answer';
var_dump($context['the_answer']);
/**
* Will output:
* int(42)
*/</code></pre>
<p>Nothing more to know about the context. It is not that complicated!</p>
<h3 id="Asserter" for="main-toc">Asserter</h3>
<p>Given a rule and a context, the asserter is responsible to compute the
result of a rule, including the values of variables which are in the
context.</p>
<p>The rule can have two different forms:</p>
<ul>
<li>a <strong>string</strong> or</li>
<li>an <strong>object</strong> model.</li>
</ul>
<p>If it is a string, it will be transformed into an object model
automatically by the <code>Hoa\Ruler\Ruler::assert</code> method. This object
model implements the interfaces of <a href="@hack:chapter=Visitor">the
<code>Hoa\Visitor</code> library</a> and thus can be visited. This is why the
<code>Hoa\Ruler\Visitor\Asserter</code> asserter is a
<strong>visitor</strong>. Finally, the context is defined on the asserter with
the <code>Hoa\Ruler\Visitor\Asserter::setContext</code> method. Thus:</p>
<pre><code class="language-php">$ruler = new Hoa\Ruler\Ruler();
$rule = 'points > 30';
$context = new Hoa\Ruler\Context();
$context['points'] = 42;
// Define an asserter.
$asserter = new Hoa\Ruler\Visitor\Asserter();
// Set this asserter on the ruler.
$ruler->setAsserter($asserter);
// Assert!
var_dump(
$ruler->assert($rule, $context)
);
/**
* Will output:
* bool(true)
*/</code></pre>
<p>The <code>Hoa\Ruler\Ruler::assert</code> method will automatically define
the context on the asserter.</p>
<h4 id="Add_functions" for="main-toc">Add functions</h4>
<p>We said the names of the operators and of the functions in the rules are
<strong>free</strong>. Therefore, we supposed the ability to define our own
operators and functions. Let's add the <code>logged</code> function that tests
if an object of kind <code>User</code> is connected. Here is this object:</p>
<pre><code class="language-php">class User
{
const DISCONNECTED = 0;
const CONNECTED = 1;
protected $_status = 1;
public function getStatus()
{
return $this->_status;
}
}</code></pre>
<p>The implementation of the <code>logged</code> function might be the
following:</p>
<pre><code class="language-php">$logged = function (User $user) {
return $user::CONNECTED === $user->getStatus();
};</code></pre>
<p>Finally, to declare this function, we will use the
<code>Hoa\Ruler\Visitor\Asserter::setOperator</code> method. We can also cite
the <code>operatorExists</code>, <code>getOperator</code> and
<code>getOperators</code> methods which respectively allow to test if an
operator exists, to get a previously declared operator and to get all the
declared operators. Thus:</p>
<pre><code class="language-php">$ruler = new Hoa\Ruler\Ruler();
$rule = 'logged(user) and points > 30';
$context = new Hoa\Ruler\Context();
$context['user'] = new User();
$context['points'] = 42;
// Declare the `logged` function.
$asserter = new Hoa\Ruler\Visitor\Asserter();
$asserter->setOperator('logged', $logged);
$ruler->setAsserter($asserter);
// Assert!
var_dump(
$ruler->assert($rule, $context)
);
/**
* Will output:
* bool(true)
*/</code></pre>
<p>The <code>Hoa\Ruler\Ruler</code> class only contains methods to work faster
and to hide the underlying mechanism (detailed hereinafter). One of them is
the <code>getDefaultAsserter</code> static method which returns a unique
instance of the <code>Hoa\Ruler\Visitor\Asserter</code> class. We can use this
unique instance to define new operators for <strong>all</strong> the rules.
Its usage is very similar to what we saw previously:</p>
<pre><code class="language-php">$ruler = new Hoa\Ruler\Ruler();
$rule = 'logged(user) and points > 30';
$context = new Hoa\Ruler\Context();
$context['user'] = new User();
$context['points'] = 42;
// Declare the `logged` function.
$ruler->getDefaultAsserter()->setOperator('logged', $logged);
// Assert!
var_dump(
$ruler->assert($rule, $context)
);
/**
* Will output:
* bool(true)
*/</code></pre>
<p>The <code>Hoa\Ruler\Visitor\Asserter::setOperator</code> method accepts any
valid callable.</p>
<p>The <code>and</code>, <code>or</code>, <code>xor</code>, <code>not</code>,
<code>=</code>, <code>!=</code> and <code>sum</code> etc. operators are
defined in
<a href="@central_resource:path=Library/Ruler/Visitor/Asserter.php">the
<code>Hoa\Ruler\Visitor\Asserter</code> class</a>. Feel free to read it to
get inspired!</p>
<h2 id="Language_transformation" for="main-toc">Language transformation</h2>
<p>The underlying mechanism hidden by the <code>Hoa\Ruler\Ruler</code> class
is simple and very <strong>modular</strong>. The following sections detail the
possible <strong>transformations</strong>, and associated usage, of the
language.</p>
<div id="transformation" class="schema"></div>
<script>
Hoa.Document.onReady(function ( ) {
var paper = Hoa.Graph(Hoa.$('#transformation'), 800, 300);
var grid = paper.grid(0, 0, 800, 300, 5, 3);
var rule = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule'), 0, 1);
var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpreter'), 1, 1);
var model = grid.push(paper.rect(0, 0, 110, 80, 3, 'model'), 2, 1);
var vasserter = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserter'), 4, 0);
var vcompiler = grid.push(paper.rect(0, 0, 110, 80, 3, 'PHP code'), 4, 1);
var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule'), 4, 2);
paper.link.between(rule, vinterpreter, '');
paper.link.between(vinterpreter, model, '');
paper.link.between(model, vasserter, '');
paper.link.between(model, vcompiler, '');
paper.link.between(model, vdisassembly, '');
});
</script>
<p>First of all, the rule is interpreted by an interpreter to be transformed
into an object model. Then, this object model is used by the asserter, or can
be transformed into PHP code or transformed into a rule. This object model is
the central point of the language, this is its most advanced form.</p>
<h3 id="Interpreter_language_to_object_model" for="main-toc">Interpreter:
Language to object model</h3>
<p>A rule is a string. To transform it into an object model, we will use
<a href="@hack:chapter=Compiler">the <code>Hoa\Compiler</code>
library</a>.</p>
<div id="transformation_interpreter" class="schema"></div>
<script>
Hoa.Document.onReady(function ( ) {
var paper = Hoa.Graph(Hoa.$('#transformation_interpreter'), 800, 300);
var grid = paper.grid(0, 0, 800, 300, 5, 3);
var rule = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule'), 0, 1);
var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpreter'), 1, 1);
var model = grid.push(paper.rect(0, 0, 110, 80, 3, 'model'), 2, 1);
var vasserter = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserter').attr({opacity: .3}), 4, 0);
var vcompiler = grid.push(paper.rect(0, 0, 110, 80, 3, 'PHP code').attr({opacity: .3}), 4, 1);
var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule').attr({opacity: .3}), 4, 2);
paper.link.between(rule, vinterpreter, '');
paper.link.between(vinterpreter, model, '');
paper.link.between(model, vasserter, '');
paper.link.between(model, vcompiler, '');
paper.link.between(model, vdisassembly, '');
});
</script>
<p>Thanks to the grammar of rules (defined in
<a href="@central_resource:path=Library/Ruler/Grammar.pp">the
<code>hoa://Library/Ruler/Grammar.pp</code> file</a>), we will get an AST: An
abstract syntax tree. For instance, for the rule <code>points > 30</code>, its
AST is:</p>
<pre><code class="language-shell">$ echo 'points > 30' | hoa compiler:pp hoa://Library/Ruler/Grammar.pp 0 --visitor dump
> #expression
> > #operation
> > > token(identifier, points)
> > > token(identifier, >)
> > > token(integer, 30)</code></pre>
<p>In order to be exploitable, this tree will be transformed into an
<strong>object model</strong>. This transformation is ensured by the
<code>Hoa\Ruler\Visitor\Interpreter</code> visitor. Thus, if we should apply
it manually:</p>
<pre><code class="language-php">$compiler = Hoa\Compiler\Llk::load(
new Hoa\File\Read('hoa://Library/Ruler/Grammar.pp')
);
$ast = $compiler->parse('points > 30');
$interpreter = new Hoa\Ruler\Visitor\Interpreter();
$model = $interpreter->visit($ast);
var_dump(
get_class($model)
);
/**
* Will output:
* string(21) "Hoa\Ruler\Model\Model"
*/</code></pre>
<p>We learn that the model is represented by classes belonging to the
<code>Hoa\Ruler\Model</code> namespace.</p>
<p>All these operations are replaced by the
<code>Hoa\Ruler\Ruler::interpret</code> static method.</p>
<pre><code class="language-php">$model = Hoa\Ruler\Ruler::interpret('points > 30');</code></pre>
<p>We can get the compiler with the <code>Hoa\Ruler\Ruler::getCompiler</code>
method.</p>
<p>We will see how this step can be important to get better performances.</p>
<h3 id="Compiler_object_model_to_PHP" for="main-toc">Compiler: Object model to
PHP</h3>
<p>The object model can be created manually by instantiating all the objects
of kind <code>Hoa\Ruler\Model\<em>*</em></code> and by combining them
together.</p>
<div id="transformation_compiler" class="schema"></div>
<script>
Hoa.Document.onReady(function ( ) {
var paper = Hoa.Graph(Hoa.$('#transformation_compiler'), 800, 300);
var grid = paper.grid(0, 0, 800, 300, 5, 3);
var rule = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule').attr({opacity: .3}), 0, 1);
var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpreter').attr({opacity: .3}), 1, 1);
var model = grid.push(paper.rect(0, 0, 110, 80, 3, 'model'), 2, 1);
var vasserter = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserter').attr({opacity: .3}), 4, 0);
var vcompiler = grid.push(paper.rect(0, 0, 110, 80, 3, 'PHP code'), 4, 1);
var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule').attr({opacity: .3}), 4, 2);
paper.link.between(rule, vinterpreter, '');
paper.link.between(vinterpreter, model, '');
paper.link.between(model, vasserter, '');
paper.link.between(model, vcompiler, '');
paper.link.between(model, vdisassembly, '');
});
</script>
<p>The PHP code required for this operation can be automatically generated
thanks to the <code>Hoa\Ruler\Visitor\Compiler</code> class. Thus:</p>
<pre><code class="language-php">$compiler = new Hoa\Ruler\Visitor\Compiler();
echo $compiler->visit($model);
/**
* Will output:
* $model = new \Hoa\Ruler\Model();
* $model->expression =
* $model->{'>'}(
* $model->variable('points'),
* 30
* );
*/</code></pre>
<p>The generated code is simplified and optimized to be as short as possible
whilst staying readable for a human.</p>
<p>We will see how this step can be important to get better performances.</p>
<h3 id="Disassembler_object_model_to_language" for="main-toc">Disassembler:
Object model to language</h3>
<p>So far, we have seen how to jump from a rule to its object model. The
disassembler applies the <strong>opposite</strong> operation: It transforms an
object model into a string.</p>
<div id="transformation_disassembler" class="schema"></div>
<script>
Hoa.Document.onReady(function ( ) {
var paper = Hoa.Graph(Hoa.$('#transformation_disassembler'), 800, 300);
var grid = paper.grid(0, 0, 800, 300, 5, 3);
var rule = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule').attr({opacity: .3}), 0, 1);
var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpreter').attr({opacity: .3}), 1, 1);
var model = grid.push(paper.rect(0, 0, 110, 80, 3, 'model'), 2, 1);
var vasserter = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserter').attr({opacity: .3}), 4, 0);
var vcompiler = grid.push(paper.rect(0, 0, 110, 80, 3, 'PHP code').attr({opacity: .3}), 4, 1);
var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'rule'), 4, 2);
paper.link.between(rule, vinterpreter, '');
paper.link.between(vinterpreter, model, '');
paper.link.between(model, vasserter, '');
paper.link.between(model, vcompiler, '');
paper.link.between(model, vdisassembly, '');
});
</script>
<p>The generated rule can differ a little bit from the original one in term of
syntax (parenthesis, quotes, spacing…) but never in term of semantics. Thus:</p>
<pre><code class="language-php">$disassembly = new Hoa\Ruler\Visitor\Disassembly();
echo $disassembly->visit($model);
/**
* Will output:
* (points > 30)
*/</code></pre>
<h2 id="Performances" for="main-toc">Performances</h2>
<p>Transforming a rule into an object model is not a low-cost operation. It
becomes significant when applied thousand times per minute. Nevertheless,
applying an asserter on an object model is a low-cost operation. We will
present two ways to avoid the transformation of a rule into an object
model.</p>
<h3 id="Serialize_the_object_model" for="main-toc">Serialize the object
model</h3>
<p>Once the object model is present, we can serialize it with the help of
<a href="http://php.net/serialize">the <code>serialize</code> PHP
function</a>. We will get a string representing instances of objects forming
the object model. To get back to the object model and being able to apply an
asserter, we will use <a href="http://php.net/unserialize">the
<code>unserialize</code> PHP function</a>. The result of this serialization
can be stored in a database instead of rules. This requires a little bit more
space but let's remind that we can transform an object model to its rule
easily thanks to the <code>Hoa\Ruler\Visitor\Disassembly</code> class,
consequently this information is not lost. Thus:</p>
<pre><code class="language-php">$ruler = new Hoa\Ruler\Ruler();
$rule = 'points > 30';
$context = new Hoa\Ruler\Context();
$context['points'] = 42;
// Nothing in the database.
if (null === $serialized = $database->get($ruleId)) {
// We transform the rule into an object model.
$model = Hoa\Ruler\Ruler::interpret($rule);
// We serialize and save the object model.
$database->save($ruleId, serialize($model));
} else {
// We have a serialization! We unserialize it to get the object model.
$model = unserialize($serialized);
}
// We can assert by using a model instead of a rule!
var_dump(
$ruler->assert($model, $context)
);
/**
* Will output:
* bool(true)
*/</code></pre>
<p>This way, the rule is transformed into an object model <strong>only
once</strong>!</p>
<h3 id="Save_and_execute_PHP_code" for="main-toc">Save and execute PHP
code</h3>
<p>Another way to avoid the transformation of a rule into its object model is
to save the PHP code allowing to build the object model thanks to the
<code>Hoa\Ruler\Visitor\Compiler</code> class. Once this PHP code stored and
executed, we get back our object model.</p>
<p>However, executing such a PHP code will prove to be slightly slower and
more difficult to deploy than the previous technique.</p>
<h2 id="Conclusion" for="main-toc">Conclusion</h2>
<p>The <code>Hoa\Ruler</code> library defines a <strong>language</strong> of
<strong>simple</strong> business rules inspired by the SQL language for the
syntax. The language can be <strong>transformed</strong> in many ways: To an
object model in order to be executed, or from this object model, into PHP code
or into the original language. The instantiation of the variables present in
the language relies on a <strong>context</strong>. All these operations are
hidden through a simple and clear interface.</p>
<p>The <strong>performance</strong> aspect has been addressed and two
solutions have been proposed. Today it is used by the industry on important
projects, <code>Hoa\Ruler</code> is able to support heavy loads if these
simple methodologies are applied.</p>
</yield>
</overlay>
================================================
FILE: Documentation/Fr/Index.xyl
================================================
<?xml version="1.0" encoding="utf-8"?>
<overlay xmlns="http://hoa-project.net/xyl/xylophone">
<yield id="chapter">
<p>Les <strong>règles métiers</strong> (comme « <q>tous les clients qui ont
dépensé plus de 100€ en une fois reçoivent une réduction de 10% sur leur
prochain achat</q> ») sont la plupart du temps définies à
l'<strong>extérieur</strong> de l'application. Elles sont même souvent
exprimées dans un <strong>langage différent</strong> que le langage de
programmation utilisé. La bibliothèque <code>Hoa\Ruler</code> offre un moteur
permettant d'exécuter des règles métiers simplement et de manière
<strong>performante</strong> tout en restant très
<strong>extensible</strong>.</p>
<h2 id="Table_of_contents">Table des matières</h2>
<tableofcontents id="main-toc" />
<h2 id="Introduction" for="main-toc">Introduction</h2>
<p>La logique métier est très différente de la logique Informatique. « <q>Tous
les clients qui ont dépensé plus de 100€ en une fois reçoivent une réduction
de 10% sur leur prochain achat</q> ». Cette règle permet d'accéder à certaines
parties du programme si elle est <strong>validée</strong>. Toutefois, elle
peut <strong>changer</strong> à n'importe quel moment. Très souvent, dans une
équipe, ce ne sera pas au développeur d'implémenter cette règle. Elle
proviendra probablement d'un <strong>dépôt de règles métiers</strong>, qui
auront été écrites par d'autres personnes, soit <strong>manuellement</strong>,
soit à l'aide d'un <strong>programme tiers</strong>. Cela implique que le
langage utilisé pour exprimer une règle n'est pas le langage utilisé pour
développer le programme. Un exemple encore plus évident avec l'utilisation
d'une règle pour filtrer des éléments : un élément est accepté si « <q>son
groupe est <em lang="en">customer</em> ou <em lang="en">guest</em> et son
nombre de points est supérieur à 30</q> ». Cette règle peut très bien être
écrite par un utilisateur via une interface en ligne de commande pour filtrer
des résultats d'une base de données ou de logs.</p>
<p>Il est important de comprendre que les règles doivent être écrites dans un
<strong>langage dédié</strong>. Néanmoins, l'usage qui est fait des règles est
très <strong>vaste</strong> et <strong>imprédictible</strong>. C'est pourquoi
il est primordiale d'avoir des règles souples et <strong>extensibles</strong>
dans leur syntaxe. Par exemple, il doit être permis d'ajouter des opérateurs
ou des fonctions : « <q>tous les clients de l'hôtel avec un pass Gold auront
une réduction de 10%</q> ». Le « pass Gold » peut être un opérateur ou une
fonction spécifique au métier du programme concerné.</p>
<p>Le langage utilisé par la bibliothèque <code>Hoa\Ruler</code> pour décrire
des règles respecte ces contraintes d'extensibilité. Les règles ne seront pas
proches du langage humain mais resterons <strong>naturelles</strong> à lire.
Si nous reprenons l'exemple de la règle « <q>son groupe est
<em lang="en">customer</em> ou <em lang="en">guest</em> et son nombre de
points est supérieur à 30</q> », elle s'écrira : <code><em>group</em> in
["customer", "guest"] and <em>points</em> > 30</code>. Les éléments
<code><em>group</em></code> et <code><em>points</em></code> sont des variables
de la règle. Leurs valeurs seront définies par un contexte.</p>
<p>D'un point de vue plus formel, une règle est un prédicat, c'est à dire que
son résultat est toujours un booléen : <code class="language-php">true</code>
ou <code class="language-php">false</code>. Comme ces règles sont destinées à
être manipulées (modifiées) et exécutées, la bibliothèque
<code>Hoa\Ruler</code> propose plusieurs outils pour travailler efficacement
avec ces contraintes, présentés dans les sections suivantes.</p>
<h2 id="Global_workflow" for="main-toc">Fonctionnement général</h2>
<p>Le fonctionnement général de la bibliothèque <code>Hoa\Ruler</code> se
déroule en 3 étapes :</p>
<ol>
<li>définition d'une <strong>règle</strong> ;</li>
<li>définition d'un <strong>contexte</strong> ;</li>
<li>usage d'un <strong>asserteur</strong> pour l'exécution.</li>
</ol>
<p>La <strong>règle</strong> est une chaîne de caractères respectant une
syntaxe précise, décrite par la grammaire du langage définie par la
bibliothèque <code>Hoa\Ruler</code> (détaillée ci-après). Cette règle contient
des variables dont les valeurs sont définies par le <strong>contexte</strong>.
Le contexte peut contenir des valeurs scalaires, des tableaux ou même des
fonctions et des objets. Enfin, l'<strong>asserteur</strong> associe le
contexte à la règle pour pouvoir l'exécuter et obtenir un résultat. Nous
rappelons que ce résultat est nécessairement un booléen. C'est alors un
prédicat.</p>
<p>Le contexte est représenté par la classe <code>Hoa\Ruler\Context</code>.
L'asserteur est représenté par la classe
<code>Hoa\Ruler\Visitor\Asserter</code>. Nous pouvons employer la méthode
<code>Hoa\Ruler\Ruler::assert</code> qui facilite son utilisation. Ainsi :</p>
<pre><code class="language-php">$ruler = new Hoa\Ruler\Ruler();
// 1. Write a rule.
$rule = 'group in ["customer", "guest"] and points > 30';
// 2. Create a context.
$context = new Hoa\Ruler\Context();
$context['group'] = 'customer';
$context['points'] = function () {
return 42;
};
// 3. Assert!
var_dump(
$ruler->assert($rule, $context)
);
/**
* Will output:
* bool(true)
*/</code></pre>
<p>La règle est définie dans la variable
<code class="language-php">$rule</code>. Le contexte, quant à lui, est défini
dans la variable <code class="language-php">$context</code>. Il contient
2 variables : <code>group</code> et <code>points</code>, respectivement avec
les valeurs <code class="language-php">'customer'</code> et
<code class="language-php">42</code> (retournée par une fonction). Enfin, la
dernière étape utilise la méthode <code>Hoa\Ruler\Ruler::assert</code> pour
exécuter la règle <code class="language-php">$rule</code> avec le contexte
<code class="language-php">$context</code> (ce dernier est optionnel). Le
résultat est <code class="language-php">true</code> car <code>group</code> est
bien dans la liste <code>customer</code> ou <code>guest</code>, et
<code>point</code> est bien supérieur à 30. Changez les valeurs dans le
contexte ou la règle pour observer un résultat différent.</p>
<p>Les sections suivantes détaillent le fonctionnement de chaque partie mais
l'usage classique reste aussi simple que ça !</p>
<h3 id="Grammar" for="main-toc">Grammaire (par l'exemple)</h3>
<p>La grammaire du langage des règles est décrite dans
<a href="@central_resource:path=Library/Ruler/Grammar.pp">le fichier
<code>hoa://Library/Ruler/Grammar.pp</code></a>. Cette grammaire est exprimée
avec le langage PP. Pour plus d'informations, voir
<a href="@hack:chapter=Compiler">la bibliothèque
<code>Hoa\Compiler</code></a>. Nous précisons que le langage supporte Unicode.
Nous n'allons pas expliquer le langage alors que la grammaire donne tous les
<strong>détails</strong> nécessaires. En revanche, nous allons donner
plusieurs exemples de syntaxe.</p>
<table>
<caption>Langage de <code>Hoa\Ruler</code> par l'exemple.</caption>
<thead>
<tr>
<th>syntaxe</th>
<th>sémantique</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>'foo'</code>, <code>"foo"</code>, <code>'f\'oo'</code></td>
<td>des chaînes de caractères</td>
</tr>
<tr>
<td><code>true</code>, <code>false</code>, <code>null</code></td>
<td>des constantes pré-définies</td>
</tr>
<tr>
<td><code>4.2</code></td>
<td>un réel</td>
</tr>
<tr>
<td><code>42</code></td>
<td>un entier</td>
</tr>
<tr>
<td><code>['foo', true, 4.2, 42]</code></td>
<td>un tableau (hétérogène)</td>
</tr>
<tr>
<td><code>sum(1, 2, 3)</code></td>
<td>un appel de la fonction <code>sum</code> avec 3 arguments</td>
</tr>
<tr>
<td><code><em>points</em></code></td>
<td>une variable</td>
</tr>
<tr>
<td><code><em>points</em>['x']</code></td>
<td>un accès tableau</td>
</tr>
<tr>
<td><code><em>line</em>.pointA</code></td>
<td>un accès objet (attribut)</td>
</tr>
<tr>
<td><code><em>line</em>.length()</code></td>
<td>un appel à une méthode</td>
</tr>
<tr>
<td><code>and</code>, <code>or</code>, <code>xor</code>, <code>not</code></td>
<td>des opérateurs logiques</td>
</tr>
<tr>
<td><code>=</code>, <code>!=</code>, <code>></code>, <code>&lt;</code>,
<code>>=</code>, <code>&lt;=</code></td>
<td>des opérateurs de comparaisons</td>
</tr>
<tr>
<td><code>is</code>, <code>in</code></td>
<td>des opérateurs d'appartenance</td>
</tr>
</tbody>
</table>
<p>Bien sûr, ces exemples représentent des parties atomiques de la grammaire
que nous pouvons combiner. Ainsi :
<code><em>userA</em>.allows(<em>groups</em>[<em>groupId</em>][<em>userB</em>])</code>
est valide. De même que <code>f(<em>user</em>, <em>points</em> > 7 and
<em>points</em> &lt; 42)</code> est également valide.</p>
<p>En réalité, les fonctions, les opérateurs de comparaisons et les opérateurs
d'appartenance ne sont pas définis par la grammaire mais par l'asserteur
(détaillé ci-après). <code>Hoa\Ruler</code> ne fait pas la différence entre un
opérateur et une fonction. <strong>Les opérateurs sont considérés comme des
fonctions</strong> ; un opérateur n'étant qu'une fonction d'arité 1 ou 2.
Ainsi, nous pouvons écrire <code>2 = 2</code> ou <code>=(2, 2)</code>, cela
produira strictement le même résultat. Tout comme le nom des fonctions qui
n'est pas défini par la grammaire, le nom des opérateurs n'est lui non plus
pas défini par la grammaire, excepté pour les opérateurs logiques qui ont un
traitement particuliers (à cause de la précédence des opérateurs). Cela a pour
effet de pouvoir <strong>créer</strong> nos propres opérateurs ou fonctions.
Nous pouvons imaginer <code><em>a</em> ∈ <em>A</em></code>, <code>√(42)</code>
ou encore <code><em>userA</em> allows <em>userB</em></code> comme étant des
expressions valides.</p>
<h3 id="Context" for="main-toc">Contexte</h3>
<p>Le contexte définit les valeurs des variables présentes dans des
règles. Ces valeurs peuvent être :</p>
<ul>
<li>des <strong>constantes</strong>, comme
<code class="language-php">42</code> ou
<code class="language-php">'foo'</code> qui sont des scalaires, ou
<code class="language-php">[1, 1, 2, 3, 5]</code> ou un objet qui sont des
types structurés ;</li>
<li>des <strong>valeurs calculées</strong>, c'est à dire retournée par une
fonction ou une méthode.</li>
</ul>
<p>Par défaut, les valeurs calculées ne le sont qu'une seule fois. En effet,
elles sont placées dans un cache pour des raisons de performance. Si nous
voulons les recalculer à chaque lecture, il faudra les encapsuler dans un
objet de type <code>Hoa\Ruler\DynamicCallable</code>.</p>
<p>Avant de détailler cette partie, présentons le contexte. Un contexte est
une instance de la classe <code>Hoa\Ruler\Context</code> qui implémente
<a href="http://php.net/arrayaccess">l'interface <code>ArrayAccess</code></a>.
Ainsi nous utilisons le contexte comme un tableau :</p>
<pre><code class="language-php">$context = new Hoa\Ruler\Context();
$context['key'] = 'value';
var_dump(
isset($context['key']),
$context['key']
);
/**
* Will output:
* bool(true)
* string(5) "value"
*/</code></pre>
<p>Pour y déposer des valeurs calculées, nous pouvons le faire via une
fonction ; ainsi :</p>
<pre><code class="language-php">$context['computed'] = function () {
return 42;
};
var_dump($context['computed']);
/**
* Will output:
* int(42)
*/</code></pre>
<p>Nous avons dit que les valeurs calculées sont placées en
<strong>cache</strong> dès la première lecture. Pour l'illustrer, nous allons
utiliser une fonction qui incrémente un entier à chaque appel :</p>
<pre><code class="language-php">$i = 0;
$context['int'] = function () use (&amp;$i) {
return ++$i;
};
var_dump(
$context['int'],
$context['int'],
$context['int'],
$i
);
/**
* Will output:
* int(1)
* int(1)
* int(1)
* int(1)
*/</code></pre>
<p>La variable <code class="language-php">$i</code> a été incrémentée une
seule fois pour passer de 0 à 1, puis elle a été placée en cache. Maintenant,
si nous encapsulons cette fonction dans une instance de la classe
<code>Hoa\Ruler\DynamicCallable</code>, observons ce qu'il se passe :</p>
<pre><code class="language-php">$i = 0;
$context['int'] = new Hoa\Ruler\DynamicCallable(
function () use (&amp;$i) {
return ++$i;
}
);
var_dump(
$context['int'],
$context['int'],
$context['int'],
$i
);
/**
* Will output:
* int(1)
* int(2)
* int(3)
* int(3)
*/</code></pre>
<p>Le résultat n'est plus mis en cache.</p>
<p>Nous pouvons également utiliser une fonction déclarée grâce à son nom.
Attention toutefois, il sera impossible d'appeler les fonctions natives de PHP
pour des raisons de sécurité. Le contexte n'a pas une telle
<strong>portée</strong>. Ainsi :</p>
<pre><code class="language-php">function answer()
{
return 42;
}
$context['the_answer'] = 'answer';
var_dump($context['the_answer']);
/**
* Will output:
* int(42)
*/</code></pre>
<p>C'est tout ce qu'il faut savoir sur le contexte. Ce n'est pas plus
compliqué que ça !</p>
<h3 id="Asserter" for="main-toc">Asserteur</h3>
<p>Étant donné une règle et un contexte, l'asserteur est chargé de calculer le
résultat de cette règle, dont les valeurs des variables sont dans le
contexte.</p>
<p>La règle peut avoir deux formes possibles :</p>
<ul>
<li>une <strong>chaîne de caractère</strong> ou</li>
<li>un modèle <strong>objet</strong>.</li>
</ul>
<p>Si c'est une chaîne de caractère, elle sera transformée en modèle objet
automatiquement par la méthode <code>Hoa\Ruler\Ruler::assert</code>. Ce modèle
objet implémente les interfaces de <a href="@hack:chapter=Visitor">la
bibliothèque <code>Hoa\Visitor</code></a> et peut donc être visité. C'est
pourquoi l'asserteur <code>Hoa\Ruler\Visitor\Asserter</code> est un
<strong>visiteur</strong>. Le contexte quant à lui est défini auprès de
l'asserteur avec la méthode
<code>Hoa\Ruler\Visitor\Asserter::setContext</code>. Ainsi :</p>
<pre><code class="language-php">$ruler = new Hoa\Ruler\Ruler();
$rule = 'points > 30';
$context = new Hoa\Ruler\Context();
$context['points'] = 42;
// Define an asserter.
$asserter = new Hoa\Ruler\Visitor\Asserter();
// Set this asserter on the ruler.
$ruler->setAsserter($asserter);
// Assert!
var_dump(
$ruler->assert($rule, $context)
);
/**
* Will output:
* bool(true)
*/</code></pre>
<p>La méthode <code>Hoa\Ruler\Ruler::assert</code> va automatiquement définir
le contexte auprès de l'asserteur.</p>
<h4 id="Add_functions" for="main-toc">Ajout de fonctions</h4>
<p>Nous avons précisé que les noms des opérateurs et des fonctions dans les
règles sont <strong>libres</strong>. Ainsi, nous avons évoqué la possibilité
de définir nos propres opérateurs et fonctions. Ajoutons la fonction
<code>logged</code> qui teste si un objet de type <code>User</code> est
connecté. Voici cet objet :</p>
<pre><code class="language-php">class User
{
const DISCONNECTED = 0;
const CONNECTED = 1;
protected $_status = 1;
public function getStatus()
{
return $this->_status;
}
}</code></pre>
<p>L'implémentation de la fonction <code>logged</code> serait alors :</p>
<pre><code class="language-php">$logged = function (User $user) {
return $user::CONNECTED === $user->getStatus();
};</code></pre>
<p>Enfin, pour déclarer cette fonction, nous allons utiliser la méthode
<code>Hoa\Ruler\Visitor\Asserter::setOperator</code>. Nous pouvons aussi citer
les méthodes <code>operatorExists</code>, <code>getOperator</code> et
<code>getOperators</code> qui permettent respectivement de tester si un
opérateur existe, d'obtenir un opérateur précédemment déclaré et d'obtenir
tous les opérateurs déclarés. Ainsi :</p>
<pre><code class="language-php">$ruler = new Hoa\Ruler\Ruler();
$rule = 'logged(user) and points > 30';
$context = new Hoa\Ruler\Context();
$context['user'] = new User();
$context['points'] = 42;
// Declare the `logged` function.
$asserter = new Hoa\Ruler\Visitor\Asserter();
$asserter->setOperator('logged', $logged);
$ruler->setAsserter($asserter);
// Assert!
var_dump(
$ruler->assert($rule, $context)
);
/**
* Will output:
* bool(true)
*/</code></pre>
<p>La classe <code>Hoa\Ruler\Ruler</code> ne contient que des méthodes pour
travailler plus vite et cacher le mécanisme sous-jacent (détaillé ci-après).
Elle contient entre autre la méthode statique <code>getDefaultAsserter</code>
qui retourne une instance unique de la classe
<code>Hoa\Ruler\Visitor\Asserter</code>. Nous pouvons utiliser cette instance
unique pour définir des nouveaux opérateurs pour <strong>toutes</strong> les
règles. Son utilisation est très similaire à ce que nous avons vu
précédemment :</p>
<pre><code class="language-php">$ruler = new Hoa\Ruler\Ruler();
$rule = 'logged(user) and points > 30';
$context = new Hoa\Ruler\Context();
$context['user'] = new User();
$context['points'] = 42;
// Declare the `logged` function.
$ruler->getDefaultAsserter()->setOperator('logged', $logged);
// Assert!
var_dump(
$ruler->assert($rule, $context)
);
/**
* Will output:
* bool(true)
*/</code></pre>
<p>La méthode <code>Hoa\Ruler\Visitor\Asserter::setOperator</code> utilise
n'importe quel <em lang="en">callable</em> valide.</p>
<p>Les opérateurs <code>and</code>, <code>or</code>, <code>xor</code>,
<code>not</code>, <code>=</code>, <code>!=</code>, <code>sum</code> etc. sont
définis dans
<a href="@central_resource:path=Library/Ruler/Visitor/Asserter.php">la classe
<code>Hoa\Ruler\Visitor\Asserter</code></a>. N'hésitez pas à vous en
inspirer !</p>
<h2 id="Language_transformation" for="main-toc">Transformation du langage</h2>
<p>Le mécanisme sous-jacent caché par la classe <code>Hoa\Ruler\Ruler</code>
est simple et très <strong>modulaire</strong>. Les sections suivantes
détaillent les <strong>transformations</strong> possibles du langage et leur
utilisation.</p>
<div id="transformation" class="schema"></div>
<script>
Hoa.Document.onReady(function ( ) {
var paper = Hoa.Graph(Hoa.$('#transformation'), 800, 300);
var grid = paper.grid(0, 0, 800, 300, 5, 3);
var rule = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle'), 0, 1);
var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpréteur'), 1, 1);
var model = grid.push(paper.rect(0, 0, 110, 80, 3, 'modèle'), 2, 1);
var vasserter = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserteur'), 4, 0);
var vcompiler = grid.push(paper.rect(0, 0, 110, 80, 3, 'code PHP'), 4, 1);
var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle'), 4, 2);
paper.link.between(rule, vinterpreter, '');
paper.link.between(vinterpreter, model, '');
paper.link.between(model, vasserter, '');
paper.link.between(model, vcompiler, '');
paper.link.between(model, vdisassembly, '');
});
</script>
<p>Tout d'abord, la règle est interprétée par un interpréteur pour être
transformée en modèle objet. Ensuite, ce modèle objet est utilisé par
l'asserteur, ou peut être transformé en code PHP ou transformé en une règle.
Le modèle objet est le point central du langage, c'est sa forme la plus
avancée.</p>
<h3 id="Interpreter_language_to_object_model" for="main-toc">Interpréteur :
langage vers modèle objet</h3>
<p>Une règle est une chaîne de caractères. Pour la transformer en modèle
objet, nous utilisons <a href="@hack:chapter=Compiler">la bibliothèque
<code>Hoa\Compiler</code></a>.</p>
<div id="transformation_interpreter" class="schema"></div>
<script>
Hoa.Document.onReady(function ( ) {
var paper = Hoa.Graph(Hoa.$('#transformation_interpreter'), 800, 300);
var grid = paper.grid(0, 0, 800, 300, 5, 3);
var rule = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle'), 0, 1);
var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpréteur'), 1, 1);
var model = grid.push(paper.rect(0, 0, 110, 80, 3, 'modèle'), 2, 1);
var vasserter = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserteur').attr({opacity: .3}), 4, 0);
var vcompiler = grid.push(paper.rect(0, 0, 110, 80, 3, 'code PHP').attr({opacity: .3}), 4, 1);
var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle').attr({opacity: .3}), 4, 2);
paper.link.between(rule, vinterpreter, '');
paper.link.between(vinterpreter, model, '');
paper.link.between(model, vasserter, '');
paper.link.between(model, vcompiler, '');
paper.link.between(model, vdisassembly, '');
});
</script>
<p>Grâce à la grammaire des règles (définie dans
<a href="@central_resource:path=Library/Ruler/Grammar.pp">le fichier
<code>hoa://Library/Ruler/Grammar.pp</code></a>), nous allons obtenir un AST :
un arbre abstrait. Par exemple, pour la règle <code>points > 30</code>, son
AST est :</p>
<pre><code class="language-shell">$ echo 'points > 30' | hoa compiler:pp hoa://Library/Ruler/Grammar.pp 0 --visitor dump
> #expression
> > #operation
> > > token(identifier, points)
> > > token(identifier, >)
> > > token(integer, 30)</code></pre>
<p>Pour que cet arbre soit exploitable plus facilement, il va être transformé
en <strong>modèle objet</strong>. Cette transformation est assurée par la
classe <code>Hoa\Ruler\Visitor\Interpreter</code>. Ainsi, si nous devions le
faire manuellement :</p>
<pre><code class="language-php">$compiler = Hoa\Compiler\Llk::load(
new Hoa\File\Read('hoa://Library/Ruler/Grammar.pp')
);
$ast = $compiler->parse('points > 30');
$interpreter = new Hoa\Ruler\Visitor\Interpreter();
$model = $interpreter->visit($ast);
var_dump(
get_class($model)
);
/**
* Will output:
* string(21) "Hoa\Ruler\Model\Model"
*/</code></pre>
<p>Nous apprenons alors que le modèle est représenté par les classes
appartenant à l'espace de nom <code>Hoa\Ruler\Model</code>.</p>
<p>Toutes ces opérations sont remplacées par la méthode statique
<code>Hoa\Ruler\Ruler::interpret</code> :</p>
<pre><code class="language-php">$model = Hoa\Ruler\Ruler::interpret('points > 30');</code></pre>
<p>Nous pouvons obtenir le compilateur avec la méthode
<code>Hoa\Ruler\Ruler::getCompiler</code>.</p>
<p>Nous verrons comment cette étape peut être importante pour améliorer les
performances.</p>
<h3 id="Compiler_object_model_to_PHP" for="main-toc">Compilateur : modèle
objet vers PHP</h3>
<p>Le modèle objet peut être créé manuellement en instanciant tous les objets
de type <code>Hoa\Ruler\Model\<em>*</em></code> et en les combinant
ensemble.</p>
<div id="transformation_compiler" class="schema"></div>
<script>
Hoa.Document.onReady(function ( ) {
var paper = Hoa.Graph(Hoa.$('#transformation_compiler'), 800, 300);
var grid = paper.grid(0, 0, 800, 300, 5, 3);
var rule = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle').attr({opacity: .3}), 0, 1);
var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpréteur').attr({opacity: .3}), 1, 1);
var model = grid.push(paper.rect(0, 0, 110, 80, 3, 'modèle'), 2, 1);
var vasserter = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserteur').attr({opacity: .3}), 4, 0);
var vcompiler = grid.push(paper.rect(0, 0, 110, 80, 3, 'code PHP'), 4, 1);
var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle').attr({opacity: .3}), 4, 2);
paper.link.between(rule, vinterpreter, '');
paper.link.between(vinterpreter, model, '');
paper.link.between(model, vasserter, '');
paper.link.between(model, vcompiler, '');
paper.link.between(model, vdisassembly, '');
});
</script>
<p>Le code PHP nécessaire à cette opération peut être automatiquement généré
grâce à la classe <code>Hoa\Ruler\Visitor\Compiler</code>. Ainsi :</p>
<pre><code class="language-php">$compiler = new Hoa\Ruler\Visitor\Compiler();
echo $compiler->visit($model);
/**
* Will output:
* $model = new \Hoa\Ruler\Model();
* $model->expression =
* $model->{'>'}(
* $model->variable('points'),
* 30
* );
*/</code></pre>
<p>Le code généré est simplifié et optimisé pour être le plus court possible
tout en restant lisible par un être humain.</p>
<p>Nous verrons comment cette étape peut être importante pour améliorer les
performances.</p>
<h3 id="Disassembler_object_model_to_language" for="main-toc">Désassembleur :
modèle objet vers langage</h3>
<p>Jusqu'à maintenant, nous avons vu comment passer d'une règle vers son
modèle objet. Le désassembleur applique l'opération <strong>inverse</strong> :
il transforme un modèle objet vers une chaîne de caractères.</p>
<div id="transformation_disassembler" class="schema"></div>
<script>
Hoa.Document.onReady(function ( ) {
var paper = Hoa.Graph(Hoa.$('#transformation_disassembler'), 800, 300);
var grid = paper.grid(0, 0, 800, 300, 5, 3);
var rule = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle').attr({opacity: .3}), 0, 1);
var vinterpreter = grid.push(paper.rect(0, 0, 110, 80, 3, 'interpréteur').attr({opacity: .3}), 1, 1);
var model = grid.push(paper.rect(0, 0, 110, 80, 3, 'modèle'), 2, 1);
var vasserter = grid.push(paper.rect(0, 0, 110, 80, 3, 'asserteur').attr({opacity: .3}), 4, 0);
var vcompiler = grid.push(paper.rect(0, 0, 110, 80, 3, 'code PHP').attr({opacity: .3}), 4, 1);
var vdisassembly = grid.push(paper.rect(0, 0, 110, 80, 3, 'règle'), 4, 2);
paper.link.between(rule, vinterpreter, '');
paper.link.between(vinterpreter, model, '');
paper.link.between(model, vasserter, '');
paper.link.between(model, vcompiler, '');
paper.link.between(model, vdisassembly, '');
});
</script>
<p>La règle générée peut différer un peu de la règle originale syntaxiquement
(parenthèses, guillemets, espacements…) mais jamais sémantiquement.
Ainsi :</p>
<pre><code class="language-php">$disassembly = new Hoa\Ruler\Visitor\Disassembly();
echo $disassembly->visit($model);
/**
* Will output:
* (points > 30)
*/</code></pre>
<h2 id="Performances" for="main-toc">Performances</h2>
<p>Transformer une règle vers un modèle objet a un coût. Ce coût devient
significatif si l'opération est appliquée des milliers de fois à la minute.
En revanche, appliquer un asserteur sur un modèle objet n'est pas coûteux.
Nous allons donc présenter deux façons d'éviter la transformation d'une règle
vers un modèle objet.</p>
<h3 id="Serialize_the_object_model" for="main-toc">Sérialiser le modèle
objet</h3>
<p>Une fois le modèle objet obtenu, nous pouvons le sérialiser à l'aide de
<a href="http://php.net/serialize">la fonction PHP <code>serialize</code></a>.
Nous allons obtenir une chaîne de caractères représentant les instances des
objets constituant le modèle objet. Pour obtenir à nouveau le modèle objet et
être capable d'y appliquer un asserteur, nous utiliserons
<a href="http://php.net/unserialize">la fonction PHP
<code>unserialize</code></a>. Le résultat de la sérialisation peut être
stocké en base de données à la place des règles. Cela nécessite un peu plus
de place mais rappelons que nous pouvons transformer le modèle objet vers sa
règle facilement grâce à la classe
<code>Hoa\Ruler\Visitor\Disassembly</code>, cette information n'est donc pas
perdue. Ainsi :</p>
<pre><code class="language-php">$ruler = new Hoa\Ruler\Ruler();
$rule = 'points > 30';
$context = new Hoa\Ruler\Context();
$context['points'] = 42;
// Nothing in the database.
if (null === $serialized = $database->get($ruleId)) {
// We transform the rule into an object model.
$model = Hoa\Ruler\Ruler::interpret($rule);
// We serialize and save the object model.
$database->save($ruleId, serialize($model));
} else {
// We have a serialization! We unserialize it to get the object model.
$model = unserialize($serialized);
}
// We can assert by using a model instead of a rule!
var_dump(
$ruler->assert($model, $context)
);
/**
* Will output:
* bool(true)
*/</code></pre>
<p>De cette manière, la règle n'est transformée en modèle objet
qu'<strong>une seule fois</strong> !</p>
<h3 id="Save_and_execute_PHP_code" for="main-toc">Enregistrer et exécute du
code PHP</h3>
<p>Une autre façon d'éviter la transformation d'une règle en modèle objet est
d'enregistrer le code PHP permettant de construire le modèle objet grâce à la
classe <code>Hoa\Ruler\Visitor\Compiler</code>. Une fois ce code PHP
enregistré et exécuté, nous retrouverons notre modèle objet.</p>
<p>Toutefois, exécuter un tel code PHP s'avèrera légèrement plus lent et plus
difficile à mettre en œuvre que la technique précédente.</p>
<h2 id="Conclusion" for="main-toc">Conclusion</h2>
<p>La bibliothèque <code>Hoa\Ruler</code> définit un <strong>langage</strong>
de règles métiers <strong>simple</strong> et inspiré du langage SQL dans sa
syntaxe. Le langage peut être <strong>transformé</strong> de plusieurs
façons : vers un modèle objet pour qu'il puisse être exécuté, ou à partir de
ce modèle, vers du code PHP ou vers le langage d'origine. L'instanciation des
variables dans ce langage se fait à travers un <strong>contexte</strong>.
Toutes ces opérations sont cachées à travers une interface simple et
claire.</p>
<p>La question des <strong>performances</strong> a été abordée et deux
solutions sont proposées. Aujourd'hui utilisé dans l'industrie sur de gros
projets, <code>Hoa\Ruler</code> est capable de supporter de lourdes charges si
ces pratiques simples sont mises en œuvre.</p>
</yield>
</overlay>
================================================
FILE: DynamicCallable.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler;
use Hoa\Consistency;
/**
* Class \Hoa\Ruler\DynamicCallable.
*
* A callable without cache. Useful for the Context.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class DynamicCallable extends Consistency\Xcallable
{
}
================================================
FILE: Exception/Asserter.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Exception;
/**
* Class \Hoa\Ruler\Asserter.
*
* Extending the \Hoa\Ruler\Exception class.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Asserter extends Exception
{
}
================================================
FILE: Exception/Exception.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Exception;
use Hoa\Consistency;
use Hoa\Exception as HoaException;
/**
* Class \Hoa\Ruler\Exception.
*
* Extending the \Hoa\Exception\Exception class.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Exception extends HoaException
{
}
/**
* Flex entity.
*/
Consistency::flexEntity('Hoa\Ruler\Exception\Exception');
================================================
FILE: Exception/Interpreter.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Exception;
/**
* Class \Hoa\Ruler\Interpreter.
*
* Extending the \Hoa\Ruler\Exception class.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Interpreter extends Exception
{
}
================================================
FILE: Grammar.pp
================================================
//
// Hoa
//
//
// @license
//
// New BSD License
//
// Copyright © 2007-2017, Hoa community. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Hoa nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Grammar \Hoa\Ruler\Grammar.
//
// Provide grammar for ruler.
//
// @copyright Copyright © 2007-2017 Hoa community.
// @license New BSD License
//
%skip space \s
// Scalars.
%token true (?i)true
%token false (?i)false
%token null (?i)null
// Logical operators
%token not (?i)not\b
%token and (?i)and\b
%token or (?i)or\b
%token xor (?i)xor\b
// Value
%token string ("|')(.*?)(?<!\\)\1
%token float \d+\.\d+
%token integer \d+
%token parenthesis_ \(
%token _parenthesis \)
%token bracket_ \[
%token _bracket \]
%token comma ,
%token dot \.
%token identifier [^\s\(\)\[\],\.]+
#expression:
logical_operation_primary()
logical_operation_primary:
logical_operation_secondary()
( ( ::or:: #or | ::xor:: #xor ) logical_operation_primary() )?
logical_operation_secondary:
operation()
( ::and:: #and logical_operation_secondary() )?
operation:
operand() ( <identifier> logical_operation_primary() #operation )?
operand:
::parenthesis_:: logical_operation_primary() ::_parenthesis::
| value()
value:
::not:: logical_operation_primary() #not
| <true> | <false> | <null> | <float> | <integer> | <string>
| array_declaration()
| chain()
chain:
( variable() | function_call() )
( ( array_access() | object_access() ) #variable_access )*
variable:
<identifier>
#array_access:
::bracket_:: value() ::_bracket::
object_access:
::dot:: ( <identifier> #attribute_access | function_call() #method_access )
#array_declaration:
::bracket_:: value() ( ::comma:: value() )* ::_bracket::
#function_call:
<identifier> ::parenthesis_::
( logical_operation_primary() ( ::comma:: logical_operation_primary() )* )?
::_parenthesis::
================================================
FILE: Model/Bag/Bag.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Model\Bag;
use Hoa\Consistency;
use Hoa\Visitor;
/**
* Class \Hoa\Ruler\Model\Bag.
*
* Generic bag.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
abstract class Bag implements Visitor\Element
{
/**
* Accept a visitor.
*
* @param \Hoa\Visitor\Visit $visitor Visitor.
* @param mixed &$handle Handle (reference).
* @param mixed $eldnah Handle (no reference).
* @return mixed
*/
public function accept(
Visitor\Visit $visitor,
&$handle = null,
$eldnah = null
) {
return $visitor->visit($this, $handle, $eldnah);
}
}
/**
* Flex entity.
*/
Consistency::flexEntity('Hoa\Ruler\Model\Bag\Bag');
================================================
FILE: Model/Bag/Context.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Model\Bag;
use Hoa\Ruler;
/**
* Class \Hoa\Ruler\Model\Bag\Context.
*
* Bag for context, i.e. a variable.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Context extends Bag
{
/**
* Access type.
*
* @const int
*/
const ACCESS_TYPE = 0;
/**
* Access value.
*
* @const int
*/
const ACCESS_VALUE = 1;
/**
* Type: array access.
*
* @const int
*/
const ARRAY_ACCESS = 0;
/**
* Type: attribute access.
*
* @const int
*/
const ATTRIBUTE_ACCESS = 1;
/**
* Type: method access.
*
* @const int
*/
const METHOD_ACCESS = 2;
/**
* ID.
*
* @var string
*/
protected $_id = null;
/**
* Index and object accesses.
*
* @var array
*/
protected $_dimensions = [];
/**
* Constructor.
*
* @param string $id ID.
*/
public function __construct($id)
{
$this->_id = $id;
return;
}
/**
* Call an index (variable[indexA][indexB][indexC]).
*
* @param mixed $index Index (a bag, a scalar or an array).
* @return \Hoa\Ruler\Model\Bag\Context
*/
public function index($index)
{
if (is_scalar($index) || null === $index) {
$index = new Scalar($index);
} elseif (is_array($index)) {
$index = new RulerArray($index);
}
$this->_dimensions[] = [
static::ACCESS_TYPE => static::ARRAY_ACCESS,
static::ACCESS_VALUE => $index
];
return $this;
}
/**
* Call an attribute (variable.attrA.attrB).
*
* @param string $attribute Attribute name.
* @return \Hoa\Ruler\Model\Bag\Context
*/
public function attribute($attribute)
{
$this->_dimensions[] = [
static::ACCESS_TYPE => static::ATTRIBUTE_ACCESS,
static::ACCESS_VALUE => $attribute
];
return $this;
}
/**
* Call a method (variable.foo().bar().baz()).
*
* @param \Hoa\Ruler\Model\Operator $method Method to call.
* @return \Hoa\Ruler\Model\Bag\Context
*/
public function call(Ruler\Model\Operator $method)
{
$this->_dimensions[] = [
static::ACCESS_TYPE => static::METHOD_ACCESS,
static::ACCESS_VALUE => $method
];
return $this;
}
/**
* Get all dimensions.
*
* @return array
*/
public function getDimensions()
{
return $this->_dimensions;
}
/**
* Get ID.
*
* @return string
*/
public function getId()
{
return $this->_id;
}
}
================================================
FILE: Model/Bag/RulerArray.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Model\Bag;
/**
* Class \Hoa\Ruler\Model\Bag\RulerArray.
*
* Bag for an array.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class RulerArray extends Bag
{
/**
* Array.
*
* @var array
*/
protected $_array = null;
/**
* Constructor.
*
* @param array $data Data.
*/
public function __construct(array $data)
{
foreach ($data as &$datum) {
if ($datum instanceof Bag) {
continue;
}
if (is_scalar($datum) || null === $datum) {
$datum = new Scalar($datum);
} elseif (is_array($datum)) {
$datum = new static($datum);
}
}
$this->_array = $data;
return;
}
/**
* Get array.
*
* @return array
*/
public function getArray()
{
return $this->_array;
}
}
================================================
FILE: Model/Bag/Scalar.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Model\Bag;
/**
* Class \Hoa\Ruler\Model\Bag\Scalar.
*
* Bag for a scalar.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Scalar extends Bag
{
/**
* Value.
*
* @var scalar
*/
protected $_value = null;
/**
* Constructor.
*
* @param string $value Value.
*/
public function __construct($value)
{
$this->_value = $value;
return;
}
/**
* Get content of the bag.
*
* @return scalar
*/
public function getValue()
{
return $this->_value;
}
}
================================================
FILE: Model/Model.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Model;
use Hoa\Consistency;
use Hoa\Ruler;
use Hoa\Visitor;
/**
* Class \Hoa\Ruler\Model.
*
* Root of the model, allow to declare everything.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Model implements Visitor\Element
{
/**
* Root.
*
* @var \Hoa\Ruler\Model\Operator
*/
protected $_root = null;
/**
* Compiler.
*
* @var \Hoa\Ruler\Visitor\Compiler
*/
protected static $_compiler = null;
/**
* Set the expression with $name = 'expression'.
*
* @param string $name Name.
* @param mixed $value Value.
* @return void
*/
public function __set($name, $value)
{
if ('expression' !== $name) {
return $this->$name = $value;
}
if (is_scalar($value)) {
$value = new Bag\Scalar($value);
} elseif (is_array($value)) {
$value = new Bag\RulerArray($value);
}
$this->_root = $value;
return;
}
/**
* Get the expression.
*
* @return \Hoa\Ruler\Model\Operator
*/
public function getExpression()
{
return $this->_root;
}
/**
* Declare a function.
*
* @param string $name Name.
* @param mixed …
* @return \Hoa\Ruler\Model\Operator
*/
public function func()
{
$arguments = func_get_args();
$name = array_shift($arguments);
return $this->_operator($name, $arguments, true);
}
/**
* Declare an operation.
*
* @param string $name Name.
* @param array $arguments Arguments.
* @return \Hoa\Ruler\Model\Operator
*/
public function operation($name, array $arguments)
{
return $this->_operator($name, $arguments, false);
}
/**
* Create an operator object.
*
* @param string $name Name.
* @param array $arguments Arguments.
* @param bool $isFunction Whether it is a function or not.
* @return \Hoa\Ruler\Model\Operator
*/
public function _operator($name, array $arguments, $isFunction)
{
return new Operator(mb_strtolower($name), $arguments, $isFunction);
}
/**
* Declare an operation.
*
* @param string $name Name.
* @param array $arguments Arguments.
* @return \Hoa\Ruler\Model\Operator
*/
public function __call($name, array $arguments)
{
return $this->operation($name, $arguments);
}
/**
* Declare a variable.
*
* @parma string $id ID.
* @return \Hoa\Ruler\Model\Bag\Context
*/
public function variable($id)
{
return new Bag\Context($id);
}
/**
* Accept a visitor.
*
* @param \Hoa\Visitor\Visit $visitor Visitor.
* @param mixed &$handle Handle (reference).
* @param mixed $eldnah Handle (no reference).
* @return mixed
*/
public function accept(
Visitor\Visit $visitor,
&$handle = null,
$eldnah = null
) {
return $visitor->visit($this, $handle, $eldnah);
}
/**
* Transform the object as a string.
*
* @return string
*/
public function __toString()
{
if (null === static::$_compiler) {
static::$_compiler = new Ruler\Visitor\Compiler();
}
return static::$_compiler->visit($this);
}
}
/**
* Flex entity.
*/
Consistency::flexEntity('Hoa\Ruler\Model\Model');
================================================
FILE: Model/Operator.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Model;
use Hoa\Ruler;
use Hoa\Visitor;
/**
* Class \Hoa\Ruler\Model\Operator.
*
* Represent an operator or a function (in prefixed notation).
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Operator
extends Ruler\Model\Bag\Context
implements Visitor\Element
{
/**
* Lazy evaluation should break.
*
* @const bool
*/
const LAZY_BREAK = false;
/**
* Lazy evaluation should continue.
*
* @const bool
*/
const LAZY_CONTINUE = true;
/**
* Name.
*
* @var string
*/
protected $_name = null;
/**
* Arguments.
*
* @var array
*/
protected $_arguments = null;
/**
* Whether the operator is a function.
*
* @var bool
*/
protected $_function = true;
/**
* Whether the operator is lazy or not.
*
* @var bool
*/
protected $_laziness = false;
/**
* Constructor.
*
* @param string $name Name.
* @param array $arguments Arguments.
* @param bool $isFunction Whether it is a function.
*/
public function __construct(
$name,
array $arguments = [],
$isFunction = true
) {
$this->setName($name);
$this->setArguments($arguments);
$this->setFunction($isFunction);
return;
}
/**
* Set name.
*
* @param string $name Name.
* @return string
*/
protected function setName($name)
{
$old = $this->_name;
$this->_name = $name;
$this->setLaziness('and' === $name || 'or' === $name);
return $old;
}
/**
* Get name.
*
* @return string
*/
public function getName()
{
return $this->_name;
}
/**
* Set arguments.
*
* @param array $arguments Arguments.
* @return array
*/
protected function setArguments(array $arguments)
{
foreach ($arguments as &$argument) {
if (is_scalar($argument) || null === $argument) {
$argument = new Bag\Scalar($argument);
} elseif (is_array($argument)) {
$argument = new Bag\RulerArray($argument);
}
}
$old = $this->_arguments;
$this->_arguments = $arguments;
return $old;
}
/**
* Get arguments.
*
* @return array
*/
public function getArguments()
{
return $this->_arguments;
}
/**
* Set whether the operator is a function or not.
*
* @param bool $isFunction Is a function or not.
* @return bool
*/
protected function setFunction($isFunction)
{
$old = $this->_function;
$this->_function = $isFunction;
return $old;
}
/**
* Check if the operator is a function or not.
*
* @return bool
*/
public function isFunction()
{
return $this->_function;
}
/**
* Set whether the operator is lazy or not.
*
* @param bool $isLazy Is a lazy operator or not.
* @return bool
*/
protected function setLaziness($isLazy)
{
$old = $this->_laziness;
$this->_laziness = $isLazy;
return $old;
}
/**
* Check if the operator is lazy or not.
*
* @return bool
*/
public function isLazy()
{
return $this->_laziness;
}
/**
* Check whether we should break the lazy evaluation or not.
*
* @param mixed $value Value to check.
* @return bool
*/
public function shouldBreakLazyEvaluation($value)
{
switch ($this->getName()) {
case 'and':
if (false === $value) {
return self::LAZY_BREAK;
}
break;
case 'or':
if (true === $value) {
return self::LAZY_BREAK;
}
break;
}
return self::LAZY_CONTINUE;
}
/**
* Check if the operator is a token of the grammar or not.
*
* @param string $operator Operator.
* @return bool
*/
public static function isToken($operator)
{
static $_tokens = ['not', 'and', 'or', 'xor'];
return true === in_array($operator, $_tokens);
}
/**
* Accept a visitor.
*
* @param \Hoa\Visitor\Visit $visitor Visitor.
* @param mixed &$handle Handle (reference).
* @param mixed $eldnah Handle (no reference).
* @return mixed
*/
public function accept(
Visitor\Visit $visitor,
&$handle = null,
$eldnah = null
) {
return $visitor->visit($this, $handle, $eldnah);
}
}
================================================
FILE: README.md
================================================
<p align="center">
<img src="https://static.hoa-project.net/Image/Hoa.svg" alt="Hoa" width="250px" />
</p>
---
<p align="center">
<a href="https://travis-ci.org/hoaproject/Ruler"><img src="https://img.shields.io/travis/hoaproject/Ruler/master.svg" alt="Build status" /></a>
<a href="https://coveralls.io/github/hoaproject/Ruler?branch=master"><img src="https://img.shields.io/coveralls/hoaproject/Ruler/master.svg" alt="Code coverage" /></a>
<a href="https://packagist.org/packages/hoa/ruler"><img src="https://img.shields.io/packagist/dt/hoa/ruler.svg" alt="Packagist" /></a>
<a href="https://hoa-project.net/LICENSE"><img src="https://img.shields.io/packagist/l/hoa/ruler.svg" alt="License" /></a>
</p>
<p align="center">
Hoa is a <strong>modular</strong>, <strong>extensible</strong> and
<strong>structured</strong> set of PHP libraries.<br />
Moreover, Hoa aims at being a bridge between industrial and research worlds.
</p>
# Hoa\Ruler
[](https://webchat.freenode.net/?channels=#hoaproject)
[](https://gitter.im/hoaproject/central)
[](https://central.hoa-project.net/Documentation/Library/Ruler)
[](https://waffle.io/hoaproject/ruler)
This library allows to manipulate a rule engine. Rules can be written
by using a dedicated language, very close to SQL. Therefore, they can
be written by a user and saved in a database.
Such rules are useful, for example, for commercial solutions that need
to manipulate promotion or special offer rules written by a user. To
quote [Wikipedia](https://en.wikipedia.org/wiki/Business_rules_engine):
> A business rules engine is a software system that executes one or more
> business rules in a runtime production environment. The rules might come from
> legal regulation (“An employee can be fired for any reason or no reason but
> not for an illegal reason”), company policy (“All customers that spend more
> than $100 at one time will receive a 10% discount”), or other sources. A
> business rule system enables these company policies and other operational
> decisions to be defined, tested, executed and maintained separately from
> application code.
[Learn more](https://central.hoa-project.net/Documentation/Library/Ruler).
## Installation
With [Composer](https://getcomposer.org/), to include this library into
your dependencies, you need to
require [`hoa/ruler`](https://packagist.org/packages/hoa/ruler):
```sh
$ composer require hoa/ruler '~2.0'
```
For more installation procedures, please read [the Source
page](https://hoa-project.net/Source.html).
## Testing
Before running the test suites, the development dependencies must be installed:
```sh
$ composer install
```
Then, to run all the test suites:
```sh
$ vendor/bin/hoa test:run
```
For more information, please read the [contributor
guide](https://hoa-project.net/Literature/Contributor/Guide.html).
## Quick usage
As a quick overview, we propose to see a very simple example that manipulates a
simple rule with a simple context. After, we will add a new operator in the
rule. And finally, we will see how to save a rule in a database.
### Three steps
So first, we create a context with two variables: `group` and `points`, and we
then assert a rule. A context holds values to concretize a rule. A value can
also be the result of a callable. Thus:
```php
$ruler = new Hoa\Ruler\Ruler();
// 1. Write a rule.
$rule = 'group in ["customer", "guest"] and points > 30';
// 2. Create a context.
$context = new Hoa\Ruler\Context();
$context['group'] = 'customer';
$context['points'] = function () {
return 42;
};
// 3. Assert!
var_dump(
$ruler->assert($rule, $context)
);
/**
* Will output:
* bool(true)
*/
```
In the next example, we have a `User` object and a context that is populated
dynamically (when the `user` variable is concretized, two new variables, `group`
and `points` are created). Moreover, we will create a new operator/function
called `logged`. There is no difference between an operator and a function
except that an operator has two operands (so arguments).
### Adding operators and functions
For now, we have the following operators/functions by default: `and`, `or`,
`xor`, `not`, `=` (`is` as an alias), `!=`, `>`, `>=`, `<`, `<=`, `in` and
`sum`. We can add our own by different way. The simplest and volatile one is
given in the following example. Thus:
```php
// The User object.
class User
{
const DISCONNECTED = 0;
const CONNECTED = 1;
public $group = 'customer';
public $points = 42;
protected $_status = 1;
public function getStatus()
{
return $this->_status;
}
}
$ruler = new Hoa\Ruler\Ruler();
// New rule.
$rule = 'logged(user) and group in ["customer", "guest"] and points > 30';
// New context.
$context = new Hoa\Ruler\Context();
$context['user'] = function () use ($context) {
$user = new User();
$context['group'] = $user->group;
$context['points'] = $user->points;
return $user;
};
// We add the logged() operator.
$ruler->getDefaultAsserter()->setOperator('logged', function (User $user) {
return $user::CONNECTED === $user->getStatus();
});
// Finally, we assert the rule.
var_dump(
$ruler->assert($rule, $context)
);
/**
* Will output:
* bool(true)
*/
```
Also, if a variable in the context is an array, we can access to its values from
a rule with the same syntax as PHP. For example, if the `a` variable is an
array, we can write `a[0]` to access to the value associated to the `0` key. It
works as an hashmap (PHP array implementation), so we can have strings & co. as
keys. In the same way, if a variable is an object, we can call a method on it.
For example, if the `a` variable is an array where the value associated to the
first key is an object with a `foo` method, we can write: `a[0].foo(b)` where
`b` is another variable in the context. Also, we can access to the public
attributes of an object. Obviously, we can mixe array and object accesses.
Please, take a look at the grammar (`hoa://Library/Ruler/Grammar.pp`) to see all
the possible constructions.
### Saving a rule
Now, we have two options to save the rule, for example, in a database. Either we
save the rule as a string directly, or we will save the serialization of the
rule which will avoid further interpretations. In the next example, we see how
to serialize and unserialize a rule by using the `Hoa\Ruler\Ruler::interpret`
static method:
```php
$database->save(
serialize(
Hoa\Ruler\Ruler::interpret(
'logged(user) and group in ["customer", "guest"] and points > 30'
)
)
);
```
And for next executions:
```php
$rule = unserialize($database->read());
var_dump(
$ruler->assert($rule, $context)
);
```
When a rule is interpreted, its object model is created. We serialize and
unserialize this model. To see the PHP code needed to create such a model, we
can print the model itself (as an example). Thus:
```php
echo Hoa\Ruler\Ruler::interpret(
'logged(user) and group in ["customer", "guest"] and points > 30'
);
/**
* Will output:
* $model = new \Hoa\Ruler\Model();
* $model->expression =
* $model->and(
* $model->func(
* 'logged',
* $model->variable('user')
* ),
* $model->and(
* $model->in(
* $model->variable('group'),
* [
* 'customer',
* 'guest'
* ]
* ),
* $model->{'>'}(
* $model->variable('points'),
* 30
* )
* )
* );
*/
```
Have fun!
## Documentation
The
[hack book of `Hoa\Ruler`](https://central.hoa-project.net/Documentation/Library/Ruler) contains
detailed information about how to use this library and how it works.
To generate the documentation locally, execute the following commands:
```sh
$ composer require --dev hoa/devtools
$ vendor/bin/hoa devtools:documentation --open
```
More documentation can be found on the project's website:
[hoa-project.net](https://hoa-project.net/).
## Getting help
There are mainly two ways to get help:
* On the [`#hoaproject`](https://webchat.freenode.net/?channels=#hoaproject)
IRC channel,
* On the forum at [users.hoa-project.net](https://users.hoa-project.net).
## Contribution
Do you want to contribute? Thanks! A detailed [contributor
guide](https://hoa-project.net/Literature/Contributor/Guide.html) explains
everything you need to know.
## License
Hoa is under the New BSD License (BSD-3-Clause). Please, see
[`LICENSE`](https://hoa-project.net/LICENSE) for details.
## Related projects
The following projects are using this library:
* [RulerZ](https://github.com/K-Phoen/rulerz), Powerful implementation of the
Specification pattern in PHP,
* [ownCloud](https://owncloud.org/), A safe home for all your data,
* [PhpMetrics](http://www.phpmetrics.org/), Static analysis tool for PHP,
* [`hiqdev/php-billing`](https://github.com/hiqdev/php-billing), a billing library in PHP
* [`atoum/ruler-extension`](https://github.com/atoum/ruler-extension), This
extension allows to filter test results in [atoum](http://atoum.org/).
================================================
FILE: Ruler.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler;
use Hoa\Compiler;
use Hoa\Consistency;
use Hoa\File;
use Hoa\Visitor as HoaVisitor;
/**
* Class \Hoa\Ruler.
*
* Ruler helpers.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Ruler
{
/**
* Compiler.
*
* @var \Hoa\Compiler\Llk\Parser
*/
protected static $_compiler = null;
/**
* Interpreter.
*
* @var \Hoa\Ruler\Visitor\Interpreter
*/
protected static $_interpreter = null;
/**
* Default asserter.
*
* @var \Hoa\Visitor\Visit
*/
protected $_asserter = null;
/**
* Default asserter.
*
* @var \Hoa\Ruler\Visitor\Asserter
*/
protected static $_defaultAsserter = null;
/**
* Assert.
*
* @param mixed $rule Rule (string or model)
* @param \Hoa\Ruler\Context $context Context.
* @return bool
* @throws \Hoa\Ruler\Exception
*/
public function assert($rule, Context $context = null)
{
if (is_string($rule)) {
$rule = static::interpret($rule);
}
if (null === $context) {
$context = new Context();
}
return $this->getAsserter($context)->visit($rule);
}
/**
* Short interpreter.
*
* @param string $rule Rule.
* @return \Hoa\Ruler\Model
* @throws \Hoa\Ruler\Exception
*/
public static function interpret($rule)
{
return static::getInterpreter()->visit(
static::getCompiler()->parse($rule)
);
}
/**
* Get interpreter.
*
* @return \Hoa\Ruler\Visitor\Interpreter
*/
public static function getInterpreter()
{
if (null === static::$_interpreter) {
static::$_interpreter = new Visitor\Interpreter();
}
return static::$_interpreter;
}
/**
* Set current asserter.
*
* @param \Hoa\Visitor\Visit $visitor Visitor.
* @return \Hoa\Visitor\Visit
*/
public function setAsserter(HoaVisitor\Visit $visitor)
{
$old = $this->_asserter;
$this->_asserter = $visitor;
return $old;
}
/**
* Get asserter.
*
* @param \Hoa\Ruler\Context $context Context.
* @return \Hoa\Visitor\Visit
*/
public function getAsserter(Context $context = null)
{
if (null === $asserter = $this->_asserter) {
return static::getDefaultAsserter($context);
}
if (null !== $context) {
$asserter->setContext($context);
}
return $asserter;
}
/**
* Get default asserter.
*
* @param \Hoa\Ruler\Context $context Context.
* @return \Hoa\Ruler\Visitor\Asserter
*/
public static function getDefaultAsserter(Context $context = null)
{
if (null === static::$_defaultAsserter) {
static::$_defaultAsserter = new Visitor\Asserter($context);
}
if (null !== $context) {
static::$_defaultAsserter->setContext($context);
}
return static::$_defaultAsserter;
}
/**
* Get compiler.
*
* @return \Hoa\Compiler\Llk\Parser
*/
public static function getCompiler()
{
if (null === static::$_compiler) {
static::$_compiler = Compiler\Llk::load(
new File\Read('hoa://Library/Ruler/Grammar.pp')
);
}
return static::$_compiler;
}
}
/**
* Flex entity.
*/
Consistency::flexEntity('Hoa\Ruler\Ruler');
================================================
FILE: Test/Integration/Documentation.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Integration;
use Hoa\Ruler as LUT;
use Hoa\Test;
/**
* Class \Hoa\Ruler\Test\Integration\Documentation.
*
* Test suite of the examples in the documentation.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Documentation extends Test\Integration\Suite implements Test\Decorrelated
{
public function case_classical()
{
$this
->given(
$ruler = new LUT(),
$rule = 'group in ["customer", "guest"] and points > 30'
);
$this->next_case_classical($ruler, $rule);
}
public function next_case_classical($ruler, $rule)
{
$this
->given(
$context = new LUT\Context(),
$context['group'] = $this->sample(
$this->realdom->regex('/customer|guest/')
),
$context['points'] = function () {
return 42;
}
)
->when($result = $ruler->assert($rule, $context))
->then
->boolean($result)
->isTrue()
->given($context['points'] = 29)
->when($result = $ruler->assert($rule, $context))
->then
->boolean($result)
->isFalse();
}
public function case_new_operators()
{
$this
->given(
$user = new \Mock\StdClass(),
$user->group = 'customer',
$user->points = 42,
$user->status = true,
$ruler = new LUT(),
$rule = 'logged(user) and group in ["customer", "guest"] and points > 30',
$context = new LUT\Context(),
$context['user'] = function () use ($user, $context) {
$context['group'] = $user->group;
$context['points'] = $user->points;
return $user;
}
)
->when(
$ruler->getDefaultAsserter()->setOperator('logged', function ($user) {
return $user->status;
}),
$result = $ruler->assert($rule, $context)
)
->then
->boolean($result)
->isTrue()
->given($user->status = false)
->when($result = $ruler->assert($rule, $context))
->then
->boolean($result)
->isFalse();
}
public function case_interpret()
{
$this
->given(
$model = LUT::interpret('group in ["customer", "guest"] and points > 30')
)
->when($ledom = unserialize(serialize($model)))
->then
->object($model)
->isEqualTo($ledom);
$this->next_case_classical(new LUT(), $model);
}
public function case_compile()
{
$expectedResult = <<<'RESULT'
$model = new \Hoa\Ruler\Model();
$model->expression =
$model->and(
$model->func(
'logged',
$model->variable('user')
),
$model->and(
$model->in(
$model->variable('group'),
[
'customer',
'guest'
]
),
$model->{'>'}(
$model->variable('points'),
30
)
)
);
RESULT;
$this
->when($result = LUT::interpret(
'logged(user) and group in ["customer", "guest"] and points > 30'
) . '')
->then
->string($result)
->isEqualTo($expectedResult);
}
}
================================================
FILE: Test/Integration/Model/Operator.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Integration\Model;
use Hoa\Ruler as LUT;
use Hoa\Test;
/**
* Class \Hoa\Ruler\Test\Integration\Model\Operator.
*
* Test suite of the operator object of the model.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Operator extends Test\Integration\Suite
{
public function case_lazy_and()
{
$this
->given(
$ruler = new LUT(),
$fExecuted = false,
$gExecuted = false,
$asserter = $ruler->getDefaultAsserter(),
$asserter->setOperator(
'f',
function ($a = false) use (&$fExecuted) {
$fExecuted = true;
return $a;
}
),
$asserter->setOperator(
'g',
function ($b = false) use (&$gExecuted) {
$gExecuted = true;
return $b;
}
),
$rule = 'f(false) and g(true)'
)
->when($result = $ruler->assert($rule, new LUT\Context()))
->then
->boolean($result)
->isFalse()
->boolean($fExecuted)
->isTrue()
->boolean($gExecuted)
->isFalse()
->given(
$fExecuted = false,
$gExecuted = false,
$rule = 'f(true) and g(true)'
)
->when($result = $ruler->assert($rule, new LUT\Context()))
->then
->boolean($result)
->isTrue()
->boolean($fExecuted)
->isTrue()
->boolean($gExecuted)
->isTrue()
->given(
$fExecuted = false,
$gExecuted = false,
$rule = 'f(true) and g(false)'
)
->when($result = $ruler->assert($rule, new LUT\Context()))
->then
->boolean($result)
->isFalse()
->boolean($fExecuted)
->isTrue()
->boolean($gExecuted)
->isTrue()
->given(
$fExecuted = false,
$gExecuted = false,
$rule = 'f(false) and g(false)'
)
->when($result = $ruler->assert($rule, new LUT\Context()))
->then
->boolean($result)
->isFalse()
->boolean($fExecuted)
->isTrue()
->boolean($gExecuted)
->isFalse();
}
public function case_lazy_or()
{
$this
->given(
$ruler = new LUT(),
$fExecuted = false,
$gExecuted = false,
$asserter = $ruler->getDefaultAsserter(),
$asserter->setOperator(
'f',
function ($a) use (&$fExecuted) {
$fExecuted = true;
return $a;
}
),
$asserter->setOperator(
'g',
function ($b) use (&$gExecuted) {
$gExecuted = true;
return $b;
}
),
$rule = 'f(false) or g(true)'
)
->when($result = $ruler->assert($rule, new LUT\Context()))
->then
->boolean($result)
->isTrue()
->boolean($fExecuted)
->isTrue()
->boolean($gExecuted)
->isTrue()
->given(
$fExecuted = false,
$gExecuted = false,
$rule = 'f(true) or g(true)'
)
->when($result = $ruler->assert($rule, new LUT\Context()))
->then
->boolean($result)
->isTrue()
->boolean($fExecuted)
->isTrue()
->boolean($gExecuted)
->isFalse()
->given(
$fExecuted = false,
$gExecuted = false,
$rule = 'f(true) or g(false)'
)
->when($result = $ruler->assert($rule, new LUT\Context()))
->then
->boolean($result)
->isTrue()
->boolean($fExecuted)
->isTrue()
->boolean($gExecuted)
->isFalse()
->given(
$fExecuted = false,
$gExecuted = false,
$rule = 'f(false) or g(false)'
)
->when($result = $ruler->assert($rule, new LUT\Context()))
->then
->boolean($result)
->isFalse()
->boolean($fExecuted)
->isTrue()
->boolean($gExecuted)
->isTrue();
}
}
================================================
FILE: Test/Unit/Context.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Unit;
use Hoa\Ruler as LUT;
use Hoa\Ruler\Context as CUT;
use Hoa\Test;
/**
* Class \Hoa\Ruler\Test\Unit\Context.
*
* Test suite of the context.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Context extends Test\Unit\Suite
{
public function case_array_access()
{
$this
->when($context = new CUT())
->then
->object($context)
->isInstanceOf('ArrayAccess');
}
public function case_exists_set_get()
{
$this
->given(
$key = 'foo',
$value = 'bar',
$context = new CUT()
)
->then
->boolean(isset($context[$key]))
->isFalse()
->when($context[$key] = $value)
->then
->boolean(isset($context[$key]))
->isTrue()
->string($context[$key])
->isEqualTo($value);
}
public function case_exception_when_getting_unspecified_key()
{
$this
->given($context = new CUT())
->exception(function () use ($context) {
$context['foo'];
})
->isInstanceOf('Hoa\Ruler\Exception');
}
public function case_unset()
{
$this
->given(
$key = 'foo',
$value = 'bar',
$context = new CUT()
)
->then
->boolean(isset($context[$key]))
->isFalse()
->when($context[$key] = $value)
->then
->boolean(isset($context[$key]))
->isTrue()
->when(function () use ($context, $key) {
unset($context[$key]);
})
->then
->boolean(isset($context[$key]))
->isFalse();
}
public function case_callable_closure()
{
$this
->given(
$context = new CUT(),
$context['foo'] = function () {
return fakeCallable();
}
)
->when($result = $context['foo'])
->then
->boolean($result)
->isTrue();
}
public function case_callable_user_function()
{
$this
->given(
$context = new CUT(),
$context['foo'] = __NAMESPACE__ . '\fakeCallable'
)
->when($result = $context['foo'])
->then
->boolean($result)
->isTrue();
}
public function case_callable_internal_function()
{
$this
->given(
$context = new CUT(),
$context['foo'] = 'var_dump'
)
->when($result = $context['foo'])
->then
->string($result)
->isEqualTo('var_dump');
}
public function case_callable_method()
{
$this
->given(
$context = new CUT(),
$context['foo'] = [$this, 'fakeCallable']
)
->when($result = $context['foo'])
->then
->boolean($result)
->isTrue();
}
public function case_callable_xcallable()
{
$this
->given(
$context = new CUT(),
$context['foo'] = xcallable($this, 'fakeCallable')
)
->when($result = $context['foo'])
->then
->boolean($result)
->isTrue();
}
public function case_callable_cache()
{
$this
->given(
$context = new CUT(),
$context['foo'] = function () {
static $i = 0;
return $i++;
}
)
->when($result = $context['foo'])
->then
->integer($result)
->isEqualTo(0)
->when($result = $context['foo'])
->then
->integer($result)
->isEqualTo(0);
}
public function case_callable_no_cache()
{
$this
->given(
$context = new CUT(),
$context['foo'] = new LUT\DynamicCallable(function () {
static $i = 0;
return $i++;
})
)
->when($result = $context['foo'])
->then
->integer($result)
->isEqualTo(0)
->when($result = $context['foo'])
->then
->integer($result)
->isEqualTo(1);
}
public function case_callable_argument()
{
$this
->given(
$self = $this,
$context = new CUT(),
$context['foo'] = function () use ($self, $context) {
$arguments = func_get_args();
$self
->integer(count($arguments))
->isEqualTo(1)
->object($arguments[0])
->isIdenticalTo($context);
}
)
->when($result = $context['foo']);
}
public function case_access_as_properties()
{
$this
->given(
$self = $this,
$context = new CUT(),
$context['foo'] = 42
)
->when($result = $context->foo)
->then
->integer($result)
->isEqualTo(42)
->when($context->bar = 24)
->then
->integer($context['bar'])
->isEqualTo(24);
}
public function fakeCallable()
{
return fakeCallable();
}
}
function fakeCallable()
{
return true;
}
================================================
FILE: Test/Unit/DynamicCallable.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Unit;
use Hoa\Consistency;
use Hoa\Ruler\DynamicCallable as SUT;
use Hoa\Test;
/**
* Class \Hoa\Ruler\Test\Unit\DynamicCallable.
*
* Test suite of the dynamic callable.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class DynamicCallable extends Test\Unit\Suite
{
public function case_is_a_xcallable()
{
$this
->when($result = new SUT(function () {}))
->then
->object($result)
->isInstanceOf(Consistency\Xcallable::class);
}
}
================================================
FILE: Test/Unit/Exception/Asserter.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Unit\Exception;
use Hoa\Ruler as LUT;
use Hoa\Ruler\Exception\Asserter as SUT;
use Hoa\Test;
/**
* Class \Hoa\Ruler\Test\Unit\Exception\Asserter.
*
* Test suite of the asserter exception.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Asserter extends Test\Unit\Suite
{
public function case_is_an_exception()
{
$this
->when($result = new SUT('foo'))
->then
->object($result)
->isInstanceOf(LUT\Exception::class);
}
}
================================================
FILE: Test/Unit/Exception/Exception.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Unit\Exception;
use Hoa\Exception as HoaException;
use Hoa\Ruler\Exception as SUT;
use Hoa\Test;
/**
* Class \Hoa\Ruler\Test\Unit\Exception\Exception.
*
* Test suite of the main exception class.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Exception extends Test\Unit\Suite
{
public function case_is_a_hoa_exception()
{
$this
->when($result = new SUT('foo'))
->then
->object($result)
->isInstanceOf(HoaException::class);
}
}
================================================
FILE: Test/Unit/Exception/Interpreter.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Unit\Exception;
use Hoa\Ruler as LUT;
use Hoa\Ruler\Exception\Interpreter as SUT;
use Hoa\Test;
/**
* Class \Hoa\Ruler\Test\Unit\Exception\Interpreter.
*
* Test suite of the asserter exception.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Interpreter extends Test\Unit\Suite
{
public function case_is_an_exception()
{
$this
->when($result = new SUT('foo'))
->then
->object($result)
->isInstanceOf(LUT\Exception::class);
}
}
================================================
FILE: Test/Unit/Issue.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Unit;
use Hoa\Ruler as LUT;
use Hoa\Test;
/**
* Class \Hoa\Ruler\Test\Unit\Issue.
*
* Test suite of detected issues.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Issue extends Test\Unit\Suite implements Test\Decorrelated
{
public function case_github_50()
{
$this
->given(
$ruler = new LUT(),
$rule = 'variable',
$context = new LUT\Context(),
$context['variable'] = 'file'
)
->when(function () use ($ruler, $rule, $context) {
$ruler->assert($rule, $context);
})
->error()
->notExists();
}
public function case_github_70()
{
$this
->given(
$ruler = new LUT(),
$rule = 'variable["foo"] is null',
$context = new LUT\Context(),
$context['variable'] = ['foo' => null]
)
->when($result = $ruler->assert($rule, $context))
->then
->boolean($result)
->isTrue();
}
public function case_github_100_1()
{
$this
->given(
$ruler = new LUT(),
$rule = '(false and true) or true'
)
->when($result = $ruler->assert($rule))
->then
->boolean($result)
->isTrue();
}
public function case_github_100_2()
{
$this
->given(
$ruler = new LUT(),
$rule = 'false and true or true'
)
->when($result = $ruler->assert($rule))
->then
->boolean($result)
->isTrue();
}
public function case_github_100_3()
{
$this
->given(
$ruler = new LUT(),
$rule = 'true or true and false'
)
->when($result = $ruler->assert($rule))
->then
->boolean($result)
->isTrue();
}
}
================================================
FILE: Test/Unit/Model/Bag/Bag.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Unit\Model\Bag;
use Hoa\Test;
use Hoa\Visitor;
use Mock\Hoa\Ruler\Model\Bag as SUT;
/**
* Class \Hoa\Ruler\Test\Unit\Model\Bag\Bag.
*
* Test suite of the bag generic class.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Bag extends Test\Unit\Suite
{
public function case_is_a_visitor()
{
$this
->when($result = new SUT())
->then
->object($result)
->isInstanceOf(Visitor\Element::class);
}
}
================================================
FILE: Test/Unit/Model/Bag/Context.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSE DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTER HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISIY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Unit\Model\Bag;
use Hoa\Ruler as LUT;
use Hoa\Ruler\Model\Bag\Context as SUT;
use Hoa\Test;
/**
* Class \Hoa\Ruler\Test\Unit\Model\Bag\Context.
*
* Test suite of the context bag class.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Context extends Test\Unit\Suite
{
public function case_is_a_bag()
{
$this
->when($result = new SUT('foobar'))
->then
->object($result)
->isInstanceOf(LUT\Model\Bag::class);
}
public function case_constructor()
{
$this
->given($id = 'foobar')
->when($result = new SUT($id))
->then
->string($result->getId())
->isEqualTo($id)
->array($result->getDimensions())
->isEmpty();
}
public function case_scalar_index_from_root()
{
return $this->_case_index_from_root(
'baz',
new LUT\Model\Bag\Scalar('baz')
);
}
public function case_array_index_from_root()
{
return $this->_case_index_from_root(
['baz'],
new LUT\Model\Bag\RulerArray(['baz'])
);
}
public function case_bag_index_from_root()
{
return $this->_case_index_from_root(
new LUT\Model\Bag\Scalar('baz'),
new LUT\Model\Bag\Scalar('baz')
);
}
protected function _case_index_from_root($index, $expectedIndex)
{
$this
->given(
$id = 'foobar',
$context = new SUT($id)
)
->when($result = $context->index($index))
->then
->object($result)
->isIdenticalTo($context)
->array($result->getDimensions())
->isEqualTo([
[
SUT::ACCESS_TYPE => SUT::ARRAY_ACCESS,
SUT::ACCESS_VALUE => $expectedIndex
]
]);
}
public function case_scalar_index()
{
return $this->_case_index(
'baz',
new LUT\Model\Bag\Scalar('baz')
);
}
public function case_array_index()
{
return $this->_case_index(
['baz'],
new LUT\Model\Bag\RulerArray(['baz'])
);
}
public function case_bag_index()
{
return $this->_case_index(
new LUT\Model\Bag\Scalar('baz'),
new LUT\Model\Bag\Scalar('baz')
);
}
protected function _case_index($index, $expectedIndex)
{
$this
->given(
$id = 'foobar',
$context = new SUT($id),
$context->index(new LUT\Model\Bag\Scalar('qux'))
)
->when($result = $context->index($index))
->then
->object($result)
->isIdenticalTo($context)
->array($result->getDimensions())
->isEqualTo([
[
SUT::ACCESS_TYPE => SUT::ARRAY_ACCESS,
SUT::ACCESS_VALUE => new LUT\Model\Bag\Scalar('qux')
],
[
SUT::ACCESS_TYPE => SUT::ARRAY_ACCESS,
SUT::ACCESS_VALUE => $expectedIndex
]
]);
}
public function case_attribute()
{
$this
->given(
$id = 'foobar',
$attribute = 'bazqux',
$context = new SUT($id)
)
->when($result = $context->attribute($attribute))
->then
->object($result)
->isIdenticalTo($context)
->array($result->getDimensions())
->isEqualTo([
[
SUT::ACCESS_TYPE => SUT::ATTRIBUTE_ACCESS,
SUT::ACCESS_VALUE => $attribute
]
]);
}
public function case_call()
{
$this
->given(
$id = 'foobar',
$method = new LUT\Model\Operator('f'),
$context = new SUT($id)
)
->when($result = $context->call($method))
->then
->object($result)
->isIdenticalTo($context)
->array($result->getDimensions())
->isEqualTo([
[
SUT::ACCESS_TYPE => SUT::METHOD_ACCESS,
SUT::ACCESS_VALUE => $method
]
]);
}
public function case_multiple_dimension_types()
{
$this
->given(
$id = 'foobar',
$index = 'baz',
$attribute = 'qux',
$method = new LUT\Model\Operator('f'),
$context = new SUT($id),
$context->attribute($attribute),
$context->index($index),
$context->call($method)
)
->when($result = $context->getDimensions())
->then
->array($result)
->isEqualTo([
[
SUT::ACCESS_TYPE => SUT::ATTRIBUTE_ACCESS,
SUT::ACCESS_VALUE => $attribute
],
[
SUT::ACCESS_TYPE => SUT::ARRAY_ACCESS,
SUT::ACCESS_VALUE => new LUT\Model\Bag\Scalar($index)
],
[
SUT::ACCESS_TYPE => SUT::METHOD_ACCESS,
SUT::ACCESS_VALUE => $method
]
]);
}
}
================================================
FILE: Test/Unit/Model/Bag/RulerArray.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Unit\Model\Bag;
use Hoa\Ruler as LUT;
use Hoa\Ruler\Model\Bag\RulerArray as SUT;
use Hoa\Test;
/**
* Class \Hoa\Ruler\Test\Unit\Model\Bag\RulerArray.
*
* Test suite of the array bag class.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class RulerArray extends Test\Unit\Suite
{
public function case_is_a_bag()
{
$this
->when($result = new SUT(['foobar']))
->then
->object($result)
->isInstanceOf(LUT\Model\Bag::class);
}
public function case_constructor()
{
$this
->given($data = ['foo', ['bar'], new LUT\Model\Bag\Scalar('baz')])
->when($result = new SUT($data))
->then
->let($array = $result->getArray())
->array($array)
->hasSize(count($data))
->isEqualTo([
new LUT\Model\Bag\Scalar('foo'),
new SUT([new LUT\Model\Bag\Scalar('bar')]),
new LUT\Model\Bag\Scalar('baz')
]);
}
}
================================================
FILE: Test/Unit/Model/Bag/Scalar.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Unit\Model\Bag;
use Hoa\Ruler as LUT;
use Hoa\Ruler\Model\Bag\Scalar as SUT;
use Hoa\Test;
/**
* Class \Hoa\Ruler\Test\Unit\Model\Bag\Scalar.
*
* Test suite of the scalar bag class.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Scalar extends Test\Unit\Suite
{
public function case_is_a_bag()
{
$this
->when($result = new SUT('foobar'))
->then
->object($result)
->isInstanceOf(LUT\Model\Bag::class);
}
public function case_constructor()
{
$this
->given($scalar = 'foobar')
->when($result = new SUT($scalar))
->then
->string($result->getValue())
->isEqualTo($scalar);
}
}
================================================
FILE: Test/Unit/Model/Model.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Unit\Model;
use Hoa\Ruler as LUT;
use Hoa\Ruler\Model as SUT;
use Hoa\Test;
use Hoa\Visitor;
/**
* Class \Hoa\Ruler\Test\Unit\Model\Model.
*
* Test suite of the model root object.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Model extends Test\Unit\Suite
{
public function case_is_a_visitor_element()
{
$this
->when($result = new SUT())
->then
->object($result)
->isInstanceOf(Visitor\Element::class);
}
public function case_set_default()
{
$this
->given($model = new SUT())
->when($result = $model->foo = 'bar')
->then
->string($result)
->isEqualTo('bar')
->string($model->foo)
->isEqualTo('bar');
}
public function case_set_expression()
{
$this
->given($model = new SUT())
->when($result = $model->expression = 'foo')
->then
->string($result)
->isEqualTo('foo')
->boolean(isset($model->expression))
->isFalse();
}
public function case_get_expression_is_a_scalar()
{
$this
->given(
$model = new SUT(),
$model->expression = 'foo'
)
->when($result = $model->getExpression())
->then
->object($result)
->isEqualTo(new LUT\Model\Bag\Scalar('foo'));
}
public function case_get_expression_is_an_array()
{
$this
->given(
$model = new SUT(),
$model->expression = ['foo']
)
->when($result = $model->getExpression())
->then
->object($result)
->isEqualTo(new LUT\Model\Bag\RulerArray(['foo']));
}
public function case_get_expression()
{
$this
->given(
$model = new SUT(),
$model->expression = new LUT\Model\Operator('f')
)
->when($result = $model->getExpression())
->then
->object($result)
->isEqualTo(new LUT\Model\Operator('f'));
}
public function case_func()
{
$this
->given($model = new SUT())
->when($result = $model->func('f', 'x', 42))
->then
->object($result)
->isInstanceOf(LUT\Model\Operator::class)
->string($result->getName())
->isEqualTo('f')
->array($result->getArguments())
->isEqualTo([
new LUT\Model\Bag\Scalar('x'),
new LUT\Model\Bag\Scalar(42)
])
->boolean($result->isFunction())
->isTrue()
->boolean($result->isLazy())
->isFalse();
}
public function case_operation()
{
$this
->given($model = new SUT())
->when($result = $model->operation('f', ['x', 42]))
->then
->object($result)
->isInstanceOf(LUT\Model\Operator::class)
->string($result->getName())
->isEqualTo('f')
->array($result->getArguments())
->isEqualTo([
new LUT\Model\Bag\Scalar('x'),
new LUT\Model\Bag\Scalar(42)
])
->boolean($result->isFunction())
->isFalse()
->boolean($result->isLazy())
->isFalse();
}
public function case_operator()
{
$this
->given($model = new SUT())
->when($result = $model->_operator('f', ['x', 42], true))
->then
->object($result)
->isInstanceOf(LUT\Model\Operator::class)
->string($result->getName())
->isEqualTo('f')
->array($result->getArguments())
->isEqualTo([
new LUT\Model\Bag\Scalar('x'),
new LUT\Model\Bag\Scalar(42)
])
->boolean($result->isFunction())
->isTrue()
->boolean($result->isLazy())
->isFalse();
}
public function case_call()
{
$this
->given($model = new SUT())
->when($result = $model->f('x', 42))
->then
->object($result)
->isInstanceOf(LUT\Model\Operator::class)
->string($result->getName())
->isEqualTo('f')
->array($result->getArguments())
->isEqualTo([
new LUT\Model\Bag\Scalar('x'),
new LUT\Model\Bag\Scalar(42)
])
->boolean($result->isFunction())
->isFalse()
->boolean($result->isLazy())
->isFalse();
}
public function case_variable()
{
$this
->given($model = new SUT())
->when($result = $model->variable('x'))
->then
->object($result)
->isEqualTo(new LUT\Model\Bag\Context('x'));
}
public function case_to_string()
{
$this
->given(
$model = new SUT(),
$model->expression = $model->f('x', 42)
)
->when($result = $model->__toString())
->then
->string($result)
->isEqualTo(
'$model = new \Hoa\Ruler\Model();' . "\n" .
'$model->expression =' . "\n" .
' $model->f(' . "\n" .
' \'x\',' . "\n" .
' 42' . "\n" .
' );'
);
}
}
================================================
FILE: Test/Unit/Model/Operator.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Hoa\Ruler\Test\Unit\Model;
use Hoa\Ruler as LUT;
use Hoa\Ruler\Model\Operator as SUT;
use Hoa\Test;
use Hoa\Visitor;
/**
* Class \Hoa\Ruler\Test\Unit\Model\Operator.
*
* Test suite of the operator object of the model.
*
* @copyright Copyright © 2007-2017 Hoa community
* @license New BSD License
*/
class Operator extends Test\Unit\Suite
{
public function case_is_a_visitor_element()
{
$this
->given($name = 'foo')
->when($result = new SUT($name))
->then
->object($result)
->isInstanceOf(Visitor\Element::class);
}
public function case_is_a_context()
{
$this
->given($name = 'foo')
->when($result = new SUT($name))
->then
->object($result)
->isInstanceOf(LUT\Model\Bag\Context::class);
}
public function case_constructor()
{
$this
->given($name = 'foo')
->when($result = new SUT($name))
->then
->string($result->getName())
->isEqualTo($name)
->array($result->getArguments())
->isEmpty()
->boolean($result->isFunction())
->isTrue()
->boolean($result->isLazy())
->isFalse();
}
public function case_constructor_with_arguments()
{
$this
->given(
$name = 'foo',
$arguments = [new LUT\Model\Bag\Scalar(42)]
)
->when($result = new SUT($name, $arguments))
->then
->string($result->getName())
->isEqualTo($name)
->array($result->getArguments())
->isEqualTo($arguments)
->boolean($result->isFunction())
->isTrue()
->boolean($result->isLazy())
->isFalse();
}
public function case_constructor_with_arguments_and_function_flag()
{
$this
->given(
$name = 'foo',
$arguments = [new LUT\Model\Bag\Scalar(42)],
$isFunction = false
)
->when($result = new SUT($name, $arguments, $isFunction))
->then
->string($result->getName())
->isEqualTo($name)
->array($result->getArguments())
->isEqualTo($arguments)
->boolean($result->isFunction())
->isFalse()
->boolean($result->isLazy())
->isFalse();
}
public function case_set_name()
{
$this
->given(
$oldName = 'foo',
$name = 'bar',
$operator = new SUT('foo')
)
->when($result = $this->invoke($operator)->setName($name))
->then
->string($result)
->isEqualTo($oldName)
->boolean($operator->isLazy())
->isFalse();
}
public function case_set_name_with_the_and_operator_for_auto_laziness()
{
return $this->_case_set_name_with_auto_laziness('and');
}
public function case_set_name_with_the_or_operator_for_auto_laziness()
{
return $this->_case_set_name_with_auto_laziness('or');
}
protected function _case_set_name_with_auto_laziness($name)
{
$this
->given($operator = new SUT('foo'))
->when($result = $this->invoke($operator)->setName($name))
->then
->string($result)
->isEqualTo('foo')
->boolean($operator->isLazy())
->isTrue();
}
public function case_get_name()
{
$this
->given(
$name = 'bar',
$operator = new SUT('foo'),
$this->invoke($operator)->setName($name)
)
->when($result = $operator->getName())
->then
->string($result)
->isEqualTo($name);
}
public function case_set_arguments()
{
$this
->given(
$operator = new SUT('foo'),
$arguments = ['foo', [42], new LUT\Model\Bag\Scalar('baz')]
)
->when($result = $this->invoke($operator)->setArguments($arguments))
->then
->array($result)
->isEmpty();
}
public function case_set_arguments_not_additive()
{
$this
->given(
$operator = new SUT('foo'),
$argumentsA = [new LUT\Model\Bag\Scalar('foo')],
$argumentsB = [new LUT\Model\Bag\Scalar('bar')],
$this->invoke($operator)->setArguments($argumentsA)
)
->when($result = $this->invoke($operator)->setArguments($argumentsB))
->then
->array($result)
->isEqualTo($argumentsA)
->array($operator->getArguments())
->isEqualTo($argumentsB);
}
public function case_get_arguments()
{
$this
->given(
$operator = new SUT('foo'),
$arguments = ['foo', [42], new LUT\Model\Bag\Scalar('baz')],
$this->invoke($operator)->setArguments($arguments)
)
->when($result = $operator->getArguments())
->then
->array($result)
->isEqualTo([
new LUT\Model\Bag\Scalar('foo'),
new LUT\Model\Bag\RulerArray([42]),
new LUT\Model\Bag\Scalar('baz')
]);
}
public function case_set_function()
{
$this
->given($operator = new SUT('foo'))
->when($result = $this->invoke($operator)->setFunction(false))
->then
->boolean($result)
->isTrue();
}
public function case_is_function()
{
$this
->given(
$operator = new SUT('foo'),
$this->invoke($operator)->setFunction(true)
)
->when($result = $operator->isFunction())
->then
->boolean($result)
->isTrue();
}
public function case_is_not_function()
{
$this
->given(
$operator = new SUT('foo'),
$this->invoke($operator)->setFunction(false)
)
->when($result = $operator->isFunction())
->then
->boolean($result)
->isFalse();
}
public function case_set_laziness()
{
$this
->given($operator = new SUT('foo'))
->when($result = $this->invoke($operator)->setLaziness(false))
->then
->boolean($result)
->isFalse();
}
public function case_is_lazy()
{
$this
->given(
$operator = new SUT('foo'),
$this->invoke($operator)->setLaziness(true)
)
->when($result = $operator->isLazy())
->then
->boolean($result)
->isTrue();
}
public function case_is_not_lazy()
{
$this
->given(
$operator = new SUT('foo'),
$this->invoke($operator)->setLaziness(false)
)
->when($result = $operator->isLazy())
->then
->boolean($result)
->isFalse();
}
public function case_should_break_lazy_evaluation_with_and_operator()
{
return $this->_case_should_break_lazy_evaluation_with_x_operator(
'and',
false,
SUT::LAZY_BREAK
);
}
public function case_should_not_break_lazy_evaluation_with_and_operator()
{
return $this->_case_should_break_lazy_evaluation_with_x_operator(
'and',
true,
SUT::LAZY_CONTINUE
);
}
public function case_should_break_lazy_evaluation_with_or_operator()
{
return $this->_case_should_break_lazy_evaluation_with_x_operator(
'or',
true,
SUT::LAZY_BREAK
);
}
public function case_should_not_break_lazy_evaluation_with_or_operator()
{
return $this->_case_should_break_lazy_evaluation_with_x_operator(
'or',
false,
SUT::LAZY_CONTINUE
);
}
public function case_should_not_break_lazy_evaluation_with_any_operator()
{
return $this->_case_should_break_lazy_evaluation_with_x_operator(
'foo',
42,
SUT::LAZY_CONTINUE
);
}
protected function _case_should_break_lazy_evaluation_with_x_operator($name, $value, $expect)
{
$this
->given($operator = new SUT($name))
->when($result = $operator->shouldBreakLazyEvaluation($value))
->then
->boolean($result)
->isEqualTo($expect);
}
public function case_is_token()
{
$this
->when(function () {
foreach (['not', 'and', 'or', 'xor'] as $token) {
$this
->when($result = SUT::isToken($token))
->then
->boolean($result)
->isTrue();
}
});
}
public function case_is_not_token()
{
$this
->when($result = SUT::isToken('foo'))
->then
->boolean($result)
->isFalse();
}
}
================================================
FILE: Test/Unit/Ruler.php
================================================
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific
gitextract_hj2vqy6x/ ├── .State ├── .gitignore ├── .travis.yml ├── Bin/ │ └── Assert.php ├── CHANGELOG.md ├── Context.php ├── Documentation/ │ ├── En/ │ │ └── Index.xyl │ └── Fr/ │ └── Index.xyl ├── DynamicCallable.php ├── Exception/ │ ├── Asserter.php │ ├── Exception.php │ └── Interpreter.php ├── Grammar.pp ├── Model/ │ ├── Bag/ │ │ ├── Bag.php │ │ ├── Context.php │ │ ├── RulerArray.php │ │ └── Scalar.php │ ├── Model.php │ └── Operator.php ├── README.md ├── Ruler.php ├── Test/ │ ├── Integration/ │ │ ├── Documentation.php │ │ └── Model/ │ │ └── Operator.php │ └── Unit/ │ ├── Context.php │ ├── DynamicCallable.php │ ├── Exception/ │ │ ├── Asserter.php │ │ ├── Exception.php │ │ └── Interpreter.php │ ├── Issue.php │ ├── Model/ │ │ ├── Bag/ │ │ │ ├── Bag.php │ │ │ ├── Context.php │ │ │ ├── RulerArray.php │ │ │ └── Scalar.php │ │ ├── Model.php │ │ └── Operator.php │ ├── Ruler.php │ └── Visitor/ │ ├── Asserter.php │ ├── Compiler.php │ ├── Disassembly.php │ └── Interpreter.php ├── Visitor/ │ ├── Asserter.php │ ├── Compiler.php │ ├── Disassembly.php │ └── Interpreter.php ├── bors.toml └── composer.json
SYMBOL INDEX (395 symbols across 36 files)
FILE: Bin/Assert.php
class Assert (line 50) | class Assert extends Console\Dispatcher\Kit
method main (line 69) | public function main()
method usage (line 101) | public function usage()
FILE: Context.php
class Context (line 47) | class Context implements \ArrayAccess
method __construct (line 70) | public function __construct(array $data = [])
method offsetSet (line 84) | public function offsetSet($id, $value)
method offsetGet (line 98) | public function offsetGet($id)
method offsetExists (line 131) | public function offsetExists($id)
method offsetUnset (line 142) | public function offsetUnset($id)
method __get (line 156) | public function __get($name)
method __set (line 168) | public function __set($id, $value)
FILE: DynamicCallable.php
class DynamicCallable (line 49) | class DynamicCallable extends Consistency\Xcallable
FILE: Exception/Asserter.php
class Asserter (line 47) | class Asserter extends Exception
FILE: Exception/Exception.php
class Exception (line 50) | class Exception extends HoaException
FILE: Exception/Interpreter.php
class Interpreter (line 47) | class Interpreter extends Exception
FILE: Model/Bag/Bag.php
class Bag (line 50) | abstract class Bag implements Visitor\Element
method accept (line 60) | public function accept(
FILE: Model/Bag/Context.php
class Context (line 49) | class Context extends Bag
method __construct (line 107) | public function __construct($id)
method index (line 120) | public function index($index)
method attribute (line 142) | public function attribute($attribute)
method call (line 158) | public function call(Ruler\Model\Operator $method)
method getDimensions (line 173) | public function getDimensions()
method getId (line 183) | public function getId()
FILE: Model/Bag/RulerArray.php
class RulerArray (line 47) | class RulerArray extends Bag
method __construct (line 63) | public function __construct(array $data)
method getArray (line 87) | public function getArray()
FILE: Model/Bag/Scalar.php
class Scalar (line 47) | class Scalar extends Bag
method __construct (line 63) | public function __construct($value)
method getValue (line 75) | public function getValue()
FILE: Model/Model.php
class Model (line 51) | class Model implements Visitor\Element
method __set (line 76) | public function __set($name, $value)
method getExpression (line 98) | public function getExpression()
method func (line 110) | public function func()
method operation (line 125) | public function operation($name, array $arguments)
method _operator (line 138) | public function _operator($name, array $arguments, $isFunction)
method __call (line 150) | public function __call($name, array $arguments)
method variable (line 161) | public function variable($id)
method accept (line 174) | public function accept(
method __toString (line 187) | public function __toString()
FILE: Model/Operator.php
class Operator (line 50) | class Operator
method __construct (line 103) | public function __construct(
method setName (line 121) | protected function setName($name)
method getName (line 136) | public function getName()
method setArguments (line 147) | protected function setArguments(array $arguments)
method getArguments (line 168) | public function getArguments()
method setFunction (line 179) | protected function setFunction($isFunction)
method isFunction (line 192) | public function isFunction()
method setLaziness (line 203) | protected function setLaziness($isLazy)
method isLazy (line 216) | public function isLazy()
method shouldBreakLazyEvaluation (line 227) | public function shouldBreakLazyEvaluation($value)
method isToken (line 254) | public static function isToken($operator)
method accept (line 269) | public function accept(
FILE: Ruler.php
class Ruler (line 52) | class Ruler
method assert (line 92) | public function assert($rule, Context $context = null)
method interpret (line 112) | public static function interpret($rule)
method getInterpreter (line 124) | public static function getInterpreter()
method setAsserter (line 139) | public function setAsserter(HoaVisitor\Visit $visitor)
method getAsserter (line 153) | public function getAsserter(Context $context = null)
method getDefaultAsserter (line 172) | public static function getDefaultAsserter(Context $context = null)
method getCompiler (line 190) | public static function getCompiler()
FILE: Test/Integration/Documentation.php
class Documentation (line 50) | class Documentation extends Test\Integration\Suite implements Test\Decor...
method case_classical (line 52) | public function case_classical()
method next_case_classical (line 63) | public function next_case_classical($ruler, $rule)
method case_new_operators (line 87) | public function case_new_operators()
method case_interpret (line 122) | public function case_interpret()
method case_compile (line 136) | public function case_compile()
FILE: Test/Integration/Model/Operator.php
class Operator (line 50) | class Operator extends Test\Integration\Suite
method case_lazy_and (line 52) | public function case_lazy_and()
method case_lazy_or (line 130) | public function case_lazy_or()
FILE: Test/Unit/Context.php
class Context (line 51) | class Context extends Test\Unit\Suite
method case_array_access (line 53) | public function case_array_access()
method case_exists_set_get (line 62) | public function case_exists_set_get()
method case_exception_when_getting_unspecified_key (line 82) | public function case_exception_when_getting_unspecified_key()
method case_unset (line 92) | public function case_unset()
method case_callable_closure (line 117) | public function case_callable_closure()
method case_callable_user_function (line 132) | public function case_callable_user_function()
method case_callable_internal_function (line 145) | public function case_callable_internal_function()
method case_callable_method (line 158) | public function case_callable_method()
method case_callable_xcallable (line 171) | public function case_callable_xcallable()
method case_callable_cache (line 184) | public function case_callable_cache()
method case_callable_no_cache (line 206) | public function case_callable_no_cache()
method case_callable_argument (line 228) | public function case_callable_argument()
method case_access_as_properties (line 247) | public function case_access_as_properties()
method fakeCallable (line 265) | public function fakeCallable()
function fakeCallable (line 271) | function fakeCallable()
FILE: Test/Unit/DynamicCallable.php
class DynamicCallable (line 51) | class DynamicCallable extends Test\Unit\Suite
method case_is_a_xcallable (line 53) | public function case_is_a_xcallable()
FILE: Test/Unit/Exception/Asserter.php
class Asserter (line 51) | class Asserter extends Test\Unit\Suite
method case_is_an_exception (line 53) | public function case_is_an_exception()
FILE: Test/Unit/Exception/Exception.php
class Exception (line 51) | class Exception extends Test\Unit\Suite
method case_is_a_hoa_exception (line 53) | public function case_is_a_hoa_exception()
FILE: Test/Unit/Exception/Interpreter.php
class Interpreter (line 51) | class Interpreter extends Test\Unit\Suite
method case_is_an_exception (line 53) | public function case_is_an_exception()
FILE: Test/Unit/Issue.php
class Issue (line 50) | class Issue extends Test\Unit\Suite implements Test\Decorrelated
method case_github_50 (line 52) | public function case_github_50()
method case_github_70 (line 68) | public function case_github_70()
method case_github_100_1 (line 83) | public function case_github_100_1()
method case_github_100_2 (line 96) | public function case_github_100_2()
method case_github_100_3 (line 109) | public function case_github_100_3()
FILE: Test/Unit/Model/Bag/Bag.php
class Bag (line 51) | class Bag extends Test\Unit\Suite
method case_is_a_visitor (line 53) | public function case_is_a_visitor()
FILE: Test/Unit/Model/Bag/Context.php
class Context (line 51) | class Context extends Test\Unit\Suite
method case_is_a_bag (line 53) | public function case_is_a_bag()
method case_constructor (line 62) | public function case_constructor()
method case_scalar_index_from_root (line 74) | public function case_scalar_index_from_root()
method case_array_index_from_root (line 82) | public function case_array_index_from_root()
method case_bag_index_from_root (line 90) | public function case_bag_index_from_root()
method _case_index_from_root (line 98) | protected function _case_index_from_root($index, $expectedIndex)
method case_scalar_index (line 118) | public function case_scalar_index()
method case_array_index (line 126) | public function case_array_index()
method case_bag_index (line 134) | public function case_bag_index()
method _case_index (line 142) | protected function _case_index($index, $expectedIndex)
method case_attribute (line 167) | public function case_attribute()
method case_call (line 188) | public function case_call()
method case_multiple_dimension_types (line 209) | public function case_multiple_dimension_types()
FILE: Test/Unit/Model/Bag/RulerArray.php
class RulerArray (line 51) | class RulerArray extends Test\Unit\Suite
method case_is_a_bag (line 53) | public function case_is_a_bag()
method case_constructor (line 62) | public function case_constructor()
FILE: Test/Unit/Model/Bag/Scalar.php
class Scalar (line 51) | class Scalar extends Test\Unit\Suite
method case_is_a_bag (line 53) | public function case_is_a_bag()
method case_constructor (line 62) | public function case_constructor()
FILE: Test/Unit/Model/Model.php
class Model (line 52) | class Model extends Test\Unit\Suite
method case_is_a_visitor_element (line 54) | public function case_is_a_visitor_element()
method case_set_default (line 63) | public function case_set_default()
method case_set_expression (line 75) | public function case_set_expression()
method case_get_expression_is_a_scalar (line 87) | public function case_get_expression_is_a_scalar()
method case_get_expression_is_an_array (line 100) | public function case_get_expression_is_an_array()
method case_get_expression (line 113) | public function case_get_expression()
method case_func (line 126) | public function case_func()
method case_operation (line 147) | public function case_operation()
method case_operator (line 168) | public function case_operator()
method case_call (line 189) | public function case_call()
method case_variable (line 210) | public function case_variable()
method case_to_string (line 220) | public function case_to_string()
FILE: Test/Unit/Model/Operator.php
class Operator (line 52) | class Operator extends Test\Unit\Suite
method case_is_a_visitor_element (line 54) | public function case_is_a_visitor_element()
method case_is_a_context (line 64) | public function case_is_a_context()
method case_constructor (line 74) | public function case_constructor()
method case_constructor_with_arguments (line 90) | public function case_constructor_with_arguments()
method case_constructor_with_arguments_and_function_flag (line 109) | public function case_constructor_with_arguments_and_function_flag()
method case_set_name (line 129) | public function case_set_name()
method case_set_name_with_the_and_operator_for_auto_laziness (line 145) | public function case_set_name_with_the_and_operator_for_auto_laziness()
method case_set_name_with_the_or_operator_for_auto_laziness (line 150) | public function case_set_name_with_the_or_operator_for_auto_laziness()
method _case_set_name_with_auto_laziness (line 155) | protected function _case_set_name_with_auto_laziness($name)
method case_get_name (line 167) | public function case_get_name()
method case_set_arguments (line 181) | public function case_set_arguments()
method case_set_arguments_not_additive (line 194) | public function case_set_arguments_not_additive()
method case_get_arguments (line 211) | public function case_get_arguments()
method case_set_function (line 229) | public function case_set_function()
method case_is_function (line 239) | public function case_is_function()
method case_is_not_function (line 252) | public function case_is_not_function()
method case_set_laziness (line 265) | public function case_set_laziness()
method case_is_lazy (line 275) | public function case_is_lazy()
method case_is_not_lazy (line 288) | public function case_is_not_lazy()
method case_should_break_lazy_evaluation_with_and_operator (line 301) | public function case_should_break_lazy_evaluation_with_and_operator()
method case_should_not_break_lazy_evaluation_with_and_operator (line 310) | public function case_should_not_break_lazy_evaluation_with_and_operator()
method case_should_break_lazy_evaluation_with_or_operator (line 319) | public function case_should_break_lazy_evaluation_with_or_operator()
method case_should_not_break_lazy_evaluation_with_or_operator (line 328) | public function case_should_not_break_lazy_evaluation_with_or_operator()
method case_should_not_break_lazy_evaluation_with_any_operator (line 337) | public function case_should_not_break_lazy_evaluation_with_any_operator()
method _case_should_break_lazy_evaluation_with_x_operator (line 346) | protected function _case_should_break_lazy_evaluation_with_x_operator(...
method case_is_token (line 356) | public function case_is_token()
method case_is_not_token (line 370) | public function case_is_not_token()
FILE: Test/Unit/Ruler.php
class Ruler (line 52) | class Ruler extends Test\Unit\Suite
method case_assert (line 54) | public function case_assert()
method case_assert_with_a_context (line 67) | public function case_assert_with_a_context()
method case_assert_with_rule_as_a_model (line 82) | public function case_assert_with_rule_as_a_model()
method case_interpret (line 97) | public function case_interpret()
method case_get_interpreter (line 106) | public function case_get_interpreter()
method case_set_asserter (line 115) | public function case_set_asserter()
method case_get_asserter (line 128) | public function case_get_asserter()
method case_get_asserter_with_a_specific_context (line 146) | public function case_get_asserter_with_a_specific_context()
method case_get_asserter_the_default_one (line 165) | public function case_get_asserter_the_default_one()
method case_get_asserter_the_default_one_with_a_specific_context (line 179) | public function case_get_asserter_the_default_one_with_a_specific_cont...
method case_get_default_asserter (line 196) | public function case_get_default_asserter()
method case_get_default_asserter_with_a_specific_context (line 209) | public function case_get_default_asserter_with_a_specific_context()
method case_get_compiler (line 223) | public function case_get_compiler()
FILE: Test/Unit/Visitor/Asserter.php
class Asserter (line 53) | class Asserter extends Test\Unit\Suite
method case_is_a_visitor (line 55) | public function case_is_a_visitor()
method case_constructor (line 64) | public function case_constructor()
method case_constructor_with_a_context (line 89) | public function case_constructor_with_a_context()
method case_operator_and_arity_0 (line 99) | public function case_operator_and_arity_0()
method case_operator_and_true (line 104) | public function case_operator_and_true()
method case_operator_and_false (line 109) | public function case_operator_and_false()
method case_operator_and_true_true (line 114) | public function case_operator_and_true_true()
method case_operator_and_true_false (line 119) | public function case_operator_and_true_false()
method case_operator_and_false_true (line 124) | public function case_operator_and_false_true()
method case_operator_and_false_false (line 129) | public function case_operator_and_false_false()
method case_operator_or_arity_0 (line 134) | public function case_operator_or_arity_0()
method case_operator_or_true (line 139) | public function case_operator_or_true()
method case_operator_or_false (line 144) | public function case_operator_or_false()
method case_operator_or_true_true (line 149) | public function case_operator_or_true_true()
method case_operator_or_true_false (line 154) | public function case_operator_or_true_false()
method case_operator_or_false_true (line 159) | public function case_operator_or_false_true()
method case_operator_or_false_false (line 164) | public function case_operator_or_false_false()
method case_operator_xor_true_true (line 169) | public function case_operator_xor_true_true()
method case_operator_xor_true_false (line 174) | public function case_operator_xor_true_false()
method case_operator_xor_false_true (line 179) | public function case_operator_xor_false_true()
method case_operator_xor_false_false (line 184) | public function case_operator_xor_false_false()
method case_operator_not_true (line 189) | public function case_operator_not_true()
method case_operator_not_false (line 194) | public function case_operator_not_false()
method case_operator_equal_7_42 (line 199) | public function case_operator_equal_7_42()
method case_operator_equal_7_7 (line 204) | public function case_operator_equal_7_7()
method case_operator_equal_7_7_casting (line 209) | public function case_operator_equal_7_7_casting()
method case_operator_is_7_42 (line 214) | public function case_operator_is_7_42()
method case_operator_is_7_7 (line 219) | public function case_operator_is_7_7()
method case_operator_is_7_7_casting (line 224) | public function case_operator_is_7_7_casting()
method case_operator_not_equal_7_42 (line 229) | public function case_operator_not_equal_7_42()
method case_operator_not_equal_7_7 (line 234) | public function case_operator_not_equal_7_7()
method case_operator_not_equal_7_7_casting (line 239) | public function case_operator_not_equal_7_7_casting()
method case_operator_greater_than_7_42 (line 244) | public function case_operator_greater_than_7_42()
method case_operator_greater_than_42_7 (line 249) | public function case_operator_greater_than_42_7()
method case_operator_greater_than_7_7 (line 254) | public function case_operator_greater_than_7_7()
method case_operator_greater_than_or_equal_to_7_42 (line 259) | public function case_operator_greater_than_or_equal_to_7_42()
method case_operator_greater_than_or_equal_to_42_7 (line 264) | public function case_operator_greater_than_or_equal_to_42_7()
method case_operator_greater_than_or_equal_to_7_7 (line 269) | public function case_operator_greater_than_or_equal_to_7_7()
method case_operator_lower_than_7_42 (line 274) | public function case_operator_lower_than_7_42()
method case_operator_lower_than_42_7 (line 279) | public function case_operator_lower_than_42_7()
method case_operator_lower_than_7_7 (line 284) | public function case_operator_lower_than_7_7()
method case_operator_lower_than_or_equal_to_7_42 (line 289) | public function case_operator_lower_than_or_equal_to_7_42()
method case_operator_lower_than_or_equal_to_42_7 (line 294) | public function case_operator_lower_than_or_equal_to_42_7()
method case_operator_lower_than_or_equal_to_7_7 (line 299) | public function case_operator_lower_than_or_equal_to_7_7()
method case_operator_in_empty_array (line 304) | public function case_operator_in_empty_array()
method case_operator_in (line 309) | public function case_operator_in()
method case_operator_in_falsy (line 314) | public function case_operator_in_falsy()
method _case_boolean_operator (line 319) | protected function _case_boolean_operator($operator, array $parameters...
method case_operator_sum_arity_0 (line 332) | public function case_operator_sum_arity_0()
method case_operator_sum_arity_1 (line 337) | public function case_operator_sum_arity_1()
method case_operator_sum (line 342) | public function case_operator_sum()
method _case_operator_sum (line 347) | protected function _case_operator_sum(array $parameters, $expected)
method case_operator_matches (line 360) | public function case_operator_matches()
method case_operator_matches_falsy (line 365) | public function case_operator_matches_falsy()
method case_operator_matches_escaped_delimiter (line 370) | public function case_operator_matches_escaped_delimiter()
method case_set_context (line 375) | public function case_set_context()
method case_get_context (line 388) | public function case_get_context()
method case_set_operator (line 402) | public function case_set_operator()
method case_set_operator_overwrite (line 422) | public function case_set_operator_overwrite()
method case_operator_exists (line 443) | public function case_operator_exists()
method case_operator_does_not_exist (line 456) | public function case_operator_does_not_exist()
method case_get_operator (line 466) | public function case_get_operator()
method case_get_undefined_operator (line 480) | public function case_get_undefined_operator()
method case_get_operators (line 490) | public function case_get_operators()
method case_visit_model (line 500) | public function case_visit_model()
method case_visit_operator (line 516) | public function case_visit_operator()
method case_visit_operator_does_not_exist (line 531) | public function case_visit_operator_does_not_exist()
method case_visit_operator_array_dimension_1 (line 550) | public function case_visit_operator_array_dimension_1()
method case_visit_operator_array_dimension_2 (line 572) | public function case_visit_operator_array_dimension_2()
method case_visit_operator_array_like_dimension_1 (line 595) | public function case_visit_operator_array_like_dimension_1()
method case_visit_operator_array_like_dimension_2 (line 617) | public function case_visit_operator_array_like_dimension_2()
method case_visit_operator_array_dimension_1_undefined_index (line 640) | public function case_visit_operator_array_dimension_1_undefined_index()
method case_visit_operator_array_dimension_1_not_an_array (line 666) | public function case_visit_operator_array_dimension_1_not_an_array()
method case_visit_operator_array_like_dimension_1_undefined_index (line 692) | public function case_visit_operator_array_like_dimension_1_undefined_i...
method case_visit_operator_attribute_dimension_1 (line 718) | public function case_visit_operator_attribute_dimension_1()
method case_visit_operator_attribute_dimension_2 (line 740) | public function case_visit_operator_attribute_dimension_2()
method case_visit_operator_attribute_dimension_1_undefined_name (line 763) | public function case_visit_operator_attribute_dimension_1_undefined_na...
method case_visit_operator_attribute_dimension_1_not_an_object (line 789) | public function case_visit_operator_attribute_dimension_1_not_an_object()
method case_visit_operator_method_dimension_1 (line 815) | public function case_visit_operator_method_dimension_1()
method case_visit_operator_method_dimension_2 (line 838) | public function case_visit_operator_method_dimension_2()
method case_visit_operator_method_dimension_1_undefined_method (line 861) | public function case_visit_operator_method_dimension_1_undefined_method()
method case_visit_operator_method_dimension_1_not_an_object (line 887) | public function case_visit_operator_method_dimension_1_not_an_object()
method case_visit_operator_mixed_dimensions (line 913) | public function case_visit_operator_mixed_dimensions()
method case_visit_scalar_null (line 937) | public function case_visit_scalar_null()
method case_visit_scalar_boolean (line 942) | public function case_visit_scalar_boolean()
method case_visit_scalar_integer (line 947) | public function case_visit_scalar_integer()
method case_visit_scalar_float (line 952) | public function case_visit_scalar_float()
method case_visit_scalar_string (line 957) | public function case_visit_scalar_string()
method _case_visit_scalar (line 962) | protected function _case_visit_scalar($scalar)
method case_visit_array (line 977) | public function case_visit_array()
method case_visit_undefined_context (line 992) | public function case_visit_undefined_context()
method case_visit_context_undefined_reference (line 1011) | public function case_visit_context_undefined_reference()
method case_visit_context (line 1031) | public function case_visit_context()
method case_visit_context_array_dimension_1 (line 1047) | public function case_visit_context_array_dimension_1()
method case_visit_context_array_dimension_2 (line 1064) | public function case_visit_context_array_dimension_2()
method case_visit_context_array_dimension_1_undefined_index (line 1082) | public function case_visit_context_array_dimension_1_undefined_index()
method case_visit_context_array_dimension_1_not_an_array (line 1103) | public function case_visit_context_array_dimension_1_not_an_array()
method case_visit_context_attribute_dimension_1 (line 1124) | public function case_visit_context_attribute_dimension_1()
method case_visit_context_attribute_dimension_2 (line 1141) | public function case_visit_context_attribute_dimension_2()
method case_visit_context_attribute_dimension_1_undefined_name (line 1159) | public function case_visit_context_attribute_dimension_1_undefined_name()
method case_visit_context_attribute_dimension_1_not_an_object (line 1180) | public function case_visit_context_attribute_dimension_1_not_an_object()
method case_visit_context_method_dimension_1 (line 1201) | public function case_visit_context_method_dimension_1()
method case_visit_context_method_dimension_2 (line 1218) | public function case_visit_context_method_dimension_2()
method case_visit_context_method_dimension_1_undefined_method (line 1236) | public function case_visit_context_method_dimension_1_undefined_method()
method case_visit_context_method_dimension_1_not_an_object (line 1257) | public function case_visit_context_method_dimension_1_not_an_object()
method case_visit_context_mixed_dimensions (line 1278) | public function case_visit_context_mixed_dimensions()
class C (line 1298) | class C
method f (line 1300) | public function f($x, $y)
method newMe (line 1305) | public function newMe()
FILE: Test/Unit/Visitor/Compiler.php
class Compiler (line 52) | class Compiler extends Test\Unit\Suite
method case_is_a_visitor (line 54) | public function case_is_a_visitor()
method case_model (line 63) | public function case_model()
method case_operator (line 73) | public function case_operator()
method case_operator_is_an_identifier (line 86) | public function case_operator_is_an_identifier()
method case_function (line 99) | public function case_function()
method case_function_of_arity_1 (line 113) | public function case_function_of_arity_1()
method case_function_with_array_dimension (line 126) | public function case_function_with_array_dimension()
method case_function_with_attribute_dimension (line 142) | public function case_function_with_attribute_dimension()
method case_function_with_call_dimension (line 156) | public function case_function_with_call_dimension()
method case_function_with_many_dimensions (line 175) | public function case_function_with_many_dimensions()
method case_scalar_true (line 198) | public function case_scalar_true()
method case_scalar_false (line 208) | public function case_scalar_false()
method case_scalar_null (line 218) | public function case_scalar_null()
method case_scalar_numeric (line 231) | public function case_scalar_numeric()
method case_scalar_string (line 241) | public function case_scalar_string()
method case_scalar_escaped_string (line 251) | public function case_scalar_escaped_string()
method case_array (line 261) | public function case_array()
method case_context (line 275) | public function case_context()
method case_context_with_array_dimension (line 285) | public function case_context_with_array_dimension()
method case_context_with_array_dimensions (line 298) | public function case_context_with_array_dimensions()
method case_context_with_attribute_dimension (line 314) | public function case_context_with_attribute_dimension()
method case_context_with_attribute_dimensions (line 325) | public function case_context_with_attribute_dimensions()
method case_context_with_call_dimension (line 337) | public function case_context_with_call_dimension()
method case_context_with_call_dimensions (line 353) | public function case_context_with_call_dimensions()
method case_context_with_many_dimensions (line 375) | public function case_context_with_many_dimensions()
method _case (line 403) | protected function _case($rule, $compiled)
FILE: Test/Unit/Visitor/Disassembly.php
class Disassembly (line 52) | class Disassembly extends Test\Unit\Suite
method case_is_a_visitor (line 54) | public function case_is_a_visitor()
method case_model (line 63) | public function case_model()
method case_operator (line 71) | public function case_operator()
method case_operator_is_an_identifier (line 79) | public function case_operator_is_an_identifier()
method case_function (line 87) | public function case_function()
method case_function_of_arity_1 (line 95) | public function case_function_of_arity_1()
method case_function_with_array_dimensions (line 103) | public function case_function_with_array_dimensions()
method case_function_with_attribute_dimensions (line 111) | public function case_function_with_attribute_dimensions()
method case_function_with_call_dimensions (line 119) | public function case_function_with_call_dimensions()
method case_function_with_many_dimensions (line 127) | public function case_function_with_many_dimensions()
method case_scalar_true (line 135) | public function case_scalar_true()
method case_scalar_false (line 143) | public function case_scalar_false()
method case_scalar_null (line 151) | public function case_scalar_null()
method case_scalar_numeric (line 159) | public function case_scalar_numeric()
method case_scalar_string (line 167) | public function case_scalar_string()
method case_scalar_escaped_string (line 175) | public function case_scalar_escaped_string()
method case_array (line 183) | public function case_array()
method case_context (line 191) | public function case_context()
method case_context_with_array_dimension (line 199) | public function case_context_with_array_dimension()
method case_context_with_attribute_dimension (line 207) | public function case_context_with_attribute_dimension()
method case_context_with_call_dimension (line 215) | public function case_context_with_call_dimension()
method case_context_with_many_dimensions (line 223) | public function case_context_with_many_dimensions()
method _case (line 231) | protected function _case($rule, $disassembled)
FILE: Test/Unit/Visitor/Interpreter.php
class Interpreter (line 52) | class Interpreter extends Test\Unit\Suite
method case_is_a_visitor (line 54) | public function case_is_a_visitor()
method case_model (line 63) | public function case_model()
method case_operator (line 76) | public function case_operator()
method case_operator_is_an_identifier (line 93) | public function case_operator_is_an_identifier()
method case_operator_and (line 110) | public function case_operator_and()
method case_operator_or (line 127) | public function case_operator_or()
method case_operator_xor (line 144) | public function case_operator_xor()
method case_operator_not (line 161) | public function case_operator_not()
method case_function (line 177) | public function case_function()
method case_function_of_arity_1 (line 195) | public function case_function_of_arity_1()
method case_function_with_array_dimension (line 212) | public function case_function_with_array_dimension()
method case_function_with_attribute_dimension (line 232) | public function case_function_with_attribute_dimension()
method case_function_with_call_dimension (line 250) | public function case_function_with_call_dimension()
method case_function_with_many_dimensions (line 273) | public function case_function_with_many_dimensions()
method case_scalar_true (line 300) | public function case_scalar_true()
method case_scalar_false (line 314) | public function case_scalar_false()
method case_scalar_null (line 328) | public function case_scalar_null()
method case_scalar_float (line 345) | public function case_scalar_float()
method case_scalar_integer (line 359) | public function case_scalar_integer()
method case_scalar_string (line 373) | public function case_scalar_string()
method case_scalar_escaped_string (line 387) | public function case_scalar_escaped_string()
method case_array (line 401) | public function case_array()
method case_context (line 419) | public function case_context()
method case_context_with_array_dimension (line 433) | public function case_context_with_array_dimension()
method case_context_with_array_dimensions (line 450) | public function case_context_with_array_dimensions()
method case_context_with_attribute_dimension (line 470) | public function case_context_with_attribute_dimension()
method case_context_with_attribute_dimensions (line 485) | public function case_context_with_attribute_dimensions()
method case_context_with_many_dimensions (line 501) | public function case_context_with_many_dimensions()
method _case (line 533) | protected function _case($rule, \Closure $expected)
FILE: Visitor/Asserter.php
class Asserter (line 52) | class Asserter implements Visitor\Visit
method __construct (line 75) | public function __construct(Ruler\Context $context = null)
method visit (line 112) | public function visit(Visitor\Element $element, &$handle = null, $eldn...
method visitModel (line 143) | public function visitModel(Ruler\Model $element, &$handle = null, $eld...
method visitOperator (line 156) | protected function visitOperator(Ruler\Model\Operator $element, &$hand...
method visitScalar (line 197) | protected function visitScalar(Ruler\Model\Bag\Scalar $element, &$hand...
method visitArray (line 210) | protected function visitArray(Ruler\Model\Bag\RulerArray $element, &$h...
method visitContext (line 229) | protected function visitContext(Ruler\Model\Bag\Context $element, &$ha...
method visitDimensions (line 269) | protected function visitDimensions(
method visitArrayDimension (line 334) | protected function visitArrayDimension(
method visitAttributeDimension (line 385) | protected function visitAttributeDimension(
method visitMethodDimension (line 430) | protected function visitMethodDimension(
method setContext (line 480) | public function setContext(Ruler\Context $context)
method getContext (line 493) | public function getContext()
method setOperator (line 505) | public function setOperator($operator, $callable)
method operatorExists (line 518) | public function operatorExists($operator)
method getOperator (line 529) | public function getOperator($operator)
method getOperators (line 549) | public function getOperators()
FILE: Visitor/Compiler.php
class Compiler (line 51) | class Compiler implements Visitor\Visit
method visit (line 70) | public function visit(Visitor\Element $element, &$handle = null, $eldn...
method visitContext (line 168) | protected function visitContext(Ruler\Model\Bag\Context $context, &$ha...
FILE: Visitor/Disassembly.php
class Disassembly (line 50) | class Disassembly implements Visitor\Visit
method visit (line 60) | public function visit(Visitor\Element $element, &$handle = null, $eldn...
method visitContext (line 124) | protected function visitContext(Ruler\Model\Bag\Context $context, &$ha...
FILE: Visitor/Interpreter.php
class Interpreter (line 50) | class Interpreter implements Visitor\Visit
method visit (line 77) | public function visit(Visitor\Element $element, &$handle = null, $eldn...
method getRoot (line 246) | public function getRoot()
Condensed preview — 46 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (334K chars).
[
{
"path": ".State",
"chars": 10,
"preview": "finalized\n"
},
{
"path": ".gitignore",
"chars": 24,
"preview": "/vendor/\n/composer.lock\n"
},
{
"path": ".travis.yml",
"chars": 1095,
"preview": "language: php\n\nbranches:\n only:\n - staging\n - trying\n - master\n\nmatrix:\n include:\n - php: 5.5\n - php: 5"
},
{
"path": "Bin/Assert.php",
"chars": 3306,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "CHANGELOG.md",
"chars": 8158,
"preview": "# 2.17.05.16\n\n * Asserter: Can visit array-like dimensions. (Arne Groskurth, 2017-05-16T09:42:29+02:00)\n\n# 2.17.04.26\n\n"
},
{
"path": "Context.php",
"chars": 4258,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Documentation/En/Index.xyl",
"chars": 29312,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<overlay xmlns=\"http://hoa-project.net/xyl/xylophone\">\n<yield id=\"chapter\">\n\n <"
},
{
"path": "Documentation/Fr/Index.xyl",
"chars": 30847,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<overlay xmlns=\"http://hoa-project.net/xyl/xylophone\">\n<yield id=\"chapter\">\n\n <"
},
{
"path": "DynamicCallable.php",
"chars": 1935,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Exception/Asserter.php",
"chars": 1889,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Exception/Exception.php",
"chars": 2038,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Exception/Interpreter.php",
"chars": 1895,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Grammar.pp",
"chars": 3491,
"preview": "//\n// Hoa\n//\n//\n// @license\n//\n// New BSD License\n//\n// Copyright © 2007-2017, Hoa community. All rights reserved.\n//\n//"
},
{
"path": "Model/Bag/Bag.php",
"chars": 2435,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Model/Bag/Context.php",
"chars": 4477,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Model/Bag/RulerArray.php",
"chars": 2616,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Model/Bag/Scalar.php",
"chars": 2290,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Model/Model.php",
"chars": 5323,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Model/Operator.php",
"chars": 6635,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "README.md",
"chars": 9612,
"preview": "<p align=\"center\">\n <img src=\"https://static.hoa-project.net/Image/Hoa.svg\" alt=\"Hoa\" width=\"250px\" />\n</p>\n\n---\n\n<p al"
},
{
"path": "Ruler.php",
"chars": 5301,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Integration/Documentation.php",
"chars": 5501,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Integration/Model/Operator.php",
"chars": 6901,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Context.php",
"chars": 7793,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/DynamicCallable.php",
"chars": 2228,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Exception/Asserter.php",
"chars": 2224,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Exception/Exception.php",
"chars": 2234,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Exception/Interpreter.php",
"chars": 2233,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Issue.php",
"chars": 3901,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Model/Bag/Bag.php",
"chars": 2198,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Model/Bag/Context.php",
"chars": 7677,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Model/Bag/RulerArray.php",
"chars": 2801,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Model/Bag/Scalar.php",
"chars": 2472,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Model/Model.php",
"chars": 7821,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Model/Operator.php",
"chars": 11615,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Ruler.php",
"chars": 7568,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Visitor/Asserter.php",
"chars": 44910,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Visitor/Compiler.php",
"chars": 12636,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Visitor/Disassembly.php",
"chars": 5641,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Test/Unit/Visitor/Interpreter.php",
"chars": 13774,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Visitor/Asserter.php",
"chars": 17437,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Visitor/Compiler.php",
"chars": 6840,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Visitor/Disassembly.php",
"chars": 5485,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "Visitor/Interpreter.php",
"chars": 7736,
"preview": "<?php\n\n/**\n * Hoa\n *\n *\n * @license\n *\n * New BSD License\n *\n * Copyright © 2007-2017, Hoa community. All rights reserve"
},
{
"path": "bors.toml",
"chars": 74,
"preview": "status = [\n \"continuous-integration/travis-ci/push\"\n]\ntimeout_sec = 1800\n"
},
{
"path": "composer.json",
"chars": 1325,
"preview": "{\n \"name\" : \"hoa/ruler\",\n \"description\": \"The Hoa\\\\Ruler library.\",\n \"type\" : \"library\",\n \"keywo"
}
]
About this extraction
This page contains the full source code of the hoaproject/Ruler GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 46 files (310.5 KB), approximately 78.2k tokens, and a symbol index with 395 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.