Expression builder for runtime-safe logic
The expression builder lets you describe logic as data that the runtime can evaluate safely.
What thorm() is
thorm() is an expression builder for runtime-safe logic. Instead of calculating values in PHP, you describe the logic as a small tree of operations. The runtime evaluates that tree later, so it can keep UI updates reactive and predictable.
This keeps your logic declarative. The tree is serializable, safe to run in the browser, and deterministic in both client and server rendering.
Who this is for
This page is for anyone, but a little PHP knowledge helps because the IR is authored in PHP. If you know basic JavaScript, the runtime model will click faster because the expressions are evaluated in the browser after hydration.
If either of those is new, you can still follow along, but expect to pause and look up a few fundamentals.
Mental model: build a tree, not a value
thorm() does not execute logic in PHP. It builds an expression tree that is stored in the IR. The runtime evaluates that tree when it needs to render or update UI.
This is why thorm() exists: it lets you write logic once, then run it safely in a controlled runtime, with dependency tracking and automatic updates.
Quick start
Here is a small, complete example. The math is authored in thorm(), and the UI reads the expression directly. The runtime evaluates the tree and updates the text automatically.
Functions used here: state(), read(), thorm(), el(), text(), concat().
You can mix thorm() with standard expression helpers like concat() or cond(). The key is that the actual logic lives in the expression tree, not in PHP.
Full ops list
thorm() accepts a small, explicit set of operations. This whitelist is what keeps the runtime safe and deterministic.
Aliases: +, -, *, /, %, ==, >, <, >=, <=, mulx, divx are accepted and mapped to add/sub/mul/div/mod/eq/gt/lt/gte/lte.
- Math: add, sub, mul, div, mod, abs, min, max, round, floor, ceil, sqrt, pow, trunc, sign, log, log10, log2, exp
- Comparisons: eq, gt, lt, gte, lte
- Flow: cond, get
- Strings: strlen, substr, strpos, str_replace, strtolower, strtoupper, trim, ltrim, rtrim, explode, implode
- Arrays: in_array, count, array_keys, array_values
- Types: parseInt, parseFloat, intval, floatval, boolval, strval, is_numeric, is_string, is_array
- Utility: json_encode
- Explicit nodes: val, read
Anything outside this list is intentionally disallowed. That is part of the safety guarantee.
Classic expression style still works
If you already prefer the nested helper style, keep using it. thorm() is optional and designed to make complex trees easier to read, but the classic approach remains valid.
You can mix both styles in the same project, so pick the one that keeps your code clearest.
Safety and determinism
thorm() is designed to be pure and deterministic. That means no randomness, no time access, no DOM access, and no external I/O. Expressions only transform the inputs you give them.
If you need side effects, use actions or runtime capabilities instead. This separation makes it easier to reason about what can and cannot happen during rendering.
Best practices
- Keep expressions small and readable; split complex math into smaller trees.
- Use read($atom) directly in thorm() to keep dependencies explicit.
- Use cond() or thorm(['cond', ...]) for safe branching instead of PHP conditionals.
- Prefer derived expressions over storing computed values in state.
Example: split complex math into smaller chunks.
Bad: one long expression
Good: named steps that explain intent
Example: derived expressions vs stored computed values.
Bad: store computed values in state
Good: derive it once and always correct
These habits keep your UI predictable and make reactivity easier to debug.
SSR note
The same expression tree can be evaluated on the server or in the browser. SSR uses the initial atom values, while the client continues evaluating the same tree after hydration.
If a value is not available on the server, you can still render a fallback and let the client take over after mount.
What is on the horizon
Future work may extend thorm() with list operators (map/filter/reduce) and lambda nodes for algorithmic use cases. These are not implemented yet and are intentionally kept out of the current runtime for now.
When they arrive, they will follow the same safety-first approach: a small whitelist, deterministic evaluation, and full serialization.