Last updated: May 2026. Edit this file when milestones change; keep in sync with issue #78, #1044 (North Star 1), #1056 (North Star 2), and the README.

What this project is

php-compiler is a PHP compiler that:

  1. Parses PHP into a control-flow graph (via php-cfg)
  2. Lowers the CFG to internal opcodes (lib/Compiler.php)
  3. Executes on a VM (phpc run, bin/vm.php)
  4. JIT-compiles hot paths to LLVM 9 (lib/JIT.php)
  5. AOT-links whole programs to native binaries (phpc build, bin/compile.php)

Deployed apps can run without Zend PHP at runtime. Development and bootstrap still use system PHP today.

This document is the public development status for the project. Technical contributor docs (inventory, capabilities matrices, CI matrices) stay in the repository docs/ tree and are not mirrored on this site.


How complete is the compiler?

Indicative composite toward a web-capable, self-hosting compiler (not line-count parity with Zend PHP):

Area Progress Summary
Foundation (CI, CLI, Docker) ~88% phpc CLI, local/Docker CI, bootstrap GHA workflow
Language (OOP, types, CFG) ~62% VM/JIT OOP largely works; gaps in typed arrays, ::class, some returns
Stdlib ~55% Large batches of JIT builtins; many functions VM-only
Web AOT (build, deploy) ~65% Project link βœ…; home-route execute βœ…; PATH_INFO / layout chain 🚧
Reference app (MiniWebApp) ~55% VM βœ…; AOT link βœ…; AOT execute partial
Self-host bootstrap ~35% M0 βœ…; M1 🚧; full self-compile M5 ⬜ (#1056)

Overall (indicative): ~45% toward the stated north stars below.

For per-function truth, see the generated capabilities matrix in the repo.


Two north stars

Work is prioritized along two parallel tracks:

1. Web-app north star

Living tracker: #1044 Β· Roadmap: #78 Β· Gate ladder: #472

Vision (definition of done)

Compile and run a normal small PHP web application end-to-end without Zend PHP at runtime:

This is North Star 1. It is orthogonal to North Star 2 (self-host) β€” the compiler compiling its own lib/ tree.

Reference application: 003-MiniWebApp

Canonical app: examples/003-MiniWebApp Β· scaffold: phpc init --profile miniwebapp

Route Method Behavior
/ or /index.php GET Home page (layout + config)
/index.php/hello?name= GET Greeting
/index.php/contact POST Form thank-you
/index.php/api/status GET JSON status
?route=… GET/POST Legacy query dispatch (still supported)

Layout: phpc.json manifest, public/index.php (PATH_INFO + query fallback), src/Router.php, templates/ (runtime includes), assets/style.css.

Progress snapshot

Layer Status Notes
Lint (phpc lint --all) βœ… Class methods, includes, superglobals (#539)
VM serve βœ… phpc serve + PATH_INFO curls (#489)
VM CLI matrix βœ… MiniWebApp*VmCli in ci-fast.sh (#597)
Web shell smoke βœ… examples-web-smoke.sh (#664)
AOT link βœ… phpc build --project (#752)
AOT execute 🚧 partial Home ?route=home βœ… (#764 closed, #1040); hello / contact / PATH_INFO / layout chain still open
AOT HTTP / deploy 003 ⬜ Blocked on execute matrix (#676, #833, #612)

Examples 000–002 and 004 already pass VM + AOT link + AOT execute. 003 is the integration stress test for real web apps.

CI gate ladder (stages 0 β†’ 4d)

Run ./script/miniwebapp-gates.sh or phpc doctor --gates. Details: miniwebapp-gates.md (repo only).

Stage Check Default on master
1 Lint green βœ…
1b VM CLI route matrix βœ…
2 PHPUnit ServeTest @miniwebapp βœ…
3 Examples web-smoke (curl) βœ…
4a AOT dry-run lint probe
4b AOT link βœ…
4b2 AOT execute (PHPUnit) opt-in MINIWEBAPP_AOT_EXECUTE_GATE=1 (#791)
4c examples-aot-smoke 003 slice ❌ (#881)
4d Deploy smoke (003) 001/002 only (#718)

AOT execute bisect ladder (after #764 home fix)

Smallest reproducers first β€” see #78 bisect table:

Step Issue Focus
βœ… #848, #806 isset / require_return
🚧 #878 Nested two-tier includes
🚧 #867 miniwebapp_render_home phpt
🚧 #866 $_SERVER in included layout.php
🚧 #846, #831, #832 Layout partials, contact, private methods
🚧 #849 JSON api/status in class method
🚧 #784, #807 Title-branch partial includes

DevEx: #879 miniwebapp-aot-bisect.sh, #880 @group miniwebapp-bisect.

Verify locally

./phpc lint --all examples/003-MiniWebApp
./phpc serve examples/003-MiniWebApp
cd examples/003-MiniWebApp && ../../phpc build --project .
QUERY_STRING=route=home REQUEST_METHOD=GET ./.phpc/bin/app | wc -c   # expect non-zero
make miniwebapp-gates
MINIWEBAPP_AOT_EXECUTE_GATE=1 ./script/ci-local.sh --filter MiniWebAppAotExecuteTest

What is out of scope (for this north star)

2. Self-host north star

Living tracker: #1056 Β· Roadmap: #78 Β· Process: #1025

Vision (definition of done)

The compiler fully compiles itself β€” the stretch goal behind every bootstrap milestone:

This is North Star 2. It is orthogonal to North Star 1 (web app) β€” user-facing web apps vs. the compiler eating its own lib/ tree.

Not required for M5 close: in-process LLVM linker (lib/AOT/Linker.php may keep external clang); 100% Zend parity; replacing North Star 1.

Where we are today

Zend PHP still runs bin/compile.php during bootstrap. The output is a curated, stub-tolerant native bundle (test/selfhost/compiler_minimal/) β€” not yet a replacement for system PHP. With PHP_COMPILER_SELFHOST_AOT=1, many Compiler / JIT / VM paths use LLVM stubs so the bundle links; SelfHostBuiltinPolicy keeps ~40 stdlib builtins at real lowering and stubs the rest.


Self-host milestone ladder

Milestone Meaning Status
M0 β€” Bundled subset runs ~109 literal require_once units in test/selfhost/compiler_minimal/main.php β†’ build/selfhost prints compiler_minimal bundle OK βœ… #557, #913
M1 β€” Compiler-shaped bundle Bundled Compiler.php AOT lint; compile-smoke native link + AOT echo (compiler smoke); driver smoke toward bin/compile.php 🚧 #1025
M2 β€” Full top-level lib/ All 14 top-level lib/*.php in one honest bundle; bundle grows toward bin/vm.php spine 🚧 lint βœ…; link/run open
M3 β€” Native compiles PHP Self-hosted binary compiles + runs examples/000-HelloWorld without Zend ⬜
M4 β€” Bootstrap loop Native toolchain rebuilds the next compiler sources (same tree, new revision) ⬜
M5 β€” Full self-host Real bin/vm.php / bin/compile.php path on full inventory; no Zend bootstrap ⬜ north star (#1056)

Scale (why M5 is big)

Set ~Files Notes
compiler_minimal bundle today 109 Literal require_once closure
bin/vm.php inventory target 413 Full compiler spine
Top-level lib/*.php 14 Per-file AOT lint βœ…

Bootstrap phases (repo scripts)

Phase Command / doc Status
A β€” Inventory php script/bootstrap-inventory.php --check βœ… ~413 files; 0 source blockers
B β€” AOT lint lib/*.php, test/bootstrap-aot/, selfhost bundles βœ…
C β€” Native fixtures make bootstrap-aot-link βœ…
D β€” lib/ in bundle lib/OpCode.php etc. βœ… #540
E β€” Waves ./script/bootstrap-wave-check.sh 🚧 NEXT_LOWER probes

Automated bootstrap gates

Gate Command Status
Inventory php script/bootstrap-inventory.php --check βœ… M0+
Lib AOT lint php bin/compile.php -l lib/*.php βœ… 14/14 top-level lib/*.php
Bundled compiler lint ./script/bootstrap-selfhost-lint.sh βœ… M0
Native link + run ./script/bootstrap-selfhost-link.sh βœ… M0
Compile smoke link make bootstrap-selfhost-compile-smoke βœ… M1
Compile smoke AOT echo make bootstrap-selfhost-compile-smoke-run βœ… M1 partial
Wave gate ./script/bootstrap-wave-check.sh βœ… CI / GHA
Next includes probe php script/bootstrap-selfhost-next-includes.php 🚧 bundle growth

Verify locally

make bootstrap-wave-check
./script/bootstrap-selfhost-link.sh
make bootstrap-selfhost-compile-smoke-run
php script/bootstrap-selfhost-next-includes.php

Self-host blockers (priority)

Area Issues
Class methods / JIT objects #58, #145, #828
Namespaces + bundle growth #84
Try/catch unwind #57
Parser / vendor strategy php-cfg, php-types, php-llvm β€” M5 approach TBD
External linker lib/AOT/Linker.php excluded from bundle (shell_exec)

Development phases (roadmap #78)

GitHub issues use labels phase-0:Foundation … phase-5:reference-app. Delivery order:

Phase Focus Representative status
0 β€” Foundation CI, phpc CLI, Docker, docs, JIT compliance Largely βœ…
1 β€” Language OOP, types, includes, ::class VM/JIT strong; native AOT gaps remain
2 β€” Stdlib Web builtins, filesystem, JSON, regex Ongoing batches; audit in repo
3 β€” Web AOT phpc build --project, deploy, runtime includes Link βœ…; execute partial (North Star 1)
4 β€” Polish MiniWebApp gates, HTTP smokes, doc sync Active

Current active phase: Phase 4 polish (AOT execute matrix + HTTP smokes) while self-host waves continue in parallel.


Shipped examples (000–004)

Example VM AOT link AOT execute
000–002, 004 βœ… βœ… βœ…
003-MiniWebApp βœ… βœ… 🚧 partial (home βœ…; #676)

Commands: ./phpc run, ./phpc build, ./phpc serve, make examples-aot-smoke (see README).


Compiler pipeline (one paragraph per stage)

  1. Parse & CFG β€” PHP source β†’ PHPCfg; Compiler lowers statements/expressions to OpCodes. Unsupported constructs throw LogicException (β€œnot yet lowered”).
  2. VM β€” Interpreter for dev/tests; full PHP subset not implemented.
  3. JIT β€” LLVM 9 for hot paths and stdlib Internal builtins (ext/standard/, ext/types/).
  4. AOT β€” Whole-program link to a native binary; external clang via lib/AOT/Linker.php (not in self-host bundle yet).

Major open blockers

Issue Area Impact
#676 North Star 1 execute Unskip MiniWebApp PHPUnit + shell smokes
#878–#849 AOT bisect Layout/includes/superglobals in native 003
#828 Self-host JIT Object_.php external property children
#84 Self-host tree Full lib/ bundle growth + namespaces
#57 Self-host runtime Try/catch unwind in native bundle
M3–M5 North Star 2 Native PHP β†’ rebuild compiler β†’ full tree (#1056)

How to contribute

  1. Pick an issue by phase label, #1044 (North Star 1), #1056 (North Star 2), or self-host bootstrap issues.
  2. For self-host work, run ./script/bootstrap-wave-check.sh before opening a PR.
  3. Update this file (docs/pages/development-status.md) when a user-visible milestone lands.

← Back to status overview