A small expression engine for PHP.
composer require eventjet/ausdruck
use Eventjet\Ausdruck\Parser\ExpressionParser;
use Eventjet\Ausdruck\Parser\Types;
class Person { public function __construct(public string $name) {} }
$expression = ExpressionParser::parse(
'joe:MyPersonType.name:string()',
new Types(['MyPersonType' => Person::class]),
);
$scope = new Scope(
// Passing values to the expression
['joe' => new Person('Joe')],
// Custom function definitions
['name' => static fn (Person $person): string => $person->name],
);
$name = $expression->evaluate($scope);
assert($name === 'Joe');
Syntax: varName:type
Scope variables are passed from PHP when it calls evaluate()
on the expression:
use Eventjet\Ausdruck\Parser\ExpressionParser;
use Eventjet\Ausdruck\Scope;
$x = ExpressionParser::parse('foo:int')
->evaluate(new Scope(['foo' => 123]));
assert($x === 123);
foo:int
, foo:list<string>
See Types
123
: Integer"foo"
: String1.23
: Float[1, myInt:int, 3]
: List of integers["foo", myString:string, "bar"]
: List of strings
Both operands must be of the same type.
Operator | Description | Example | Note |
---|---|---|---|
=== |
Equality | foo:string === "bar" |
|
- |
Subtraction | foo:int - bar:int |
Operands must be of type int or float |
> |
Greater than | foo:int > bar:int |
Operands must be of type int or float |
|| |
Logical OR | foo:bool || bar:bool |
Operands must be of type bool |
&& | Logical AND | foo:bool && bar:bool |
Operands must be of type bool |
Where's the rest? We're implementing more as we need them.
The following types are supported:
int
: Integerstring
: Stringbool
: Booleanfloat
: Floating point numberlist<T>
: List of type Tmap<K, V>
: Map with key type K and value type V- Any other type will be treated as an alias that you will have to provide when parsing the expression:
use Eventjet\Ausdruck\Parser\ExpressionParser; use Eventjet\Ausdruck\Type; ExpressionParser::parse('foo:MyType', ['MyType' => Type::object(Foo::class)]);
Syntax: target.functionName:returnType(arg1, arg2, ...)
The target can be any expression. It will be passed as the first argument to the function.
haystack:list<string>.contains:bool(needle:string)
Function | Description | Example |
---|---|---|
count |
Returns the number of elements in a list | foo:list<string>.count:int() |
contains |
Returns whether a list contains a value | foo:list<string>.contains:bool("bar") |
head |
Returns the first element of a list as an Option |
foo:list<string>.head:Option<string>() |
isSome |
Takes an Option and returns whether it is Some |
foo:Option<int>.isSome:bool() |
map |
Returns a new list with the results of applying a function | foo:list<int>.map:list<int>(|i| i:int - 2) |
some |
Returns whether any element matches a predicate | foo:list<int>.some:bool(|item| item:int > 5) |
substr |
Returns a substring of a string | foo:string.substr:string(0, 5) |
tail |
Returns all elements of a list except the first | foo:list<string>.tail:list<string>() |
take |
Returns the first n elements of a list | foo:list<string>.take:list<string>(5) |
unique |
Returns a list with duplicate elements removed | foo:list<string>.unique:list<string>() |
You can pass custom functions along with the scope variables:
use Eventjet\Ausdruck\Parser\ExpressionParser;use Eventjet\Ausdruck\Scope;
$scope = new Scope(
['foo' => 'My secret'],
['mask' => fn (string $str, string $mask) => str_repeat($mask, strlen($str))]
);
$result = ExpressionParser::parse('foo:string.mask("x")')->evaluate($scope);
assert($result === 'xxxxxxxxx');
The target of the function/method call (foo:string
in the example above) will be passed as the first argument to the
function.
Syntax: |arg1, arg2, ... | expression
To access an argument, you must specify its type, just like when accessing scope variables.
|item| item:int > 5