FunctionRegistry. Once registered, they are available everywhere — both in the fluent API and in FQL string queries — and dispatch goes through the same expression evaluator as the built-ins.
Function contracts
Each class is a static-only container — no constructor, no
__invoke. A scalar function exposes name() and a static execute(...). An aggregate function exposes name() plus static initial(), accumulate(...), and finalize(...) methods.
Example: scalar function
The following example appends a_custom suffix to any string value.
Example: aggregate function
The following example computes a population standard deviation across a group.Registering your function
Register the class once at application bootstrap — typically right after the Composer autoloader. The registry is process-global and bootstraps fromsrc/Functions/functions.neon for built-ins.
If you try to register a class that is missing one of the contracts, the registry throws
FQL\Exception\FunctionRegistrationException. Calling an unknown function at runtime throws FQL\Exception\UnknownFunctionException.
Using a registered function
Once registered, the function is available by name in both APIs.FQL string syntax
Fluent API
The fluent helpers parse SQL expression strings, so you can call your custom function inline anywhere a built-in works:Custom functions are dispatched through
Sql\Provider::parseExpression() together with built-ins. The same code path powers both the fluent helpers and the FQL string parser, so a registered function behaves identically in either form.