abivia / criteria
A simple logical expression processor with regex capability.
Requires
- php: ^8.2
Requires (Dev)
- phpunit/phpunit: ^11.5
README
This is a simple package for evaluating boolean logic in a data structure.
The entry point is Criteria::evaluate(array $criteria, callable $accessor)
.
$criteria
is an array of terms. If any of the terms evaluates as true, evaluate
returns.
This is an implicit "or" operation.
Terms
A term is an array with these elements:
arg
- the name of a value to be retrieved via the accessor callback.op
- A comparison operator, see below.value
orvar
- Required for binary operators (i.e. those other than null, !null), the value to be compared or the regular expression to be matched. Thevalue
can be an array. If an array value is provided for a binary operator (other than "in" or "includes"), each value is compared and the firsttrue
result causes the expression to be true. Ifvar
is provided, it must be a string. This is passed to the accessor callback to retrieve a value from the application.name
- An optional name, used when reporting errors.invert
- An optional boolean. When true, the result of the current (sub-)criteria is inverted.and
- An optional list of sub-criteria. The list is evaluated as a series of logical "or" conjunctions, then a logical "and" is performed against the result of the argument/value operation.xor
- Similar to theand
element instead the result is exclusive or-ed with the result.
Element names are configurable to suit the application. See "property configuration" below.
Operators
The operator is one of ==, ===, !=, !==, >, >=, <, <=, contains, !contains, in, !in, includes, !includes, null, !null, regex, !regex. If no operator is specified, then == is assumed.
- The "contains" operator is true if
var
/value
is a substring ofarg
. This test is case-sensitive. - The "in" operator is true if
arg
can be found invar
/value
, which normally is an array. If botharg
andvar
/value
are scalar, "in" is equivalent to ==. Ifarg
is an array, then the expression is true if all elements inarg
can be found invar
/value
. - The "includes" operator is true if
var
/value
can be found inarg
, which normally is an array. If botharg
andvar
/value
are scalar, "in" is equivalent to ==. If botharg
andvar
/value
are arrays, then the expression is true if all elements invar
/value
can be found inarg
. - The "null" operator is unary, any
var
/value
is ignored. - The "regex" operator expects a PHP regular expression.
- A ! in front of "contains", "in", "includes", and "regex" will invert the result of the test.
Examples
Criteria are specified in JSON for readability.
[ { "arg": "language", "op": "==", "value": "en" } ]
Will evaluate true if the value of acessor('language')
is "en".
[ { "arg": "firstName", "var": "lastName" } ]
This assumes the operator is == and Will evaluate true
if acessor('firstName')
is equal to acessor('lastName')
.
[ { "arg": "firstName", "op": "==", "var": "lastName" }, { "arg": "firstName", "op": "==", "var": "" } ]
Will evaluate true if acessor('firstName')
is equal to acessor('lastName')
or if acessor('firstName')
is an empty string.
[ { "arg": "province", "op": "==", "value": "ON", "and": [ { "arg": "areaCode", "op": "in", "value": [ "226", "249", "289", "343", "365", "416", "437", "519", "548", "613", "647", "705", "807", "905" ] } ] } ]
Will evaluate true if acessor('province')
is "ON"
and accessor('areaCode')
matches one of the values in the list.
Operator Access Control
If your application is processing user-generated data and you want to
restrict access to some operators, this is possible with the
operatorState
option.
$criteria = new \Abivia\Criteria\Criteria( ['operatorState' => ['regex' => false, '!regex' => false]] );
This will disable use of the regular expression operators.
Property Configuration
Property names used in the criteria can be changed at instantiation by passing an array of replacements. Only the passed in properties will be modified.
$criteria = new \Abivia\Criteria\Criteria( ['overrides' => ['arg' => 'left', 'op' => 'operator', 'value' => 'right']] );
Would change a valid input to:
[ { "left": "firstName", "operator": "==", "right": "lastName", "invert": true } ]