Evaluator
in package
CRUDUI expression evaluator (expressions.md §5/§6/§7, JS PathResolver parity).
The shared 4-language fixture (tests/fixtures/expr/cases.json) is generated from the JS reference engine and is the §9 contract all four engines pass, so this evaluator mirrors validator-ts PathResolver function-for-function:
- evaluate(node) → boolean (JS evaluateCondition): Boolean of the resolved value for Path/Literal; logical short-circuit; comparison; in; unary; a ternary is Boolean(its branch value).
- evaluateValue(node) → value (JS evaluateExpressionValue): ternary returns its branch value; EVERYTHING else returns the boolean condition result.
truthy (JS Boolean): null/false/0/"" falsy; "0", "false", [], }, and every non-empty value truthy. == / != use JS loose equality (same-type ===, else numeric when both Number()-parse, else string). > >= < <= coerce BOTH sides to number (parseFloat, NaN→0) and compare numerically — there is no lexicographic branch (JS compare parity).
No eval / no regex evaluation (GRAMMAR §10) — pure AST walk.
Table of Contents
Properties
- $currentPath : array<string|int, mixed>
- $formData : array<string|int, mixed>|stdClass
- $rowKeys : array<string|int, mixed>
Methods
- __construct() : mixed
- coerceNumber() : float
- Coerce a value to a number for comparison (JS coerceNumber): numbers pass through; a string is parseFloat-ed (NaN→0); booleans → 1/0; else 0.
- evaluate() : bool
- Evaluate to a boolean condition result (JS evaluateCondition).
- evaluateValue() : mixed
- Evaluate to a value (JS evaluateExpressionValue). A ternary returns its chosen branch's RAW value; every other node returns its boolean condition result (the shared fixture's `value` for non-ternary mirrors `truthy`).
- isTruthy() : bool
- Truthy test (JS Boolean). null/false/0/0.0/"" are falsy; "0", "false", empty array, empty object, and every non-empty value are truthy.
- looseEquals() : bool
- Loose equality (JS looseEquals). Values of one JS type compare as JSON values (jsonEquals); null equals only null; otherwise numeric equality when both Number()-parse, else string equality.
- reference() : mixed
- The value of a field reference of a rule parameter (`equalTo`, `notEqual`, `enddate`): a relative path resolves as a relative expression path, a dotted name from the data root, and a bare name beside the current field.
- compareOp() : bool
- evaluateBinary() : bool
- evaluateIn() : bool
- evaluateTernary() : mixed
- evaluateUnary() : bool
- getValueByPath() : mixed
- Read the value at a concrete path (JS getValueByPath). A missing key yields null. Numeric segments index into list arrays.
- hasWildcard() : bool
- indexOfWildcard() : int
- inList() : bool
- isNumericSegment() : bool
- jsNumber() : array{0: float, 1: bool}
- JS Number(value) for equality: numbers pass; a string is numeric only when the WHOLE string parses (JS Number('12abc') is NaN); booleans → 1/0.
- jsonEquals() : bool
- JSON-value structural equality (docs/spec/expressions.md, Evaluation): two lists hold equal elements in the same order, two objects the same keys with equal values in any key order; other values compare without conversion, numbers by value (int 1 equals float 1.0). A list never equals an object.
- jsTypeOf() : string
- parseFloatPrefix() : array{0: float, 1: bool}
- parseFloat-style leading-number parse (JS parseFloat): reads an optional sign, digits, fraction, exponent prefix; returns failure when no number leads the string.
- replaceWildcardWithIndex() : array<int, string>
- Replace each wildcard with the corresponding array index from currentPath, in order (JS replaceWildcardWithIndex). Wildcards with no matching index stay as '*' for later list resolution.
- resolveOperand() : array{0: mixed, 1: bool}
- Resolve the left operand of a comparison: the list of candidate values of a wildcard path with true, or the one value of any other node with false.
- resolvePath() : mixed
- resolvePathOperand() : array{0: mixed, 1: bool}
- The value of a path with false, or the list of candidate values of a wildcard that the current path does not resolve with true.
- resolvePathSegments() : array<int, string>
- Compute the absolute path segments for a Path node (JS resolvePathSegments).
- resolveTernaryBranchValue() : mixed
- Resolve a ternary branch to its RAW value (JS resolveTernaryBranchValue): nested ternary recurses; literal/path return their value; logical/in/ comparison/unary return their boolean.
- resolveValue() : mixed
- Resolve a node to its raw value (JS resolveValue): Literal → its value; Path → the data at the resolved path (wildcards handled); Group → the inner condition's boolean.
-
resolveWildcardPath()
: array<int, array{path: list
, value: mixed}> - Resolve a wildcard path to all concrete {path,value} pairs (JS resolveWildcardPath, ANY strategy across the array; object source skips the wildcard index per multiple:'only').
- sameJsType() : bool
- Whether two values share the same JS typeof bucket (number covers int and float; arrays/objects are 'object').
- toString() : string
Properties
$currentPath read-only
private
array<string|int, mixed>
$currentPath
= []
$formData read-only
private
array<string|int, mixed>|stdClass
$formData
$rowKeys read-only
private
array<string|int, mixed>
$rowKeys
= []
Methods
__construct()
public
__construct(array<string, mixed> $formData[, array<int, string> $currentPath = [] ][, array<int, int> $rowKeys = [] ]) : mixed
Parameters
- $formData : array<string, mixed>
-
full form data tree (assoc arrays)
- $currentPath : array<int, string> = []
-
path of the field carrying the condition, including the field name itself
- $rowKeys : array<int, int> = []
-
positions of the row keys in $currentPath
coerceNumber()
Coerce a value to a number for comparison (JS coerceNumber): numbers pass through; a string is parseFloat-ed (NaN→0); booleans → 1/0; else 0.
public
static coerceNumber(mixed $value) : float
Parameters
- $value : mixed
Return values
floatevaluate()
Evaluate to a boolean condition result (JS evaluateCondition).
public
evaluate(Node $node) : bool
Parameters
- $node : Node
Return values
boolevaluateValue()
Evaluate to a value (JS evaluateExpressionValue). A ternary returns its chosen branch's RAW value; every other node returns its boolean condition result (the shared fixture's `value` for non-ternary mirrors `truthy`).
public
evaluateValue(Node $node) : mixed
Parameters
- $node : Node
isTruthy()
Truthy test (JS Boolean). null/false/0/0.0/"" are falsy; "0", "false", empty array, empty object, and every non-empty value are truthy.
public
static isTruthy(mixed $value) : bool
Parameters
- $value : mixed
Return values
boollooseEquals()
Loose equality (JS looseEquals). Values of one JS type compare as JSON values (jsonEquals); null equals only null; otherwise numeric equality when both Number()-parse, else string equality.
public
static looseEquals(mixed $a, mixed $b) : bool
Parameters
- $a : mixed
- $b : mixed
Return values
boolreference()
The value of a field reference of a rule parameter (`equalTo`, `notEqual`, `enddate`): a relative path resolves as a relative expression path, a dotted name from the data root, and a bare name beside the current field.
public
reference(string $expression) : mixed
Parameters
- $expression : string
compareOp()
private
static compareOp(mixed $left, mixed $right, string $operator) : bool
Parameters
- $left : mixed
- $right : mixed
- $operator : string
Return values
boolevaluateBinary()
private
evaluateBinary(BinaryNode $node) : bool
Parameters
- $node : BinaryNode
Return values
boolevaluateIn()
private
evaluateIn(InNode $node) : bool
Parameters
- $node : InNode
Return values
boolevaluateTernary()
private
evaluateTernary(TernaryNode $node) : mixed
Parameters
- $node : TernaryNode
evaluateUnary()
private
evaluateUnary(UnaryNode $node) : bool
Parameters
- $node : UnaryNode
Return values
boolgetValueByPath()
Read the value at a concrete path (JS getValueByPath). A missing key yields null. Numeric segments index into list arrays.
private
getValueByPath(array<int, string> $path) : mixed
Parameters
- $path : array<int, string>
hasWildcard()
private
static hasWildcard(array<int, string> $path) : bool
Parameters
- $path : array<int, string>
Return values
boolindexOfWildcard()
private
static indexOfWildcard(array<int, string> $path) : int
Parameters
- $path : array<int, string>
Return values
intinList()
private
static inList(mixed $value, array<int, mixed> $list) : bool
Parameters
- $value : mixed
- $list : array<int, mixed>
Return values
boolisNumericSegment()
private
static isNumericSegment(string $segment) : bool
Parameters
- $segment : string
Return values
booljsNumber()
JS Number(value) for equality: numbers pass; a string is numeric only when the WHOLE string parses (JS Number('12abc') is NaN); booleans → 1/0.
private
static jsNumber(mixed $value) : array{0: float, 1: bool}
Parameters
- $value : mixed
Return values
array{0: float, 1: bool}jsonEquals()
JSON-value structural equality (docs/spec/expressions.md, Evaluation): two lists hold equal elements in the same order, two objects the same keys with equal values in any key order; other values compare without conversion, numbers by value (int 1 equals float 1.0). A list never equals an object.
private
static jsonEquals(mixed $a, mixed $b) : bool
Parameters
- $a : mixed
- $b : mixed
Return values
booljsTypeOf()
private
static jsTypeOf(mixed $v) : string
Parameters
- $v : mixed
Return values
stringparseFloatPrefix()
parseFloat-style leading-number parse (JS parseFloat): reads an optional sign, digits, fraction, exponent prefix; returns failure when no number leads the string.
private
static parseFloatPrefix(string $value) : array{0: float, 1: bool}
Parameters
- $value : string
Return values
array{0: float, 1: bool}replaceWildcardWithIndex()
Replace each wildcard with the corresponding array index from currentPath, in order (JS replaceWildcardWithIndex). Wildcards with no matching index stay as '*' for later list resolution.
private
static replaceWildcardWithIndex(array<int, string> $path, array<int, string> $currentPath) : array<int, string>
Parameters
- $path : array<int, string>
- $currentPath : array<int, string>
Return values
array<int, string>resolveOperand()
Resolve the left operand of a comparison: the list of candidate values of a wildcard path with true, or the one value of any other node with false.
private
resolveOperand(Node $node) : array{0: mixed, 1: bool}
Parameters
- $node : Node
Return values
array{0: mixed, 1: bool}resolvePath()
private
resolvePath(PathNode $node) : mixed
Parameters
- $node : PathNode
resolvePathOperand()
The value of a path with false, or the list of candidate values of a wildcard that the current path does not resolve with true.
private
resolvePathOperand(PathNode $node) : array{0: mixed, 1: bool}
Parameters
- $node : PathNode
Return values
array{0: mixed, 1: bool}resolvePathSegments()
Compute the absolute path segments for a Path node (JS resolvePathSegments).
private
resolvePathSegments(PathNode $node) : array<int, string>
Parameters
- $node : PathNode
Return values
array<int, string>resolveTernaryBranchValue()
Resolve a ternary branch to its RAW value (JS resolveTernaryBranchValue): nested ternary recurses; literal/path return their value; logical/in/ comparison/unary return their boolean.
private
resolveTernaryBranchValue(Node $node) : mixed
Parameters
- $node : Node
resolveValue()
Resolve a node to its raw value (JS resolveValue): Literal → its value; Path → the data at the resolved path (wildcards handled); Group → the inner condition's boolean.
private
resolveValue(Node $node) : mixed
Parameters
- $node : Node
resolveWildcardPath()
Resolve a wildcard path to all concrete {path,value} pairs (JS resolveWildcardPath, ANY strategy across the array; object source skips the wildcard index per multiple:'only').
private
resolveWildcardPath(array<int, string> $path) : array<int, array{path: list, value: mixed}>
Parameters
- $path : array<int, string>
Return values
array<int, array{path: listsameJsType()
Whether two values share the same JS typeof bucket (number covers int and float; arrays/objects are 'object').
private
static sameJsType(mixed $a, mixed $b) : bool
Parameters
- $a : mixed
- $b : mixed
Return values
booltoString()
private
static toString(mixed $value) : string
Parameters
- $value : mixed