Hoa is a modular, extensible and structured set of PHP libraries

Overview

Hoa


Build status Code coverage Packagist License

Hoa is a modular, extensible and structured set of PHP libraries.
Moreover, Hoa aims at being a bridge between industrial and research worlds.

Hoa\Ruler

Help on IRC Help on Gitter Documentation Board

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:

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.

Installation

With Composer, to include this library into your dependencies, you need to require hoa/ruler:

$ composer require hoa/ruler '~2.0'

For more installation procedures, please read the Source page.

Testing

Before running the test suites, the development dependencies must be installed:

$ composer install

Then, to run all the test suites:

$ vendor/bin/hoa test:run

For more information, please read the contributor guide.

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:

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) */">
$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:

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) */">
// 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:

30' ) ) );">
$database->save(
    serialize(
        Hoa\Ruler\Ruler::interpret(
            'logged(user) and group in ["customer", "guest"] and points > 30'
        )
    )
);

And for next executions:

$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:

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 * ) * ) * ); */">
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 contains detailed information about how to use this library and how it works.

To generate the documentation locally, execute the following commands:

$ composer require --dev hoa/devtools
$ vendor/bin/hoa devtools:documentation --open

More documentation can be found on the project's website: hoa-project.net.

Getting help

There are mainly two ways to get help:

Contribution

Do you want to contribute? Thanks! A detailed contributor guide explains everything you need to know.

License

Hoa is under the New BSD License (BSD-3-Clause). Please, see LICENSE for details.

Related projects

The following projects are using this library:

Issues
  • Can the ruler output stand-alone PHP?

    Can the ruler output stand-alone PHP?

    Taken from the readme

    $rule  = 'group in ("customer", "guest") and points > 30';
    

    There is an example of how to convert it to PHP code by calling Hoa\Ruler\Ruler::interprete. But this return \Hoa\Ruler\Model objects. Is it also possible generate stand-alone PHP code?

    Getting a closure like this

    <?php
    $closure = function($group, $points) {
        return in_array($group, ['customer', 'guest']) AND $points > 30;
    }
    

    would be great to reuse the code in projects and test it with PHP directly.

    Perhaps it can be done with using the Hoa compiler ??

    enhancement 
    opened by flip111 58
  • Adding possibility to return context keys for dynamically populate context.

    Adding possibility to return context keys for dynamically populate context.

    In my case, I want to be able to populate my context dynamically so i've added this possibility. The code will look something like this:

        $ruler = new \Hoa\Ruler\Ruler();
    
        $rule  = 'group in ("customer", "guest") and points > 30';
    
        $rule =  \Hoa\Ruler\Ruler::interprete($rule);
        $contextKeys = $ruler->getContextKeys();
    
        //dynamically populate context based on context keys
        $context = getContext (new \Hoa\Ruler\Context(), $contextKeys);
    
        var_dump($ruler->assert($rule, $context));
    
    question difficulty: medium 
    opened by ncosmin2001 27
  • Edit visit method into Visitor\Asserter to easily extend/test it.

    Edit visit method into Visitor\Asserter to easily extend/test it.

    This PR improve readability, extensibility, testability for Visitor\Asserter

    enhancement 
    opened by stephpy 25
  • add ability to pass operator via context

    add ability to pass operator via context

    It would be fantastic if i could pass the operator in via the context like the following example

        $ruleString = 'app operator value';
    
        $context = new Context();
        $context['app'] = $this->critera['app'];
        $context['operator'] = '!=';
        $context['value'] = $rule->value;
    
    question 
    opened by th3fallen 22
  • Add branch-alias.

    Add branch-alias.

    enhancement 
    opened by stephpy 22
  • Generating valid PHP code within visit method

    Generating valid PHP code within visit method

    Within the visit method, if the $element is an Operator and $element->isFunction() === true and this function is called without any arguments then an invalid PHP code is generated.

    e.g.

    $model->func('function_name',)
    

    When a method has no arguments, the comma that separates the method name from the list of the arguments needs to be removed .

    bug 
    opened by crislinu 17
  • Introduce lazy evaluation in functions/operators

    Introduce lazy evaluation in functions/operators

    For example, if I have a rule

    $rule  = 'group in ("customer", "guest") and points > 30';
    

    and group is not customer neither guest we don't need to evaluate points > 30 and directly return false.

    enhancement 
    opened by vonglasow 16
  • Provide array and object access

    Provide array and object access

    It would be great to see array and object access in rules:

    Array access:

    $rule = 'user[age] > 18';
    $context['user'] = array('age' => 25);
    

    Object access:

    $rule = 'user.age > 18;
    
    class User {
        public $age = 32;
    }
    
    $context['user'] = new User();
    
    enhancement 
    opened by ghost 15
  • Code formatting in README

    Code formatting in README

    Syntax highlighting makes it so much more readable ;)

    enhancement 
    opened by mnapoli 15
  • Could identifiers start with a digit?

    Could identifiers start with a digit?

    Bonjour,

    Pour faire suite à cette discussions sur la mailing list support d'Hoa => http://lists.hoa-project.net/arc/support/2014-11/msg00004.html, je pense que la grammaire de reconnaissance des identifiants pour l'interprétation des règles peut être améliorée.

    En effet, dans le cas d'identifiants commençant par un chiffre comme 1_identifiant, le premier caractère est reconnu comme un entier et le reste génère une erreur au lieu de considérer l'ensemble comme un identifiant.

    J'ai trouvé une grammaire alternative qui fonctionne sans casser la détection des entiers (a vérifier sur des jeux de test plus complet cependant) qui est la suivante => \d+(?![^\s()[],.])

    Qu'en pensez-vous ?

    enhancement wontfix 
    opened by gael-wogenstahl 15
  • Upgrade to GitHub-native Dependabot

    Upgrade to GitHub-native Dependabot

    Dependabot Preview will be shut down on August 3rd, 2021. In order to keep getting Dependabot updates, please merge this PR and migrate to GitHub-native Dependabot before then.

    Dependabot has been fully integrated into GitHub, so you no longer have to install and manage a separate app. This pull request migrates your configuration from Dependabot.com to a config file, using the new syntax. When merged, we'll swap out dependabot-preview (me) for a new dependabot app, and you'll be all set!

    With this change, you'll now use the Dependabot page in GitHub, rather than the Dependabot dashboard, to monitor your version updates, and you'll configure Dependabot through the new config file rather than a UI.

    If you've got any questions or feedback for us, please let us know by creating an issue in the dependabot/dependabot-core repository.

    Learn more about migrating to GitHub-native Dependabot

    Please note that regular @dependabot commands do not work on this pull request.

    dependencies 
    opened by dependabot-preview[bot] 3
  • Interface for writing rules

    Interface for writing rules

    Hi and thanks for the awesome package!

    We're looking to build a front-end interface for writing rules and wondering if there were any recommendations for autocompleting / suggesting rule operators?

    opened by drewsymo 0
  • Space or no-space in expressions

    Space or no-space in expressions

    It is a difference to have foo="bar" or foo = "bar" when evaluating this grammar. Is there a way to have both the same?

    in progress difficulty: medium 
    opened by beberlei 10
  • Support traversables inside `in` operator

    Support traversables inside `in` operator

    enhancement in progress difficulty: medium 
    opened by ostrolucky 9
  • How could I combine different existing rules?

    How could I combine different existing rules?

    Hi,

    I am new to Ruler, and just have a basic question: How could I combine different existing rules dynamically? Tried to google and searched in the GIT repo, did not find any related things...

    Thanks!

    question difficulty: casual 
    opened by xeoshow 1
  • Matches operator does not match

    Matches operator does not match

    I really love this project, it's working great so far! Except one thing, which I'm probably failing to understand (correctly). I'm trying to match a 'description' with a partial string. I've found the matches operator and created the following rule: description matches "somestring"

    I load up the context, the rule, etc. And run it against this context: lorem ipsum dolor sit somestring amet

    Now I'm expecting this to return true (as 'somestring' matches a part of the context. Yet it returns false. I was expecting a similar functionality like the strpos function in PHP.

    Am I using the operator wrong, or do I have to define a custom operator to accomplish something like this?

    Thanks in advance.

    To add a bit more context (hehe), this is the code that's supposed to run all the rules. If a rule matches a transaction. a category_id is added to the transaction.

    $rules = Rule::all();
    $transactions = Transaction::all();
    foreach ($transactions as $transaction) {
        $context = new Context();
        $context['account_id'] = $transaction->account_id;
        $context['account_number'] = $transaction->account_number;
        $context['account_name'] = $transaction->account_name;
        $context['type'] = $transaction->type;
        $context['amount'] = $transaction->amount;
        $context['description'] = $transaction->description;
        $context['date'] = $transaction->date;
        $context['book_code'] = $transaction->book_code;
    
        $hoaRuler = new Ruler();
    
        foreach ($rules as $rule) {
            if ($hoaRuler->assert(strtolower($rule->rule), $context)) {
                $transaction->category_id = $rule->category_id;
                $transaction->save();
            }
        }
    }
    
    question in progress difficulty: casual 
    opened by sanderdekroon 1
  • Example benchmark

    Example benchmark

    Just an example benchmark using PHPBench to get things started.

    $ phpbench report --uuid=latest --report=hoa
    benchmark: RulerBench, subject: benchAssert
    +---------------------------------------------------------------+------+-----+------------+---------+---------+---------+--------+
    | params                                                        | revs | its | mem_peak   | best    | mean    | mode    | rstdev |
    +---------------------------------------------------------------+------+-----+------------+---------+---------+---------+--------+
    | {"rule":"group in [\"customer\", \"guest\"] and points > 30"} | 10   | 10  | 2,249,760b | 3.786ms | 4.724ms | 4.123ms | 15.20% |
    | {"rule":"points > 30"}                                        | 10   | 10  | 2,200,408b | 1.386ms | 1.836ms | 1.909ms | 17.00% |
    | {"rule":"group in [\"customer\", \"guest\"]"}                 | 10   | 10  | 2,216,400b | 2.240ms | 2.781ms | 2.715ms | 15.34% |
    +---------------------------------------------------------------+------+-----+------------+---------+---------+---------+--------+``
    
    enhancement in progress difficulty: hard 
    opened by dantleech 4
  • Produce results in response of rule evaluations

    Produce results in response of rule evaluations

    See #94 for detailed information.

    Todo:

    • [x] Implement the Rules collection with an SplPriorityQueue
    • [ ] Use the hoa/heap library for the Rules collection
    • [x] Implement the Then rule class (it returns a result when the rule validates the input)
    • [x] Implement the ThenElse rule class (it returns a result when the rule validates the input and another when the rule does not validate the input)
    • [ ] Implement an algorithm to resolve rules dependencies (indeed, a rule can depend on another one). hoa/graph could be a good candidate for this kind of work.
    • [x] Implement a rule() function to reference the result of one rule from another rule

    Some notes about the technical choices:

    • I used some clones in the code: this is to guarantee immutability of the passed objects. For example the Hoa\Ruler\Rules::initializeRuler() may alter the ruler's asserter. Before doing this, we clone the ruler and then alter its content. Finally we return it to be used where needed.
    • I introduced a Hoa\Ruler\Result class (with a magic method: this will change). It also does a clone of the result in some cases: this is to avoid mutating the value returned by the rules.
    • This class (Hoa\Ruler\Result) is also here to return a structured result letting developers keep track of the returned value, the rule that produced it and its name in the collection
    enhancement in progress difficulty: hard 
    opened by jubianchi 10
  • Produce results in response of rule evaluations

    Produce results in response of rule evaluations

    Ruler is well designed for writing rules and making assertions on them (i.e checking if some input validates the rule).

    An nice feature would to be able to produce any result in response of these assertions (i.e if some input validates a rule then the engine should provide us a value).

    Let's take a simple example:

    • If I'm at least 16 years old, I can buy beer
    • If I'm at least 18 years old, I can buy whiskey, gin or vodka

    With only ruler, I would have to write two rules:

    • $r1 = 'person.age >= 16'
    • $r2 = 'person.age >= 18'

    And some PHP code:

    <?php
    
    $ruler = new Hoa\Ruler\Ruler();
    $context = new Hoa\Ruler\Context();
    $context['person'] = new Person();
    $context['person']->age = 19;
    
    $beverages = [];
    
    if ($ruler->assert($r1, $context)) {
        $beverages[] = 'beer';
    }
    
    if ($ruler->assert($r2, $context)) {
        $beverages[] = 'whiskey';
        $beverages[] = 'gin';
        $beverages[] = 'vodka';
    }
    
    var_dump($beverages); 
    
    /*
    array(4) {
      [0] => string(3) "beer",
      [1] => string(3) "whiskey",
      [2] => string(3) "gin",
      [3] => string(3) "vodka"
    }
    */
    

    This is not really efficient: the more rules I have, the more code I need to write. If the results change, I'll have to change my PHP code, ...

    What I propose here is something more flexible:

    $ruler = new Hoa\Ruler\Ruler();
    $rules = new Hoa\Ruler\Rules();
    $context = new Hoa\Ruler\Context();
    $context['person'] = new Person();
    $context['person']->age = 19;
    
    $rules->add('16yo', new Hoa\Ruler\Rules\ThenElse('person.age >= 16', ['beer'], ['water']), 1);
    $rules->add('18yo', new Hoa\Ruler\Rules\Then('person.age >= 18', ['whiskey', 'gin', 'vodka']), 2);
    
    var_dump($rules->getBestResult($ruler, $context));
    /*
    array(3) {
      [0] => string(3) "whiskey",
      [1] => string(3) "gin",
      [2] => string(3) "vodka"
    }
    */
    
    var_dump($rules->getAllResults($ruler, $context));
    /*
    array(2) {
      '18yo' => array(3) {
        [0] => string(3) "whiskey",
        [1] => string(3) "gin",
        [2] => string(3) "vodka"
      },
      '16yo' => array(1) {
        [0] => string(3) "beer"
      }
    }
    */
    

    Let me explain:

    • Hoa\Ruler\Rules is a collection of rules, ordered by a priority,
    • it has a add(string $id, Rule $rule, int $priority) method,
    • the $id lets us reuse the rule result in following rules,
    • the $rule is a standard ruler rule (a string or a compiled rule) wrapped in a Then or ThenElse class,
    • the $priority is used to order the collection (higher priority will run first);
    • we can ask for the best results (getBestResult) which will be the result of the higher priority rule validating the context,
    • we can ask for all results (getAllResults) of valid rules

    This kind of feature will allow for more generic code when it comes to work with rules and produce results depending on them.

    The Rules collection can easily be built with results coming from a database.

    What has to be done:

    • [ ] Implement the Rules collection (first with an SplPriorityQueue and then with the hoa/heap library)
    • [ ] Implement the Then rule class (it returns a result when the rule validates the input)
    • [ ] Implement the ThenElse rule class (it returns a result when the rule validates the input and another when the rule does not validate the input)
    • [ ] Implement an algorithm to resolve rules dependencies (indeed, a rule can depend on another one). hoa/graph could be a good candidate for this kind of work.
    • [ ] Implement a rule() function to reference the result of one rule from another rule

    Let me know if I forgot something. I have a working POC which needs some cleanup.

    enhancement in progress difficulty: hard 
    opened by jubianchi 5
  • case-insensitive for Operator name

    case-insensitive for Operator name

    ~~Address to #92~~ Fix #92.

    Edit by @Hywan.

    enhancement in progress difficulty: medium 
    opened by Pierozi 10
Releases(2.17.05.16)
  • 2.17.05.16(May 16, 2017)

  • 2.17.04.26(Apr 26, 2017)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 2.17.01.13(Jan 13, 2017)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 2.16.10.24(Oct 24, 2016)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 2.16.09.07(Sep 7, 2016)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 2.16.04.06(Apr 6, 2016)

  • 2.16.03.15(Mar 15, 2016)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 2.16.01.15(Jan 14, 2016)

  • 2.16.01.14(Jan 14, 2016)

  • 2.16.01.11(Jan 11, 2016)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 1.15.11.09(Nov 9, 2015)

  • 1.15.09.22(Sep 22, 2015)

  • 1.15.09.08(Sep 8, 2015)

  • 1.15.07.28(Jul 28, 2015)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 1.15.05.29(May 29, 2015)

  • 1.15.04.13(Apr 13, 2015)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 1.15.02.05(Feb 16, 2015)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 1.15.02.02(Feb 16, 2015)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 1.14.12.10(Feb 16, 2015)

  • 1.14.12.09(Feb 16, 2015)

     * 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)
    Source code(tar.gz)
    Source code(zip)
  • 1.14.11.10(Feb 16, 2015)

  • 1.14.11.09(Feb 16, 2015)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 1.14.09.25(Feb 16, 2015)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 1.14.09.23(Feb 16, 2015)

    • 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)
    Source code(tar.gz)
    Source code(zip)
  • 0.14.09.17(Feb 16, 2015)

Owner
Hoa
Hoa ~is~ was a modular, extensible and structured set of PHP libraries. The project is now archived. Thanks for all the fish!
Hoa
Geo-related tools PHP 7.3+ library built atop Geocoder and React libraries

Geotools Geotools is a PHP geo-related library, built atop Geocoder and React libraries. Features Batch geocode & reverse geocoding request(s) in seri

The League of Extraordinary Packages 1.2k Jan 13, 2022
Laravel SEO - This is a simple and extensible package for improving SEO via meta tags, such as OpenGraph tags.

This is a simple and extensible package for improving SEO via meta tags, such as OpenGraph tags.

ARCHTECH 119 Jan 6, 2022
Laravel Boilerplate provides a very flexible and extensible way of building your custom Laravel applications.

Laravel Boilerplate Project Laravel Boilerplate provides a very flexible and extensible way of building your custom Laravel applications. Table of Con

Labs64 769 Jan 12, 2022
The University of Arizona Libraries will no longer provide support for Guide on the Side.

The University of Arizona Libraries will no longer provide support for Guide on the Side. The code will remain openly available; however, UAL can no longer provide code fixes or upgrades.

The University of Arizona Libraries 66 Dec 29, 2021
A light weight laravel package that facilitates dealing with arabic concepts using a set of classes and methods to make laravel speaks arabic

A light weight laravel package that facilitates dealing with arabic concepts using a set of classes and methods to make laravel speaks arabic! concepts like , Hijri Dates & Arabic strings and so on ..

Adnane Kadri 45 Nov 1, 2021
Gallium is a TALL stack starter kit offering a robust set of options enabling you to get up and running in a snap.

Very short description of the package This is where your description should go. Try and limit it to a paragraph or two, and maybe throw in a mention o

null 1 Nov 20, 2021
A set of useful Laravel collection macros

A set of useful Laravel collection macros This repository contains some useful collection macros. Spatie is a webdesign agency based in Antwerp, Belgi

Spatie 1.3k Jan 17, 2022
⛽ Set of utilities to test Laravel applications powered by Octane.

⛽ Octane Testbench Set of utilities to test Laravel applications powered by Octane. Install Via Composer: composer require --dev cerbero/octane-testbe

Andrea Marco Sartori 24 Nov 15, 2021
Update multiple Laravel Model records, each with it's own set of values, sending a single query to your database!

Laravel Mass Update Update multiple Laravel Model records, each with its own set of values, sending a single query to your database! Installation You

Jorge González 24 Jan 5, 2022
Cagilo - a set of simple components for use in your views Laravel Blade.

Cagilo - a set of simple components for use in your views Laravel Blade. Official Documentation Documentation for Cagilo can be found on its we

Cagilo 128 Jan 11, 2022
This package gives you a set of conventions to make the most out of Hotwire in Laravel

Introduction This package gives you a set of conventions to make the most out of Hotwire in Laravel (inspired by the turbo-rails gem). There is a comp

Tony Messias 523 Jan 8, 2022
Laracademy Generators - is a tool set that helps speed up the development process of a Laravel application.

Laracademy Generators Laracademy Generators - is a tool set that helps speed up the development process of a Laravel application. Author(s): Laracadem

Laracademy 283 Jan 11, 2022
Laravel package for giving admin-created accounts to users via 'set-password' email.

Invytr When making a website where users are created instead of registering themselves, you are faced with the challenge of safely giving users the ac

GlaivePro 63 Jan 2, 2022
This Laravel 8 package makes it possible for you to set your website in "Under Construction" mode.

Laravel Under Construction This Laravel package makes it possible to set your website in "Under Construction" mode. Only users with the correct 4 digi

Lars Janssen 559 Jan 9, 2022
Blade UI Kit is a set of renderless components to utilise in your Laravel Blade views

Blade UI Kit is a set of renderless components to utilise in your Laravel Blade views. In all essence, it's a collection of useful utilities, connecting the dots between different parts of the TALL stack. It was made for Blade, Laravel's powerful templating engine.

Blade UI Kit 1k Jan 14, 2022
Easily set up a sync endpoint to support Watermelon DB in your Laravel projects.

Laravel Watermelon This package provides a Watermelon DB backend sync implementation for Laravel. Watermelon DB is a robust local database synchroniza

Nathan Heffley 8 Nov 23, 2021
A Laravel 8 and Livewire 2 demo showing how to search and filter by tags, showing article and video counts for each tag (Polymorphic relationship)

Advanced search and filter with Laravel and Livewire A demo app using Laravel 8 and Livewire 2 showing how to implement a list of articles and tags, v

Sérgio Jardim 9 Jan 10, 2022
Boilerplate code for protecting a form with proof of work. Uses javascript in the browser to generate the hashcash and PHP on the server to generate the puzzle and validate the proof of work.

Boilerplate code for protecting a form with proof of work. Uses javascript in the browser to generate the hashcash and PHP on the server to generate the puzzle and validate the proof of work.

Jameson Lopp 12 Nov 16, 2021
Stop duplicating your Eloquent query scopes and constraints in PHP. This package lets you re-use your query scopes and constraints by adding them as a subquery.

Laravel Eloquent Scope as Select Stop duplicating your Eloquent query scopes and constraints in PHP. This package lets you re-use your query scopes an

Protone Media 63 Dec 28, 2021