// PHPLANG — ENGINEERING REPORT

phplang v0.1.0 · PHP on fusevm · lex/parse → AST → bytecode → tracing Cranelift JIT · PhpHost object heap · first compiled standalone PHP runtime · MIT · in active development

Docs GitHub
// Color scheme

>_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.

PHP
fusevm language host
~90
stdlib functions
v0.1.0
version
MIT
license · free / OSS

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

ComponentStateNotes
two-mode lexer / parser → ASTImplementedInline-HTML + <?php>/<?=, comments, $var interpolation (src/lexer.rs, src/parser.rs).
AST → fusevm bytecodeImplementedNative arithmetic + CallBuiltin host dispatch; no local VM (src/compiler.rs).
scalars / strings / castsImplementedint/float/bool/string/null; interpolation, escapes, all four casts.
operators & comparisonImplemented+ - * / % **, ., compound assign, ++/--, loose/strict, logical, ternary/elvis, ??.
arrays (indexed / assoc / append)ImplementedOrdered PhpHost heap; index read/write, $a[] = (src/host.rs).
control flowImplementedif/elseif/else, while, do…while, for, foreach, switch, break/continue/return, match.
user functionsImplementedPositional parameters, recursion.
standard library (~90 fns)ImplementedString / array / math / type-util families (src/builtins.rs).
REPL (php -a) & bytecode dumpImplementedPersistent-state REPL (src/repl.rs); --dump-bytecode prints lowered chunks.
parity fuzzerImplementedDifferential vs reference php, delta-debugged (src/bin/parity_fuzz.rs). Dev tool, never in CI.
classes / traits / closures / exceptionsPlannedOO, closures/arrows, references, try/catch, generators, superglobals, default/variadic/typed params, deep lvalues.
bytecode cache / AOT / LSP / DAPPlannedPresent in sibling frontends; not wired in phplang yet.

Dependency posture

Dependencies are kept foundational and durable. Direct dependencies from Cargo.toml:

CrateRole
fusevmLanguage-agnostic bytecode VM + tracing Cranelift JIT (jit feature)
clapCLI argument parsing (derive, cargo)
thiserrorError type derivation
rustc-hashFast hashing in the frontend
indexmapInsertion-ordered PHP array storage (observable in foreach/print_r)
once_cell / libcLazy statics; low-level OS glue
reedline / nu-ansi-termInteractive 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