Error handling
Every exception the engine raises lives under
Qanna\ExpressionEngine\Exceptions and extends \RuntimeException.
Expressions on a low-code surface are authored by people (or stored config) that can get them wrong, so evaluating untrusted expressions should always be wrapped:
use Qanna\ExpressionEngine\Exceptions\LexerException;
use Qanna\ExpressionEngine\Exceptions\ParserException;
use Qanna\ExpressionEngine\Exceptions\EvaluatorException;
try {
$value = $engine->evaluate($storedExpression, $context);
} catch (LexerException | ParserException $e) {
// the expression string is malformed
} catch (EvaluatorException $e) {
// the expression is well-formed but failed to run against this data
}
The exceptions
LexerException
The expression contains a character or token the language doesn't
recognise — an unterminated string, a stray symbol, a single = (did
you mean ==?), a single & or |.
Extends \RuntimeException directly (not EvaluatorException). The
message includes position N.
ParserException
The tokens are valid but their arrangement isn't — an unbalanced
parenthesis, a missing : in a ternary, a trailing operator, an
unexpected token.
Extends \RuntimeException directly. The message includes position N,
which the playground uses to draw a caret and you
can use the same way in an editor UI.
EvaluatorException
The base class for everything that goes wrong while running a well-formed expression. Raised directly for:
- division or modulo by zero (the
/and%operators) - an operator applied to incompatible types (e.g.
-"abc") - an unknown function name
- a date string that can't be parsed
env()called with a non-whitelisted keysqrt()of a negative number
UnknownMethodException — extends EvaluatorException
A method name that isn't registered for any type.
Unknown method 'uppercase' on type 'string'.
TypeMismatchException — extends EvaluatorException
A method that exists, but not for the type it was called on — e.g.
.sum() on a string.
Method 'sum' does not support type 'string'. Expected 'array'.
UnresolvedPathException — extends EvaluatorException
Only thrown in strict mode: a path couldn't be resolved from the context.
Path 'trigger.user' could not be resolved from context.
In the default (non-strict) mode, an unresolved path is null and this
is never thrown.
Catching everything
LexerException and ParserException do not extend
EvaluatorException, so a single catch (EvaluatorException $e) misses
syntax errors. To handle any engine failure in one place, catch all
three base types, or \RuntimeException:
try {
$value = $engine->evaluate($storedExpression, $context);
} catch (\RuntimeException $e) {
report($e);
$value = null;
}
A note on non-engine errors
div() and mod() by zero raise PHP's native \DivisionByZeroError
(not an engine exception). If you evaluate expressions that can reach
those methods with untrusted input, catch \Throwable rather than only
the engine's own hierarchy.