Skip to content

Commit

Permalink
Fixed unit tests and enhanced readme.md
Browse files Browse the repository at this point in the history
Warning: This commit assumes `Hoa\Ruler::getAsserter()` does not require a
`Hoa\Ruler\Context` to be passed in (which it does at the current moment
of writing). See README.md for more info.
  • Loading branch information
flip111 committed Feb 22, 2014
1 parent 15e9191 commit 51f0718
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 13 deletions.
76 changes: 72 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,77 @@
HoaRulerPHPDumper
=================

_proof of concepts_
## Warning ##
This library assumes `Hoa\Ruler::getAsserter()` does not require a `Hoa\Ruler\Context` to be passed in (which it does at the current moment of writing).

Converts a Hoa\Ruler rule to PHP code
## Introduction ##
There are several libraries out for PHP which allow you to interpret another language in PHP. For example [Symfony Expression Language Component](http://symfony.com/doc/current/components/expression_language/index.html) and the [Hoa\Ruler](https://github.com/hoaproject/Ruler). However when executing this "expression language" or "rule" it has overhead from the given libraries instead of being able to execute the code directly. This library attempts to convert the rule from `Hoa\Ruler` to native PHP code.

The PHPDumper class converts the Hoa\Model code to PHP-AST and from there it transforms it to native PHP-AST.
This is an early version for reviewing purposes.
This opens new possiblities for this library, namely:
1. The native PHP code can be cached for fast execution in production environments.
2. To be used as a code generator.

## State ##
This library is mainly a _proof of concept_ at the moment. It's fully working and tested, but likely needs more integration and cleanup. Please follow [this thread](https://github.com/hoaproject/Ruler/issues/2) about the active discussion around this library.

## How it works internally ##
The library depends on [PHP-Parser](https://github.com/nikic/PHP-Parser) to convert the model made by `Hoa\Ruler` to PHP. It does this by first reading the Abstract Syntax Tree (AST) of `Hoa\Ruler\Model\Model` and then convert it do an AST that describes PHP. The final step is then to convert to PHP-AST to PHP code.

## Examples ##
Please see the unit tests for more examples. This readme will only show one example which is derived from the one in the `Hoa\Ruler` [readme](https://github.com/hoaproject/Ruler/blob/master/README.md).

```php
<?php
/*
* This part is taken from the Hoa\Ruler readme
*/

// 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.
$asserter = new Asserter($context);
$asserter->setOperator('logged', function ( User $user ) {

return $user::CONNECTED =## $user->getStatus();

This comment has been minimized.

Copy link
@Hywan

Hywan Feb 24, 2014

Why =##?

This comment has been minimized.

Copy link
@flip111

flip111 Feb 24, 2014

Author Owner

Because i had markdown wrong like == Examples == instead of ## Examples ## ... so i did a search and replace. So my code === got replaced by =## :( ... will have to fix this !!

});

$ruler = new Ruler();
$ruler->setAsserter($asserter);

/*
* This is the part where the PHPDumper makes the conversion
*/
$dumper = new PHPDumper(); // Initialize a new PHPDumper object

$closuresPhpAst = $dumper->getClosures($ruler); // Get the PHP-AST for the Hoa\Ruler Operators

$phpAst = $dumper->L(Ruler::interprete($rule)); // Get the PHP-AST for the Hoa\Ruler rule

This comment has been minimized.

Copy link
@Hywan

Hywan Feb 24, 2014

“Get the PHP-AST for the Hoa\Ruler model”, not the “rule”.

This comment has been minimized.

Copy link
@flip111

flip111 Feb 24, 2014

Author Owner

Ok will adjust in new version


$packedPhpAst = $dumper->pack($phpAst, $closuresPhpAst); // Pack both the rule and it's operators into a single closure (still outputs PHP-AST)

$packedPhp = $this->astToPhp($packedPhpAst); // Finally make actual PHP code

/*
our $packedPhp contains php code as string which looks like this:

$closure = function ($user, $group, $points) {
$c_logged = function (User $user) {
return $user::CONNECTED =## $user->getStatus();
};

return $c_logged($user) and (in_array($group, array('customer', 'guest')) and $points > 30);
};
*/
```
4 changes: 2 additions & 2 deletions src/PHPDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public function R($in) {
}

public function getClosures($ruler) {
$operators = $ruler->getDefaultAsserter()->getOperators();
$operators = $ruler->getAsserter()->getOperators();

$callbacks = [];
foreach ($operators as $name => $operator) {
Expand Down Expand Up @@ -288,7 +288,7 @@ private function findAstByLine($stmts, $start = null, $end = null) {
$start == $stmt->getAttribute('startLine') AND
$end == $stmt->getAttribute('endLine')) {
// The return statement is specific for a statement like this:
// $ruler->getDefaultAsserter()->setOperator('foo', function() {
// $ruler->getAsserter()->setOperator('foo', function() {
// return 'bar';
// });
return $stmt->args[1]->value;
Expand Down
23 changes: 16 additions & 7 deletions tests/PHPDumperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use Hoa\Ruler\Ruler;
use PhpParser\Lexer;
use PhpParser\Parser;
use Hoa\Ruler\Visitor\Asserter;

class PHPDumperTest extends PHPUnit_Framework_TestCase {
protected static $prettyPrinter;
Expand Down Expand Up @@ -302,11 +303,14 @@ public function testOperator2() {
public function testClosure1() {
$dumper = new PHPDumper();

$ruler = new Ruler();
$ruler->getDefaultAsserter()->setOperator('foo', function() {
$asserter = new Asserter();
$asserter->setOperator('foo', function() {
return 'bar';
});

$ruler = new Ruler();
$ruler->setAsserter($asserter);

$phpAst = $dumper->getClosures($ruler)['foo'];
$php = $this->astToPhp($phpAst);
// For testing we don't care about whitespaces
Expand Down Expand Up @@ -381,11 +385,14 @@ public function testPack4() { // With closures
$dumper = new PHPDumper();

// Setup the closure
$ruler = new Ruler();
$ruler->getDefaultAsserter()->setOperator('foo', function() {
$asserter = new Asserter();
$asserter->setOperator('foo', function() {
return 'bar';
});

$ruler = new Ruler();
$ruler->setAsserter($asserter);

$closuresPhpAst = $dumper->getClosures($ruler);

$rule = 'foo()';
Expand Down Expand Up @@ -418,8 +425,6 @@ public function testExample() {

require_once 'user.php'; // eeeww :(

$ruler = new Hoa\Ruler\Ruler();

// New rule.
$rule = 'logged(user) and group in ("customer", "guest") and points > 30';

Expand All @@ -435,11 +440,15 @@ public function testExample() {
};

// We add the logged() operator.
$ruler->getDefaultAsserter()->setOperator('logged', function ( User $user ) {
$asserter = new Asserter($context);
$asserter->setOperator('logged', function ( User $user ) {

return $user::CONNECTED === $user->getStatus();
});

$ruler = new Ruler();
$ruler->setAsserter($asserter);

// We add the secret sauce.
$closuresPhpAst = $dumper->getClosures($ruler);
$phpAst = $dumper->L(Ruler::interprete($rule));
Expand Down

0 comments on commit 51f0718

Please sign in to comment.