A PHP library that parses and evaluates boolean expressions using a JavaScript-like syntax. It supports variables, comparison and logical operators, arithmetic, regular expressions, arrays, string methods, and function calls, all from plain text rules.
Via Composer
composer install nicoswd/php-rule-parserE-commerce: Validate coupon eligibility
$variables = [
'cart_total' => 120,
'user_tier' => 'gold',
'is_blacklisted' => false,
];
$rule = new Rule('
cart_total >= 50 &&
user_tier in ["gold", "platinum"] &&
!is_blacklisted
', $variables);
var_dump($rule->isTrue()); // bool(true) — eligible for discountAccess control: Check user permissions
$variables = [
'role' => 'editor',
'is_suspended' => false,
];
$rule = new Rule('role in ["admin", "editor"] && !is_suspended', $variables);
var_dump($rule->isTrue()); // bool(true) — access grantedPricing: Calculate order total with conditions
$variables = [
'base_price' => 29.99,
'tax_rate' => 21,
'quantity' => 3,
];
$rule = new Rule('(base_price + (base_price * tax_rate / 100)) * quantity', $variables);
var_dump($rule->result()); // float(108.8571...) — total with taxForm validation: Check input constraints
$variables = [
'age' => 25,
'country' => 'US',
];
$rule = new Rule('age >= 18 && country in ["US", "CA", "UK"]', $variables);
var_dump($rule->isTrue()); // bool(true) — valid registrationNotification routing: Target specific users
$variables = [
'plan' => 'pro',
'last_login' => 3,
'notification_opt_out' => false,
];
$rule = new Rule('
plan in ["pro", "enterprise"] &&
last_login < 7 &&
!notification_opt_out
', $variables);
var_dump($rule->isTrue()); // bool(true) — send notificationFeature flags: Roll out features gradually
$variables = [
'user_id' => 7,
];
$rule = new Rule('user_id % 10 < 3', $variables);
var_dump($rule->isTrue()); // bool(true) — feature enabled for this userString manipulation: Format user data
$variables = [
'firstName' => 'John',
'lastName' => 'Doe',
];
$rule = new Rule('firstName.toUpperCase() + " " + lastName.toUpperCase()', $variables);
var_dump($rule->result()); // string("JOHN DOE")Object method calls: Evaluate complex conditions
class Subscription
{
public function isActive(): bool
{
return true;
}
public function daysUntilExpiry(): int
{
return 15;
}
}
$variables = [
'subscription' => new Subscription(),
];
$rule = new Rule('subscription.isActive() && subscription.daysUntilExpiry() > 7', $variables);
var_dump($rule->isTrue()); // bool(true) — subscription is active and not expiring soonArithmetic: Operator precedence
$rule = new Rule('2 + 3 * 4 == 14');
var_dump($rule->isTrue()); // bool(true) - multiplication before addition
$rule = new Rule('(2 + 3) * 4 == 20');
var_dump($rule->isTrue()); // bool(true) - parentheses override precedenceArithmetic: Unary operators
$rule = new Rule('-5 * 3 == -15');
var_dump($rule->isTrue()); // bool(true) - unary minus
$rule = new Rule('!false');
var_dump($rule->isTrue()); // bool(true) - logical NOT
$rule = new Rule('!(1 == 2)');
var_dump($rule->isTrue()); // bool(true) - NOT with comparisonNote
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") |
| startsWith | "foo".startsWith("fo") |
| 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$/) |
| 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 | || |
| Arithmetic | addition | + |
| Arithmetic | subtraction | - |
| Arithmetic | multiplication | * |
| Arithmetic | division | / |
| Arithmetic | modulo | % |
| Unary | negation | - |
| Unary | logical NOT | ! |
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 validity can be checked beforehand 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->error;
}Both will output: Unexpected "(" at position 28
A custom syntax highlighter is also provided.
use nicoSWD\Rule\Highlighter\Highlighter;
use nicoSWD\Rule\TokenStream\Token\TokenType;
$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 Highlighter();
// Optional custom styles
$highlighter->setStyle(
TokenType::VARIABLE,
'color: #007694; font-weight: 900;'
);
echo $highlighter->highlightString($ruleStr);Outputs:
If you discover any security related issues, please email security@nic0.me instead of using the issue tracker.
$ composer testPull requests are very welcome! If they include tests, even better. This project follows PSR-12 coding standards, please make sure your pull requests do too.
- Support for object properties (foo.length)
- Support for array / string dereferencing: "foo"[1]
Support for returning actual results, other than true or falseDon't force boolean comparison for tokens that are already booleans.my_func() && 2 > 1should workAllow string concatenating with "+"Duplicate regex modifiers should throw an errorAdd support for function callsSupport for regular expressionsFix build on PHP 7 / NightlyAllow variables in arraysVerify function and method name spelling (.tOuPpErCAse() is currently valid)Change regex and implementation for method callsAdd / implement missing methodsInvalid regex modifiers should not result in an unknown token- ...
