>_ENGINEERING REPORT
phplang is a compiled PHP runtime in Rust, hosted on the fusevm bytecode VM and its tracing Cranelift JIT over a PhpHost object heap. This report describes the architecture, the value model, the standard library, the parity fuzzer, the current state of the tree, and the dependency posture. The statements below are facts about the design and the manifest, not aspirational metrics.
Summary
Zend compiles PHP to its own opcodes and walks them in C. phplang takes a different path: it lexes and parses PHP to an AST, lowers that to fusevm bytecode, and runs it on a compiled VM with a tracing Cranelift JIT. Arithmetic lowers to native VM ops so the JIT can trace hot loops; PHP-specific behaviour is served by a PhpHost object heap through a numbered builtin-call protocol and a strict numeric hook. It joins fusevm alongside zshrs, stryke, awkrs, pythonrs, and rubylang, and reuses that shared bytecode VM and JIT rather than shipping its own.
To our knowledge it is the first compiled standalone PHP runtime. The binary is php. The current tree implements the two-mode lexer/parser, AST-to-bytecode lowering, scalars and casts, the full operator and comparison surface, ordered heap arrays, the control-flow constructs (including match), user functions with recursion, and a ~90-function standard library across strings, arrays, math, and type/util. Behaviour is checked against the reference php by a differential parity fuzzer. Classes, closures, references, exceptions, generators, bytecode caching, AOT, LSP, and DAP are planned — see the README for the honest ledger.
Hosting on fusevm
phplang contains no virtual machine or JIT of its own. The execution path is:
PHP source → lexer (two-mode) → parser → AST → lower to fusevm bytecode → fusevm VM + Cranelift JIT
│
PhpHost heap (arrays, builtins, CallBuiltin, numeric hook)
Shared engine
fusevm is pulled from crates.io with the jit feature. JIT and VM improvements land once and benefit zshrs, stryke, awkrs, pythonrs, rubylang, and phplang together.
Native arithmetic
+ - * lower to native fusevm ops; a strict numeric hook (src/builtins.rs) supplies PHP coercion only for the non-numeric operand cases and i64 overflow. / % **, concatenation, and comparison lower to CallBuiltin.
Two-mode lexer
Inline HTML passes through verbatim; <?php … ?> and <?= switch to code mode. Comment forms #, //, /* */ and $var interpolation are handled at the lexer (src/lexer.rs).
Ordered arrays
PHP arrays are heap objects in PhpHost (src/host.rs) behind Value::Obj(u32) handles, backed by an IndexMap so insertion order is observable in foreach/print_r.
Value model
Scalar values — int, float, bool, null, and string — ride through the VM directly as fusevm::Value. Arrays live on the PhpHost object heap behind Value::Obj(u32) handles. PHP arrays are ordered maps: numeric and string keys coexist, and insertion order is preserved via an IndexMap, matching what foreach and print_r observe. Truthiness, loose/strict comparison, and string↔number coercion follow PHP semantics through the host rather than the VM's default numeric rules.
One documented deviation: array cells are currently reference-based on the heap rather than PHP's copy-on-write, so an array assigned to a second variable is aliased, not copied. array_pop/array_shift are omitted pending a host by-reference delete, and a match with no matching arm and no default yields null rather than throwing \UnhandledMatchError. These are tracked in-code and in the README.
Standard library
Around 90 functions are registered as fusevm builtins (src/builtins.rs), reachable by name through the compiler's CallBuiltin lowering:
Strings
str_* family, substr, trim, sprintf, number_format, htmlspecialchars, chr/ord, and more.
Arrays
array_map/filter/reduce/merge/slice/keys/values, the sort family, in_array, array_sum, range.
Math
abs/floor/ceil/round/sqrt, trigonometry, intdiv, fmod.
Type & util
is_*, gettype, json_encode, var_dump, print_r, var_export.
Differential parity fuzzer
Correctness is measured, not asserted. parity-fuzz (src/bin/parity_fuzz.rs) generates seed-deterministic PHP snippets, runs each through both the reference php and phplang, and reports every case where stdout or success/failure diverges. It is a development tool — it needs a reference php on PATH, so CI never runs it; it spawns subprocesses only and never links the library.
Generators are biased toward where a PHP frontend is likely to disagree with the reference: float formatting, integer division/modulo signs, ** precedence, loose-vs-strict comparison, sort ordering, sprintf/number_format, and string↔number coercion. Divergences are delta-debugged to a minimal reproducer and grouped by signature; a full report lands in target/parity-fuzz/divergences.txt.
cargo build --bin parity-fuzz ./target/debug/parity-fuzz --count 5000 # fuzz 5000 cases ./target/debug/parity-fuzz --once --seed 1234 # replay one case, show both sides
Component status
| Component | State | Notes |
|---|---|---|
| two-mode lexer / parser → AST | Implemented | Inline-HTML + <?php>/<?=, comments, $var interpolation (src/lexer.rs, src/parser.rs). |
| AST → fusevm bytecode | Implemented | Native arithmetic + CallBuiltin host dispatch; no local VM (src/compiler.rs). |
| scalars / strings / casts | Implemented | int/float/bool/string/null; interpolation, escapes, all four casts. |
| operators & comparison | Implemented | + - * / % **, ., compound assign, ++/--, loose/strict, logical, ternary/elvis, ??. |
| arrays (indexed / assoc / append) | Implemented | Ordered PhpHost heap; index read/write, $a[] = (src/host.rs). |
| control flow | Implemented | if/elseif/else, while, do…while, for, foreach, switch, break/continue/return, match. |
| user functions | Implemented | Positional parameters, recursion. |
| standard library (~90 fns) | Implemented | String / array / math / type-util families (src/builtins.rs). |
REPL (php -a) & bytecode dump | Implemented | Persistent-state REPL (src/repl.rs); --dump-bytecode prints lowered chunks. |
| parity fuzzer | Implemented | Differential vs reference php, delta-debugged (src/bin/parity_fuzz.rs). Dev tool, never in CI. |
| classes / traits / closures / exceptions | Planned | OO, closures/arrows, references, try/catch, generators, superglobals, default/variadic/typed params, deep lvalues. |
| bytecode cache / AOT / LSP / DAP | Planned | Present in sibling frontends; not wired in phplang yet. |
Dependency posture
Dependencies are kept foundational and durable. Direct dependencies from Cargo.toml:
| Crate | Role |
|---|---|
| fusevm | Language-agnostic bytecode VM + tracing Cranelift JIT (jit feature) |
| clap | CLI argument parsing (derive, cargo) |
| thiserror | Error type derivation |
| rustc-hash | Fast hashing in the frontend |
| indexmap | Insertion-ordered PHP array storage (observable in foreach/print_r) |
| once_cell / libc | Lazy statics; low-level OS glue |
| reedline / nu-ansi-term | Interactive REPL line editor and banner/status colors (php -a) |
Compatibility & longevity
Reference semantics
Behaviour tracks the reference php as the compatibility target, measured byte-for-byte by the parity fuzzer; documented deviations are tracked in-code.
Ordered arrays
Insertion order is preserved for both numeric and string keys, matching what PHP programs observe in iteration and printing.
Cross-architecture
macOS aarch64 and Linux x86_64 / aarch64 via the Cranelift JIT; portable bytecode underneath.
Standalone crate
An explicit empty [workspace] keeps phplang buildable on its own, independent of the meta repo.
Links
- Docs — index.html
- Source — github.com/MenkeTechnologies/phplang
- Issues — github.com/MenkeTechnologies/phplang/issues
- fusevm — github.com/MenkeTechnologies/fusevm (the shared VM)
- License — MIT (LICENSE)