Skip to content

Repository files navigation

PHP Rule Engine

Latest Stable VersionTotal DownloadsCode QualityStyleCICode Coverage

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.

Install

Via Composer

composer install nicoswd/php-rule-parser

Usage Examples

E-commerce: Validate coupon eligibility

$variables = [
'cart_total' => 120,
'user_tier' => 'gold',
'is_blacklisted' => false,
];
$rule = newRule(' cart_total >= 50 &&  user_tier in ["gold", "platinum"] && !is_blacklisted', $variables);
var_dump($rule->isTrue()); // bool(true) — eligible for discount

Access control: Check user permissions

$variables = [
'role' => 'editor',
'is_suspended' => false,
];
$rule = newRule('role in ["admin", "editor"] && !is_suspended', $variables);
var_dump($rule->isTrue()); // bool(true) — access granted

Pricing: Calculate order total with conditions

$variables = [
'base_price' => 29.99,
'tax_rate' => 21,
'quantity' => 3,
];
$rule = newRule('(base_price + (base_price * tax_rate / 100)) * quantity', $variables);
var_dump($rule->result()); // float(108.8571...) — total with tax

Form validation: Check input constraints

$variables = [
'age' => 25,
'country' => 'US',
];
$rule = newRule('age >= 18 && country in ["US", "CA", "UK"]', $variables);
var_dump($rule->isTrue()); // bool(true) — valid registration

Notification routing: Target specific users

$variables = [
'plan' => 'pro',
'last_login' => 3,
'notification_opt_out' => false,
];
$rule = newRule(' plan in ["pro", "enterprise"] && last_login < 7 && !notification_opt_out', $variables);
var_dump($rule->isTrue()); // bool(true) — send notification

Feature flags: Roll out features gradually

$variables = [
'user_id' => 7,
];
$rule = newRule('user_id % 10 < 3', $variables);
var_dump($rule->isTrue()); // bool(true) — feature enabled for this user

String manipulation: Format user data

$variables = [
'firstName' => 'John',
'lastName' => 'Doe',
];
$rule = newRule('firstName.toUpperCase() + " " + lastName.toUpperCase()', $variables);
var_dump($rule->result()); // string("JOHN DOE")

Object method calls: Evaluate complex conditions

class Subscription
{
publicfunctionisActive(): bool
{
returntrue;
}
publicfunctiondaysUntilExpiry(): int
{
return15;
}
}
$variables = [
'subscription' => newSubscription(),
];
$rule = newRule('subscription.isActive() && subscription.daysUntilExpiry() > 7', $variables);
var_dump($rule->isTrue()); // bool(true) — subscription is active and not expiring soon

Arithmetic: Operator precedence

$rule = newRule('2 + 3 * 4 == 14');
var_dump($rule->isTrue()); // bool(true) - multiplication before addition$rule = newRule('(2 + 3) * 4 == 20');
var_dump($rule->isTrue()); // bool(true) - parentheses override precedence

Arithmetic: Unary operators

$rule = newRule('-5 * 3 == -15');
var_dump($rule->isTrue()); // bool(true) - unary minus$rule = newRule('!false');
var_dump($rule->isTrue()); // bool(true) - logical NOT$rule = newRule('!(1 == 2)');
var_dump($rule->isTrue()); // bool(true) - NOT with comparison

Note

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.

Built-in Methods

NameExample
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"

Built-in Functions

NameExample
parseIntparseInt("22aa") === 22
parseFloatparseFloat("3.1") === 3.1

Supported Operators

TypeDescriptionOperator
Comparisongreater than>
Comparisongreater than or equal to>=
Comparisonless than<
Comparisonless or equal to<=
Comparisonequal to==
Comparisonnot equal to!=
Comparisonidentical===
Comparisonnot identical!==
Containmentcontainsin
Containmentdoes not containnot in
Logicaland&&
Logicalor||
Arithmeticaddition+
Arithmeticsubtraction-
Arithmeticmultiplication*
Arithmeticdivision/
Arithmeticmodulo%
Unarynegation-
Unarylogical NOT!

Error Handling

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 = newRule($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

Syntax Highlighting

A custom syntax highlighter is also provided.

usenicoSWD\Rule\Highlighter\Highlighter;
usenicoSWD\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 = newHighlighter();
// Optional custom styles$highlighter->setStyle(
TokenType::VARIABLE,
'color: #007694; font-weight: 900;'
);
echo$highlighter->highlightString($ruleStr);

Outputs:

Syntax preview

Security

If you discover any security related issues, please email security@nic0.me instead of using the issue tracker.

Testing

$ composer test

Contributing

Pull 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.

To Do

  • Support for object properties (foo.length)
  • Support for array / string dereferencing: "foo"[1]
  • Support for returning actual results, other than true or false
  • Don't force boolean comparison for tokens that are already booleans. my_func() && 2 > 1 should work
  • Allow string concatenating with "+"
  • Duplicate regex modifiers should throw an error
  • Add support for function calls
  • Support for regular expressions
  • Fix build on PHP 7 / Nightly
  • Allow variables in arrays
  • Verify function and method name spelling (.tOuPpErCAse() is currently valid)
  • Change regex and implementation for method calls
  • Add / implement missing methods
  • Invalid regex modifiers should not result in an unknown token
  • ...

License

License

About

PHP Rule Engine - Parses & Evaluates JavaScript-like expressions

Topics

Resources

Stars

130 stars

Watchers

6 watching

Forks

Releases

Used by

Contributors

Languages