I just saw
and the thing that stood out to me was
api_check = is_admin() | (
is_active()
& account_older_than(30)
& ~is_banned()
& from_country(["NL", "BE"])
& (credit_score_above(650) | has_override())
The reason is because this is tied to a class.
class Predicate[T]:
"""
A composable predicate that supports &, |, and ~ operators.
Wraps a function (T -> bool).
"""
def __init__(self, fn: PredicateFn[T]):
self.fn = fn
def __call__(self, obj: T) -> bool:
return self.fn(obj)
def __and__(self, other: Predicate[T]) -> Predicate[T]:
return Predicate(lambda x: self(x) and other(x))
def __or__(self, other: Predicate[T]) -> Predicate[T]:
return Predicate(lambda x: self(x) or other(x))
def __invert__(self) -> Predicate[T]:
return Predicate(lambda x: not self(x))
So I was thinking how can I do that in PHP.
And the answer is the Symfony expression language component
The component makes it possible to do.
$expr = 'is_admin($user) || (
is_active($user)
&& account_older_than($user, 30)
&& !is_banned($user)
&& from_country($user, ["NL", "BE"])
&& (credit_score_above($user, 650) || has_override($user))
)';
$lang = new ExpressionLanguage();
// Register functions (compiler -> PHP code; evaluator -> runtime callable)
$lang->register('is_admin',
fn() => '($user["is_admin"] ?? false)',
fn(array $vars) => $vars['user']['is_admin'] ?? false
);
$lang->register('is_active',
fn() => '($user["is_active"] ?? false)',
fn(array $vars) => $vars['user']['is_active'] ?? false
);
// more register calls
$user = [
'is_admin' => false,
'is_active' => true,
'created_at' => (new DateTime('-40 days'))->format('Y-m-d H:i:s'),
'banned' => false,
'country' => 'NL',
'credit_score' => 660,
'override' => false,
];
// Evaluate
$result = $lang->evaluate($expr, ['user' => $user]);
The register method is a bit clunky because the second argument is called when the compile method executes, and the third argument is called when the evaluate method is executed.
But the goal, having rules as data, is achieved.
It would have been nicer if PHP had operator magic methods like Python.
At least the PHP version can enforce type checking, which is not the case for the Python code.
For people who want to know more about the Python overloading methods go to Real Python.
Top comments (0)