Stars: 114
Forks: 20
Pull Requests: 10
Issues: 15
Watchers: 7
Last Updated: 2022-03-20 22:20:27
PHP Rule Engine - Parses & Evaluates JavaScript-like expressions
License: MIT License
Languages: PHP, HCL
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
(If you're using PHP 5, you might want to take a look at version 0.4.0)
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 |
Test if a value is in a given array
$variables = ['foo' => 6];
$rule = new Rule('foo in [4, 6, 7]', $variables);
var_dump($rule->isTrue()); // bool(true)
Simple array manipulation
$rule = new Rule('[1, 4, 3].join(".") === "1.4.3"');
var_dump($rule->isTrue()); // bool(true)
Test if a value is between a given range
$variables = ['threshold' => 80];
$rule = new Rule('threshold >= 50 && threshold <= 100', $variables);
var_dump($rule->isTrue()); // bool(true)
Call methods on objects from within rules
class User
{
// ...
public function points(): int
{
return 1337;
}
}
$variables = [
'user' => new User(),
];
$rule = new Rule('user.points() > 300', $variables);
var_dump($rule->isTrue()); // bool(true)
For security reasons, PHP's magic methods like __construct
and __destruct
cannot be
called from within rules. However, __call
will be invoked automatically if available,
unless the called method is defined.
Name | Example |
---|---|
charAt | "foo".charAt(2) === "o" |
concat | "foo".concat("bar", "baz") === "foobarbaz" |
endsWith | "foo".endsWith("oo") === true |
startsWith | "foo".startsWith("fo") === true |
indexOf | "foo".indexOf("oo") === 1 |
join | ["foo", "bar"].join(",") === "foo,bar" |
replace | "foo".replace("oo", "aa") === "faa" |
split | "foo-bar".split("-") === ["foo", "bar"] |
substr | "foo".substr(1) === "oo" |
test | "foo".test(/oo$/) === true |
toLowerCase | "FOO".toLowerCase() === "foo" |
toUpperCase | "foo".toUpperCase() === "FOO" |
Name | Example |
---|---|
parseInt | parseInt("22aa") === 22 |
parseFloat | parseFloat("3.1") === 3.1 |
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 |
Containment | does not contain | not in |
Logical | and | && |
Logical | or | || |
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:
If you discover any security related issues, please email [email protected] 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.
my_func() && 2 > 1
should work