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:

Comments
  • 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
  • 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
  • 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
  • Lint

    Lint

    Hi,

    I would really like to be able to lint an expression. Ruler::interpret() seems like a candidate, but doesn't really cut it.

    Something like Ruler::lint($rule):bool would be enough for my purposes. A ::getLintErrors() is optional and could be implemented later. Examples: lint('points > 40') > true lint('points && true') > false

    Could something like this be done? I am also willing to build it myself, if anyone can provide me with some guidance.

    enhancement question difficulty: medium 
    opened by dhaarbrink 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
  • 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
  • Interpreted rule should be available in JSON format

    Interpreted rule should be available in JSON format

    serialize( Hoa\Ruler::interprete( 'logged(user) and group in ("customer", "guest") and points > 30' )

    This might be good for storing in a relational db, but for those of us using NoSql db like Mongo, It would be nice to get a JSON encoded rule model ...

    opened by bthibault 13
  • Support arimethical expressions

    Support arimethical expressions

    See https://github.com/hoaproject/Ruler/issues/11 for more details.

    You can test with the following program:

    <?php
    
    require '/usr/local/lib/Hoa/Core/Core.php';
    
    Hoa\Core::enableErrorHandler();
    Hoa\Core::enableExceptionHandler();
    
    // Rule.
    $rule = '-(10 - 2 - 2 - 1) = -y[0]';
    
    // Compile and dump the AST.
    echo (new Hoa\Compiler\Visitor\Dump())->visit(
        Hoa\Ruler\Ruler::getCompiler()->parse($rule)
    ), "\n\n";
    
    // Get the model directly (use the helper, just because I'm tired :-)).
    $imodel = Hoa\Ruler\Ruler::interprete($rule);
    
    echo $imodel, "\n\n";
    
    // Context.
    $context      = new Hoa\Ruler\Context();
    $context['y'] = [5];
    
    // Prepare some functions.
    $asserter = new Hoa\Ruler\Visitor\Asserter($context);
    $asserter->setOperator('f', function ( $x ) {
    
        return $x;
    });
    
    // First assert.
    var_dump($asserter->visit($imodel));
    
    // Here copy the result of the output of $imodel (which is the result of the
    // compiler) to compare the result (test the model).
    $model = new \Hoa\Ruler\Model();
    $model->expression =
        $model->{'='}(
            $model->func(
                '-',
                $model->{'-'}(
                    10,
                    $model->{'-'}(
                        2,
                        $model->{'-'}(
                            2,
                            1,
                            -1
                        ),
                        -1
                    ),
                    1
                )
            ),
            $model->func(
                '-',
                $model->variable('y')
                    ->index(
                        0
                    )
            )
        );
    
    // Assert again.
    var_dump($asserter->visit($model));
    
    // Compare the disassembler of both models (from the rule and from the model).
    $disassembly = new Hoa\Ruler\Visitor\Disassembly();
    echo $disassembly->visit($imodel), "\n",
         $disassembly->visit($model), "\n";
    

    and here is the result:

    >  #expression
    >  >  #operation
    >  >  >  #negative
    >  >  >  >  #substraction
    >  >  >  >  >  token(number, 10)
    >  >  >  >  >  #substraction
    >  >  >  >  >  >  token(number, 2)
    >  >  >  >  >  >  #substraction
    >  >  >  >  >  >  >  token(number, 2)
    >  >  >  >  >  >  >  token(number, 1)
    >  >  >  token(identifier, =)
    >  >  >  #negative
    >  >  >  >  #variable_access
    >  >  >  >  >  token(identifier, y)
    >  >  >  >  >  #array_access
    >  >  >  >  >  >  token(number, 0)
    
    
    $model = new \Hoa\Ruler\Model();
    $model->expression =
        $model->{'='}(
            $model->func(
                '-',
                $model->{'-'}(
                    10,
                    $model->{'-'}(
                        2,
                        $model->{'-'}(
                            2,
                            1,
                            -1
                        ),
                        -1
                    ),
                    1
                )
            ),
            $model->func(
                '-',
                $model->variable('y')
                    ->index(
                        0
                    )
            )
        );
    
    bool(true)
    bool(true)
    (-((10 - (2 - (2 - 1)))) = -(y[0]))
    (-((10 - (2 - (2 - 1)))) = -(y[0]))
    

    The only BC break introduced is the array declaration syntax ((1, 2, 3) to [1, 2, 3]). This is the first BC break of Hoa :'-), but since the library is still in the RC state, we can do this. Only the syntax (so the grammar and the disassembler) is impacted; the interpreter, the asserter and the model are not impacted (so unserializing an existing rule from a database will not break).

    Also, the README is updated consequently.

    enhancement 
    opened by Hywan 11
  • Allow an identifier to start by a digit

    Allow an identifier to start by a digit

    Fix https://github.com/hoaproject/Ruler/issues/32.

    Guys, the regex is overly complicated. I am still not a big fan of this feature :-(. Do you think it is really needed?

    enhancement 
    opened by Hywan 9
  • 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
  • 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
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
Build structured navigation menus in Filament.

Build structured navigation menus in Filament. This plugin for Filament provides a Navigation resource that allows to build structural navigation menu

Ryan Chandler 61 Dec 30, 2022
Build structured navigation menus in Filament.

Build structured navigation menus in Filament. This plugin for Filament provides a Navigation resource that allows to build structural navigation menu

HappyToDev 0 Jun 29, 2022
Livewire DataTables components for back-end. Modular, easy to use, with tons of features.

Livewire DataTables Livewire DataTables components for back-end. Modular, easy to use, with tons of features. Inspired by Caleb's Livewire Screencasts

Amir Rami 8 Jul 27, 2022
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.3k Dec 27, 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 191 Dec 30, 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 848 Dec 28, 2022
A multitool library offering access to recommended security related libraries, standardised implementations of security defences, and secure implementations of commonly performed tasks.

SecurityMultiTool A multitool library offering access to recommended security related libraries, standardised implementations of security defences, an

Pádraic Brady 131 Oct 30, 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 Oct 31, 2022
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 49 Jun 22, 2022
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
Laravel-OvalFi helps you Set up, test, and manage your OvalFi integration directly in your Laravel App.

OvalFi Laravel Package Laravel-OvalFi helps you Set up, test, and manage your OvalFi integration directly in your Laravel App. Installation You can in

Paul Adams 2 Sep 8, 2022
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.5k Dec 31, 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 35 Dec 7, 2022
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 88 Dec 31, 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 151 Dec 6, 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 665 Jan 2, 2023
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 320 Dec 24, 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 64 Jul 17, 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 571 Dec 18, 2022