If you're using PHP 5, you might want to look at version 0.4.0.
You're looking at a standalone PHP library to parse and evaluate text based rules with a Javascript-like syntax. This project was born out of the necessity to evaluate hundreds of rules that were originally written and evaluated in JavaScript, and now needed to be evaluated on the server-side, using PHP.
This library has initially been used to change and configure the behavior of certain "Workflows" (without changing actual code) in an intranet application, but it may serve a purpose elsewhere.
Find me on Twitter: @nicoSWD
Via Composer
$ composer require nicoswd/php-rule-parser
This library works best with one of these bundles below, but they're not required
Bundle | Framework | Packagist |
---|---|---|
nicoSWD/rule-engine-bundle | Symfony |
use nicoSWD\Rule\Rule;
// Composer install
require '/path/to/vendor/autoload.php';
$variables = ['foo' => 6];
$rule = new Rule('foo in [4, 6, 7]', $variables);
var_dump($rule->isTrue()); // bool(true)
$rule = new Rule('bar in "foo|bar|baz".split("|")');
var_dump($rule->isTrue()); // bool(true)
$rule = new Rule('[1, 4, 3].join("") === "143"');
var_dump($rule->isTrue()); // bool(true)
$variables = ['threshold' => 80];
$rule = new Rule('threshold >= 50 && threshold <= 100', $variables);
var_dump($rule->isTrue()); // bool(true)
$ruleStr = 'double(value) === result';
$variables = [
'value' => 2,
'result' => 4
];
$rule = new Rule($ruleStr, $variables);
$rule->registerFunction('double', function (BaseToken $multiplier): BaseToken {
if (!$multiplier instanceof TokenInteger) {
throw new \Exception;
}
return new TokenInteger($multiplier->getValue() * 2);
});
var_dump($rule->isTrue()); // bool(true)
Tokens can be customized, if desired. Note that it's very easy to break stuff by doing that, if you have colliding regular expressions.
You may want to set a different $priority
when registering a token: ::registerToken($type, $regex, $priority)
(take a look at Tokenizer::__construct()
for more info).
$ruleStr = ':this is greater than :that';
$variables = [
':this' => 8,
':that' => 7
];
$rule = new Rule($ruleStr, $variables);
$rule->registerToken(Tokenizer::TOKEN_GREATER, '\bis\s+greater\s+than\b');
$rule->registerToken(Tokenizer::TOKEN_VARIABLE, ':[a-zA-Z_][\w-]*');
var_dump($rule->isTrue()); // bool(true)
Also note that the original tokens will no longer be recognized after overwriting them. Thus, if you want to implement aliases
for custom tokens, you have to group them into one regular expression: (?:\b(?:is\s+)?greater\s+than\b|>)
Both, $rule->isTrue()
and $rule->isFalse()
will throw an exception if the syntax is invalid. These calls can either be placed inside a try
/ catch
block, or it can be checked prior using $rule->isValid()
.
$ruleStr = '
(2 == 2) && (
1 < 3 && 3 == 2 ( // Missing and/or before parentheses
1 == 1
)
)';
$rule = new Rule($ruleStr);
try {
$rule->isTrue();
} catch (\Exception $e) {
echo $e->getMessage();
}
Or alternatively:
if (!$rule->isValid()) {
echo $rule->getError();
}
Both will output: Unexpected token "(" at position 25 on line 3
A custom syntax highlighter is also provided.
use nicoSWD\Rule;
$ruleStr = '
// This is true
2 < 3 && (
// This is false
foo in [4, 6, 7] ||
// True
[1, 4, 3].join("") === "143"
) && (
// True
"foo|bar|baz".split("|" /* uh oh */) === ["foo", /* what */ "bar", "baz"] &&
// True
bar > 6
)';
$highlighter = new Rule\Highlighter\Highlighter(new Rule\Tokenizer());
// Optional custom styles
$highlighter->setStyle(
Rule\Constants::GROUP_VARIABLE,
'color: #007694; font-weight: 900;'
);
echo $highlighter->highlightString($ruleStr);
Outputs:
Type | Description | Operator |
---|---|---|
Comparison | greater than | > |
Comparison | greater than or equal to | >= |
Comparison | less than | < |
Comparison | less or equal to | <= |
Comparison | equal to | == |
Comparison | not equal to | != |
Comparison | identical | === |
Comparison | not identical | !== |
Containment | contains | in |
Logical | and | && |
Logical | or | || |
- Parentheses can be nested, and will be evaluated from right to left.
- Only value/variable comparison expressions with optional logical ANDs/ORs, are supported.
If you discover any security related issues, please email security@nic0.me instead of using the issue tracker.
$ composer test
Pull requests are very welcome! If they include tests, even better. This project follows PSR-2 coding standards, please make sure your pull requests do too.
- Support for object properties (foo.length)
- Support for returning actual results, other than true or false
- Support for array / string dereferencing: "foo"[1]
- Change regex and implementation for method calls. ".split(" should not be the token
- Add / implement missing methods
- Add "typeof" construct
- Do math (?)
- Allow string concatenating with "+"
- Support for objects {} (?)
- Invalid regex modifiers should not result in an unknown token
- Duplicate regex modifiers should throw an error
Add support for function callsSupport for regular expressionsFix build on PHP 7 / NightlyAllow variables in arraysVerify function and method name spelling (.tOuPpErCAse() is currently valid)- ...