I’ve been using PHP forever, and so I’ve relied on the Zend Engine. After all, it is the core of PHP. The PHP source goes into the Zend Engine, gets parsed into an AST, compiled to opcodes, and executed. One request, one process. That’s just what php does when you run it, and it’s been true in its broad shape since Zend Engine 2 shipped with PHP 5 in 2004. Set in the PHP era, it is roughly the end of antiquity.
Nowadays, as in October 2026, it is no longer the only way to get from a .php file to a run program. Over the last fifteen years, and especially the last three, a whole ecosystem has grown up around the idea that “run this PHP script” doesn’t have to mean “hand it to the C interpreter Rasmus Lerdorf’s successors maintain”. Some projects replace the engine outright. Some keep the real one and put it somewhere unexpected. Some don’t touch it at all and just change what it’s running inside of. They depart from the canonical pipeline at genuinely different points, which turns out to be the more useful way to sort them than by vendor or by benchmark.
None of this is entirely new, either. JPHP has been compiling PHP straight to JVM bytecode since the early 2010s, and picked up 1.7k GitHub stars doing it: sadly, its last real target is PHP 7.1, with only pieces of 7.2–7.4 layered on. It is so old that upgrading to it would be error prone in most 2026 codebases. We won’t include it in this timeline of PHP engines, but acknowledge the tremendous courage it needed, back in these ancient times. The projects that follow are the ones still tracking a current PHP version. This means PHP 8.5, because PHP 8.6 is still a month away.
The panorama
| Project | What it replaces | Departs the pipeline at | Output |
|---|---|---|---|
| Zend Engine | nothing, this is the baseline | – | opcodes, interpreted/JIT’d per request |
| KPHP | the compiler and the runtime | before parsing | native binary, via generated C++ |
| TypePHP | the compiler and the runtime | before parsing | native binary, extension, or shared lib |
| elephc | the compiler and the runtime | before parsing | native binary, assembly directly, no C++ step |
| Manticore | the compiler and the runtime | before parsing | native binary, via LLVM IR |
| Quercus | the compiler and the runtime | before parsing | JVM bytecode |
| PeachPie | the compiler and the runtime | before parsing | .NET IL |
| php.rs | the compiler and the runtime | before parsing | its own Rust-native VM |
| phpr | the compiler and the runtime | before parsing | its own Rust-native bytecode VM |
| zphp | the compiler and the runtime | before parsing | its own Zig-native runtime |
| php-wasm / WordPress Playground | nothing, recompiles the real engine | after the engine is built | the same engine, running in WASM |
| FrankenPHP | nothing, links the real engine | after the engine is built | the same engine, embedded in a Go process |
| RoadRunner | nothing, supervises the real engine | outside the pipeline entirely | long-lived worker processes, not one-shot |
HHVM, one historical ancestor of the “replace the compiler” family, is covered separately below. It no longer belongs on this table, for reasons that are themselves instructive.
The canonical path, briefly
It is worth starting precisely, since everything else in this blog is defined relative to it. php script.php does four things: lex and parse the source into an AST, compile the AST to Zend opcodes, hand those opcodes to the Zend VM, which either interprets them one at a time or, for code the PHP 8.0 JIT decides is worth it, compiles them to machine code on the fly. One process, one request, memory cleaned down at the end. Every alternative below is a departure from some part of that sequence, never all of it invented from nothing; even the projects that discard the C source still have to agree on what array_merge() does.
Replace the compiler and its target
The first one is the most drastic move: write your own front end for PHP syntax, and instead of Zend opcodes, emit something else entirely. Everything in this family shares one constraint: none of them implements all of PHP, because doing so is the whole difficulty of the exercise, and each one draws the line differently.
Straight to native code
KPHP, built at VK.com and open-sourced in 2020, compiles a restricted subset of PHP to C++, then to a native binary that serves HTTP itself: it has no interpreter, no opcode cache, no “slow startup” phase, because there’s no startup to be slow.
TypePHP does the same C++ round-trip but is, charmingly, self-hosting: its own compiler is PHP code, compiled by an earlier version of itself into the tpc binary you run. It’ll also emit a loadable extension or a shared library from the same source, if a full standalone binary is more than you need. It’s GPL-3 licensed, and its current Packagist listing wants PHP 8.4 or 8.5 to build.
elephc skips the C++ intermediate step and goes straight to assembly, that is parse, type-check, lower to native code, assemble, link, with eval() available through an experimental bridge to a statically linked interpreter, for the one thing you genuinely can’t resolve at compile time. The current release, 0.26.2, targets macOS and Linux.
Manticore is a PHP AOT compiler written in PHP, with an LLVM backend: it lowers a large subset of PHP 8.5+ through its own typed intermediate representation to LLVM IR, and produces standalone arm64 or x86_64 binaries that need no interpreter and no php.ini. The compiler compiles itself to a byte-identical fixpoint, and its README reports native binaries beating the Zend interpreter in all 47 comparable benchmark cases. Those are claims worth checking yourself, and the project’s own limitations list, such as integer overflow wraps instead of promoting to float, no extract(), shows exactly where the feature coverage stops.
# TypePHP: same source, three different outputs
tpc build app.php -o app # standalone native binary
tpc build app.php --ext -o app.so # loadable PHP extension
tpc build app.php --lib -o libapp # shared library, callable from C
Straight to someone else’s VM
Quercus, Caucho’s PHP-on-Java implementation from the Resin application server, compiles PHP source to Java source and lets javac finish the job. PHP scripts becoming .class files that run in whatever JVM your Java stack already has.
PeachPie does the .NET equivalent: PHP compiled through Roslyn to CLR IL, JIT’d by the .NET runtime, with the payoff that a PeachPie-compiled Composer package can be new‘d directly from C#.
And then there’s the newest and most crowded corner of this family: reimplementing PHP wholesale outside C entirely, mostly in Rust but not only. php.rs bills itself as the complete interpreter, compiler, and standard library rebuilt from zero, over 100,000 lines in.
phpr takes the more conservative VM-architecture route: parser, IR, bytecode compiler, dispatch loop. It measures itself against something genuinely hard to fake: whether it can install and serve WordPress.
zphp is the same wholesale-reimplementation bet made in Zig instead: a from-scratch PHP 8.x runtime with its own HTTP server, database drivers, and a --compile flag for standalone binaries. It is still tiny next to the Rust entrants, with single-digit GitHub stars, and openly built as an AI-maintained experiment rather than a community project. The commit history never sleeps, but it also never argues on the mailing list. Indeed, it is attacking exactly the same problem.
There’s enough activity here that “reimplement PHP outside the Zend C source” has quietly become its own genre, alongside smaller entrants like phprs and phpl that haven’t attempted anything close to full-language coverage.
The ancestor of this whole family is Facebook’s HipHop
It was released in 2010: PHP compiled to C++, then to a binary, on the theory that facebook.com’s PHP was CPU-bound and interpretation was the tax. It worked, roughly double the throughput, until it didn’t scale further, so in 2013 Facebook replaced it with HHVM, a JIT bytecode VM that eventually beat HipHop’s own numbers. The instructive part isn’t the speedup, it’s what happened next: by 2018, HHVM’s maintainers concluded that chasing PHP compatibility was slowing down the language features they actually wanted, and cut the cord. Hack today is PHP’s syntax with the serial numbers filed off, running on a VM that no longer promises to run your Laravel app. Every project in this section is implicitly betting it won’t end up there, a bet HHVM itself already lost.
Keep the real Zend engine, change its house
This section is for a breed of engines with a different style: don’t write a new front end at all. Take the actual Zend Engine C source, the one that ships with PHP, and put it somewhere it wasn’t designed to run initially. Embedding is the name of the game.
php-wasm and the WordPress Playground project both do this via Emscripten: compile libphp itself to WebAssembly, and you get PHP running client-side in a browser tab, sandboxed, with SQLite standing in for a filesystem. It’s the reason you can now try WordPress at wordpress.org without a server existing anywhere: the “server” is a WASM module your browser downloaded. Playground now ships a separate php-wasm build for each PHP version from 7.2 to 8.5, and it has dropped support for 7.2 and 7.3.
FrankenPHP does the embedding move instead of the cross-compilation move: it links libphp into a Go binary via cgo, so the actual Zend Engine runs inside the same process as the Caddy web server, one thread per PHP execution (Windows support arrived in 2026), with Go orchestrating the thread pool through a state machine and C handling the SAPI lifecycle. You end up with a single static binary that is your app, your PHP runtime, and your web server, with zero IPC between them. It is one fewer box-and-arrow diagram for the ops team to keep arguing about, but the opcodes running inside it are exactly the ones php-fpm would have produced.
Neither of these projects had to solve PHP-the-language a second time, which is precisely why their compatibility story is so much better than in the first section: they didn’t reimplement anything, they relocated PHP.
Leave the engine alone, change what’s around it
The least drastic and, in production terms, probably the most consequential family: don’t touch the pipeline at all. A normal PHP process runs a normal request exactly as it always has. What moves is everything outside that boundary.
RoadRunner is a Go process manager that starts a pool of long-lived PHP workers and keeps feeding them requests over a binary protocol (Goridge), instead of the PHP-FPM model of spawning and tearing down a process, or at least its framework bootstrap — on every request, which is PHP-FPM’s way of never quite unpacking its suitcase. The Zend Engine inside each worker does exactly what it always did; what disappeared is the reload cost, paid once at worker startup instead of per request.
The diagram
What the panorama shows
The starting point predicts the compatibility story. Everything in lane 1 has to reimplement PHP’s semantics from scratch, in a foreign environment: references, magic methods, the coercion rules, the standard library’s thousand small inconsistencies, which is exactly why KPHP, TypePHP, and elephc all cover a subset of PHP rather than PHP. Lane 2 sidesteps that problem entirely by never rewriting anything: php-wasm and FrankenPHP inherit the real engine’s compatibility, bugs and all, because it is the real engine. Lane 3 doesn’t even ask the question: RoadRunner runs ordinary PHP because nothing about ordinary changed.
Speed and portability pull in different directions. The native-compilation projects in lane 1 exist because interpretation has a cost that JIT alone hasn’t eliminated for every workload. KPHP’s own numbers put it at three to ten times faster than plain PHP, which is the kind of numbers you build a compiler for. The JVM/.NET/WASM branch of lane 1 isn’t chasing speed at all; Quercus and PeachPie exist so a PHP codebase can sit inside a Java or .NET deployment, calling and being called by that ecosystem’s own code. Two different problems, sharing only the shape of “write a new compiler.”
Lane 3 is where most production PHP will actually feel this. Nobody’s rewriting a Laravel app to fit KPHP’s subset, but plenty of teams are already running it inside a RoadRunner worker or a FrankenPHP binary. That is because those get the payoff without rewriting anything at all.
The bigger picture
What ties all twelve of these together, more than any shared codebase or shared goal, is that every single one is still measured against the same yardstick: does it run real PHP. The WordPress install with fifty plugins, the Composer tree with a dependency that leans on eval(), the codebase where someone passed a reference into a closure in 2016 and it’s load-bearing now, and nobody currently employed knows why. phpr’s own benchmark is literally the WordPress torture test, and PeachPie brags about running it. KPHP doesn’t try and says so upfront. That’s a true challenge: it’s the actual hard problem every project on this list is solving a different slice of, and the panorama above is really a map of how much of real PHP each one has chosen to promise.
The yardstick is also moving. PHP 8.6 is due on November 19, 2026, and every project above is chasing a target that keeps moving every year.
Which raises a question: language-level tools already audit a codebase for type coverage, or, as I’ve written about before, for where validation silently doesn’t happen. Nothing audits a codebase for engine coverage: which functions, which reference patterns, which magic-method edge cases a given PHP source tree actually exercises, and therefore which of these eleven routes it could safely take. Until that audit exists, asking if my app run on X stays a question you answer by trying it and watching what breaks.
PHP has a wild new frontier!

