v1.0 stable 15 Jun 2026 LLVM 9

A compiler for PHP in PHP that compiles itself

php-compiler is a CFG-based compiler with a bytecode VM, LLVM 9 JIT, and AOT linking. Ship CLI tools and small web apps that run without Zend PHP at runtime after phpc build or phpc deploy. The north star is the compiler rebuilding its own lib/ tree — today you can reproduce M0 link (compiler_minimal bundle OK), the M4 bootstrap loop (make bootstrap-loop-probe), and M5 presenter (make north-star5-verify-fast) on a clean clone with Docker and LLVM 9.

Stable line (2026). First maintained stable release: demo-ready VM + AOT for a web-capable PHP subset, examples 000–009, and an experimental self-host path. Not full Zend PHP — see what’s missing.
VM + JIT + AOT Closures · try/catch 321 builtins Spine **4287** / **4287** M5 strict ✅ M3 strict emit MCJIT execute
quick-start.sh
git clone https://github.com/PurHur/php-compiler.git
cd php-compiler
composer install
./phpc test --fast
./phpc build -o /tmp/hello examples/000-HelloWorld/example.php
./phpc serve examples/003-MiniWebApp

Lineage & disclaimer

This project continues a research compiler originally written in PHP. Credit the idea to the original author; this fork is a separate continuation under MIT.

Original project ircmaxell/php-compiler
Original author Anthony Ferrara (ircmaxell) — MIT © 2019
This fork PurHur/php-compiler — LLVM JIT/AOT, phpc CLI, web examples, self-host ladder

The concept of a PHP-in-PHP compiler with VM, JIT, and native compilation comes from Anthony Ferrara’s work. It is not an official release from the original author unless stated otherwise.

What is php-compiler?

Most PHP runs on Zend (opcode VM in C). php-compiler parses with php-cfg, lowers to internal opcodes, then executes via one of three backends.

VM

phpc run · bin/vm.php

Reference interpreter — broadest coverage, slowest. Default for dev and compliance.

JIT

bin/jit.php

LLVM MCJIT — compile at startup, run native code. Some CFGs fall back to VM.

AOT

phpc build · bin/compile.php

Standalone executable — no PHP installed on the server at runtime.

Targets a deliberate PHP 8 subset for CLI and CGI-style web apps (superglobals, routing, templates, sessions, uploads, JSON). Examples under examples/ are integration test fixtures, not a separate product. Active research: self-host — the compiler compiling its own lib/ without Zend in the bootstrap loop.

Try it in five minutes

Needs PHP 8.1+, Composer. LLVM 9 only for build, JIT, and full CI (not for phpc test --fast). Full walkthrough: GETTING-STARTED.md.

1

Clone & smoke

git clone https://github.com/PurHur/php-compiler.git
cd php-compiler
composer install
./phpc test --fast
2

Native binary

./phpc build -o /tmp/hello \
  examples/000-HelloWorld/example.php
/tmp/hello

Standalone executable — no php at runtime.

3

Web app (VM)

./phpc serve examples/003-MiniWebApp

Open http://127.0.0.1:8080/ — router, templates, JSON API.

4

Self-host smoke (M0)

make docker-build-22   # once
./script/docker-exec.sh -- bash -lc \
  'composer install --ignore-platform-reqs -q \
   && script/apply-patches.sh \
   && make bootstrap-selfhost-link'
./build/selfhost

Expect compiler_minimal bundle OK — native link of a minimal lib/ bundle (experimental). Needs LLVM 9.

5

North star (M4 + M5)

make bootstrap-loop-probe
make north-star5-verify-fast
# pre-merge bootstrap only (~1h):
./script/north-star5-verify.sh --strict

Full bootstrap ladder (gen-1→gen-2→gen-3 + full-revision) and M5 presenter — heavy; see GETTING-STARTED §7.

Subset compiler — not full Zend PHP. Use phpc lint before shipping your own code.

The phpc CLI

Unified developer interface (./phpcbin/phpc.php). Legacy bin/vm.php, bin/jit.php, and bin/compile.php still work.

Command Purpose
phpc runRun on the VM; -q / -p for CGI-style superglobals
phpc serveDev HTTP server (VM); phpc serve --aot for a prebuilt binary
phpc buildAOT compile; --project uses phpc.json
phpc deployPackage binary + public/ into a deploy tree
phpc lintReport unsupported syntax in a file or tree
phpc testCI locally; --fast = VM/compliance only (no LLVM)
phpc initScaffold phpc.json (miniwebapp, sessionsweb, fileupload profiles)
phpc doctorEnvironment and example gate probes

phpc.json reference · AOT deploy guide

Example applications

Reference apps in examples/ prove VM, AOT link, native execute, and deploy paths. examples/README.md

Example Highlights
000–002CLI hello, simple web, CGI query params
003-MiniWebAppRouter, templates, contact form, JSON API — AOT execute green
004-ApiJsonJSON API
005-SessionsWebsession_start, flash messages
006-FileUploadWebMultipart $_FILES
007-ThrowsWebCaught exceptions in forms
008-SelfHostProbeSelf-host presenter probe
009-FastCGIWebFastCGI-oriented layout
make web-smoke          # lint + VM smoke
make examples-aot-smoke # AOT link + execute (LLVM required)
make examples-web-smoke # phpc serve + HTTP curls

Capabilities & limitations

Not a drop-in Zend replacement. Capabilities differ by backend — VM is broadest; AOT is strictest. Full PHP vs us comparison · Gap tables.

Column Meaning
VMphpc run — broadest language coverage
JITbin/jit.php — native MCJIT; some CFGs use VM fallback
AOTphpc build — standalone binary; many constructs blocked at link

v1.0 supports well

  • Classes, interfaces, match, namespaces, parent::class
  • Closures and fn () => — VM + JIT IR (use() value and ref)
  • try / catch / finally — VM compliance; JIT IR verified
  • Generators — VM; JIT/AOT VM fallback
  • CGI superglobals, sessions, uploads, phpc deploy
  • 321 builtins + closure array_map callbacks on VM

Known gaps (summary)

  • bin/jit.php MCJIT execute — SIGSEGV in probe (#98)
  • JIT try/catch scripts — VM fallback (#2114)
  • Generator / enum AOT native lowering open
  • Self-host bootstrap may stub closures to null
  • Not Laravel, Symfony, WordPress, or arbitrary Composer stacks
Area VM JIT / AOT
Try / catch / finally Yes IR only; execute → VM
Closures / arrow functions Yes JIT IR
Generators (yield) Yes VM fallback
Enums in AOT VM No
M3 inventory emit (HelloWorld) Native Native
M4 bootstrap loop (bootstrap-loop-probe) Native
M5 presenter (north-star5-verify-fast) Strict ✅

Full gap tables → PHP capability comparison →

Self-host progress

Toward full self-host (M5) 100%

fix/spine-aot-jit-blockers @ a8cffaa0e (15 Jun 2026, LLVM 9). M2 spine SSOT: php script/bootstrap-spine-count.php**4287** / **4287** Phase A inventory (literal coverage ✅). M4: make bootstrap-loop-probe full ladder ✅. M5: make north-star5-verify-fast ✅ daily; --strict pre-merge; VM driver probe ~20ms in CI loops. Remaining: MCJIT execute ([#98](https://github.com/PurHur/php-compiler/issues/98)); honest PHP main() in full spine AOT (native bundle-OK probe is bootstrap smoke). Compiled-only cold boot on empty build/: BOOTSTRAP_M5_NO_ZEND=1 make bootstrap-selfhost-link ✅ ([#3053](https://github.com/PurHur/php-compiler/issues/3053)).

Self-host milestones

Experimental path: compiler compiling its own lib/ tree. Tracker #1492. Short status page.

  1. M0–M1

    Minimal + compiler-shaped bundles; compile-smoke AOT echo.

  2. M2

    Spine **4287** / **4287** Phase A inventory — literal coverage ✅; native link + lint ✅ (#8559).

  3. M3

    HelloWorld + inventory argv driver strict emit_path=native ✅; production bin/compile.php inventory path on gen-2 driver ✅ (#3024 closed).

  4. M4

    make bootstrap-loop-probe full ladder ✅ — gen-1→gen-2 native, gen-2→gen-3 full-spine recompile, full-revision argv probe (#1498, #2880).

  5. M5

    Vendor prelink 3/3 ✅; committed .o cold boot ✅; north-star5-verify-fast ✅ daily; --strict pre-merge; spine **4287** / **4287**; prelinked gen-0 refreshed; fast VM execute probe (~20ms); compiled-only cold boot via BOOTSTRAP_M5_NO_ZEND=1 ✅ (#3053, #8559, #1492).

Installation

Host (daily dev)

PHP 8.1+ (8.2 recommended), Composer, LLVM 9 for JIT/AOT (bundled via script/install-llvm9.sh).

composer install
script/apply-patches.sh
./script/install-llvm9.sh  # optional until build/CI

Docker only

Image php-compiler:22.04-dev — full CI without host PHP/LLVM.

make docker-build-22
make test

Harness hosts: make test-harness or ./script/docker-ci-local.sh.

Get involved

We do not accept drive-by GitHub issues or pull requests without prior coordination. Contact maintainers on other channels first; align with the AI-agent workflow before investing time here. Forks are welcome under MIT.