This document explains in detail how the PAD expression evaluation subsystem works.
The eval subsystem is responsible for parsing and evaluating expressions in PAD templates. When you write {$variable + 5 | trim} in a PAD template, this subsystem handles the parsing, variable resolution, operator execution, and pipe processing.
The main entry point is eval.php:
// Fast path: if expression is a simple function name, use optimized path
if ( file_exists ( PAD . "functions/$eval.php" ) )
return include PAD . 'eval/fast.php';
// Full evaluation pipeline
padEvalParse ( $result, $eval ); // Step 1: Parse into tokens
padEvalAfter ( $result ); // Step 2: Resolve types and operators
padEvalPipes ( $result, $pipes ); // Step 3: Split on pipe operators
foreach ( $pipes as $one )
$value = padEvalResult ( $one, $value, $eval ); // Step 4: Evaluate each pipe segment
return $value;
lib/eval/parse.php)The padEvalParse() function tokenizes the expression into an array of tokens. Each token is an array with:
[0] = The value/name[1] = The type (VAL, OPR, $, &, #, TYPE, pipe, open, close, etc.)[2] = Additional type info (for TYPE tokens)[3] = Parameter end position (for function calls)Token Types:
| Type | Meaning | Example |
|---|---|---|
VAL |
Literal value | 'hello', 123, 3.14 |
OPR |
Operator | +, -, *, LT, AND |
$ |
Variable reference | $name |
& |
Tag reference | &tagname |
# |
Option/parameter reference | #option |
$$ |
Property reference | @property or $$prop |
TYPE |
Typed value accessor | field:name, data:key |
pipe |
Pipe separator | \| |
open |
Open parenthesis | ( |
close |
Close parenthesis | ) |
a-open |
Array open bracket | [ |
a-close |
Array close bracket | ] |
hex |
Hexadecimal value | 0xFF |
other |
Unrecognized (becomes constant or type) | TRUE, fieldname |
Parsing Features:
') or double (") quotes\n, \r, \t, \\, \', \"0xFF1.5E-10-42**, <=, >=, ==, <>lib/eval/after.php)The padEvalAfter() function processes the parsed tokens:
field:name become TYPE tokensLT, AND) and alternates (< → LT)$var tokens get their values from padFieldValue()&tag tokens get values from padTagValue()#opt tokens get values from padOptValue()0xFF converts to binaryOperator Alternates:
| Symbol | Becomes |
|---|---|
< |
LT |
<= |
LE |
> |
GT |
>= |
GE |
= or == |
EQ |
<> |
NE |
lib/eval/pipes.php)The padEvalPipes() function splits the token stream at pipe (|) operators:
$name | trim | upper
Becomes three segments that are evaluated left-to-right, with each result passed to the next.
lib/eval/result.php)The padEvalResult() function evaluates each pipe segment:
padEvalValue ( $result, $value ); // Inject pipe input value
padEvalArray ( $result, $value ); // Handle array access [index]
padEvalOpnCls ( $result, $value ); // Handle parentheses
padEvalOpr ( $result, $value ); // Execute operators
padEvalMulti ( $result ); // Handle multiple expressions
lib/eval/const.php)Operators are evaluated in this order (highest to lowest):
const padEval_precedence = [
'!', // NOT (unary)
'**', '*', '/', '%', '+', '-', // Arithmetic
'.', // String concatenation
'LT', 'LE', 'GT', 'GE', 'EQ', 'NE', // Comparison
'AND', 'XOR', 'OR', // Logical
'NOT', // NOT (word form)
];
lib/eval/operations.php)The padEvalOpr() function:
Action Types:
| Action | File | Description |
|---|---|---|
single |
actions/single.php |
Unary operator with right operand |
singleRight |
actions/singleRight.php |
Unary with operator to the right |
double |
actions/double.php |
Binary operator with both operands |
doubleLeft |
actions/doubleLeft.php |
Binary with missing left operand |
doubleRight |
actions/doubleRight.php |
Binary with missing right operand |
alone |
actions/alone.php |
Operator with implicit operands |
go/go.php)Based on operand types, dispatches to:
| Left | Right | Handler |
|---|---|---|
| scalar | scalar | go/doubleVarVar.php |
| array | scalar | go/doubleArrVar.php |
| scalar | array | go/doubleVarArr.php |
| array | array | go/doubleArrArr.php |
| - | scalar | go/singleVar.php |
| - | array | go/singleArr.php |
go/doubleVarVar.php)// Comparison operators - return '1' or ''
if ( $opr == 'LT' ) $now = ($left < $right) ? 1 : '';
if ( $opr == 'LE' ) $now = ($left <= $right) ? 1 : '';
if ( $opr == 'EQ' ) $now = ($left == $right) ? 1 : '';
if ( $opr == 'GE' ) $now = ($left >= $right) ? 1 : '';
if ( $opr == 'GT' ) $now = ($left > $right) ? 1 : '';
if ( $opr == 'NE' ) $now = ($left <> $right) ? 1 : '';
// Logical operators
if ( $opr == 'AND' ) $now = ($left AND $right) ? 1 : '';
if ( $opr == 'OR' ) $now = ($left OR $right) ? 1 : '';
if ( $opr == 'XOR' ) $now = ($left XOR $right) ? 1 : '';
// String concatenation
if ( $opr == '.' ) $now = $left . $right;
// Arithmetic (with automatic int/float detection)
if ( $opr == '+' ) $now = $left + $right;
if ( $opr == '-' ) $now = $left - $right;
if ( $opr == '*' ) $now = $left * $right;
if ( $opr == '/' ) $now = $left / $right;
if ( $opr == '%' ) $now = $left % $right;
go/singleVar.php)// NOT operator - inverts truthiness
$now = ( $right ) ? '' : '1';
type/type.php)When a TYPE token is encountered:
$kind = $result[$k][2]; // e.g., 'field', 'data', 'property'
$name = $result[$k][0]; // e.g., 'username'
if ( file_exists ( PAD . "eval/single/$kind.php" ) )
$value = include PAD . 'eval/type/single.php'; // Simple type
else
$value = include PAD . 'eval/type/parms.php'; // Type with parameters
single/)| Type | File | Returns |
|---|---|---|
field |
single/field.php |
padFieldValue($name) |
data |
single/data.php |
$GLOBALS['padDataStore'][$name] |
content |
single/content.php |
$GLOBALS['padContentStore'][$name] |
property |
single/property.php |
padTagValue($name) |
tag |
single/tag.php |
padOptValue($name, 1) |
array |
single/array.php |
padArrayValue($name, TRUE) |
constant |
single/constant.php |
constant($name) |
level |
single/level.php |
padGetLevelArray($name) |
flag |
single/flag.php |
Flag value |
pull |
single/pull.php |
Pulled value |
object |
single/object.php |
Object property |
local |
single/local.php |
Local variable |
include |
single/include.php |
Included content |
parms/)Types that accept parameters:
| Type | File | Description |
|---|---|---|
tag |
parms/tag.php |
Tag as function: padTagAsFunction($name, $value, $parm) |
parm |
parms/parm.php |
Parameter access with type |
pad |
parms/pad.php |
PAD include |
php |
parms/php.php |
PHP include |
app |
parms/app.php |
Application include |
sequence |
parms/sequence.php |
Sequence generator |
fast.php)For simple expressions that are just function names (e.g., trim), the fast path bypasses full parsing:
if ( $GLOBALS['padInfo'] )
include PAD . 'events/functionsFast.php'; // Tracing
return include PAD . "functions/$eval.php"; // Direct function call
eval/
├── eval.php # Main entry point
├── fast.php # Fast path for simple functions
│
├── actions/ # Operator action handlers
│ ├── alone.php # Operator with implicit operands
│ ├── double.php # Binary operator
│ ├── doubleLeft.php # Binary with missing left
│ ├── doubleRight.php # Binary with missing right
│ ├── single.php # Unary operator
│ └── singleRight.php # Unary with right operator
│
├── go/ # Operator execution
│ ├── go.php # Dispatch based on operand types
│ ├── doubleVarVar.php # scalar OP scalar
│ ├── doubleArrVar.php # array OP scalar
│ ├── doubleVarArr.php # scalar OP array
│ ├── doubleArrArr.php # array OP array
│ ├── singleVar.php # OP scalar
│ └── singleArr.php # OP array
│
├── parms/ # Parameterized type handlers
│ ├── app.php # Application include
│ ├── pad.php # PAD include
│ ├── parm.php # Parameter access
│ ├── php.php # PHP include
│ ├── sequence.php # Sequence generator
│ └── tag.php # Tag as function
│
├── single/ # Simple type handlers
│ ├── array.php # Array access
│ ├── constant.php # PHP constant
│ ├── content.php # Content store
│ ├── data.php # Data store
│ ├── field.php # Field/variable
│ ├── flag.php # Flag value
│ ├── include.php # Include content
│ ├── level.php # Level variable
│ ├── local.php # Local variable
│ ├── object.php # Object property
│ ├── parm.php # Parameter value
│ ├── property.php # Property value
│ ├── pull.php # Pulled value
│ └── tag.php # Tag value
│
└── type/ # Type dispatch
├── parms.php # Parameterized type handler
├── single.php # Simple type handler
└── type.php # Type dispatch entry
lib/eval/)lib/eval/
├── after.php # Post-parse type resolution
├── array.php # Array access handling
├── check.php # Validation checks
├── const.php # Operator constants and precedence
├── double.php # Double operand handling
├── eval.php # Evaluation utilities
├── multi.php # Multi-expression handling
├── nextKey.php # Next key finder
├── openClose.php # Parentheses handling
├── operations.php # Main operator processing
├── parse.php # Expression parser
├── pipes.php # Pipe splitting
├── result.php # Result computation
├── types.php # Type utilities
└── value.php # Value handling
Expression: $price * 1.1 | round
Parse: [$price, $] [*, OPR] [1.1, VAL] [|, pipe] [round, other]
After: [123.45, VAL] [*, OPR] [1.1, VAL] [|, pipe] [round, TYPE]
Pipes: Split into [123.45 * 1.1] and [round]
* operator[135.795, VAL]136PAD uses string-based booleans:
'1' or any non-empty string'' (empty string)This allows natural use in string contexts while maintaining logical operations.
Errors are reported via padError():