Okay, this is going to sound dumb, but I’ve spent a decent chunk of my career explaining why “compile PHP to a binary” wasn’t a thing you should wait around for. HipHop got abandoned. HHVM drifted so far from PHP compatibility that it stopped being PHP. Peachpie was clever and stayed niche. Every few years someone promises compiled PHP, and every few years I tell a client not to plan around it. So when Swoole released TypePHP last week, an open source AOT compiler that turns PHP source into native binaries, I read the announcement on Laravel News twice. Once out of excitement, once with my consultant hat on, trying to figure out what I’d tell a client who asked about it on Monday.
Short version for the impatient: this changes less about PHP performance than the headline suggests, and more about PHP distribution than anyone seems to be talking about. If you want the reasoning, keep reading.
What TypePHP actually is
TypePHP comes from the Swoole team, the people behind the coroutine runtime that’s been powering async PHP for years. That pedigree matters. This isn’t a weekend project from someone who read a compiler textbook; it’s from a team that has spent a decade living in the guts of the Zend engine.
It’s an ahead-of-time compiler with three output targets: a native binary, a PHP extension, or a shared library.
The native binary is the headline feature. You take PHP source, you get an executable that runs without a PHP interpreter installed. That’s the demo everyone will share.
The shared library target is quieter but interesting for a different crowd: it means PHP logic can be embedded into programs written in other languages, the way people link against liblua or SQLite today. I can’t name a project that needs this yet, which either means it’s pointless or means nobody could build for it before now. With runtimes, it’s usually the second one.
The extension target is the one I keep thinking about, though. Today, if you have a code path that’s truly CPU-bound, your realistic options are: rewrite it in C (almost nobody does), rewrite it in Rust with FFI (a few brave people do), or accept the cost (what everyone actually does). Compiling a PHP file into an extension means the escape hatch is written in the language you already know. I don’t know yet how well it works in practice, and I’d want to see real projects use it before betting on it. But that’s the target I’d watch over the next year.
Where PHP performance actually goes
Here’s the uncomfortable part. Most PHP apps aren’t slow because the interpreter is slow. Opcache has been caching compiled bytecode for over a decade, and PHP has shipped a JIT compiler since 8.0. The engine is fine. It’s been fine for years.
When I profile client apps, and most of my client work is Laravel apps that spend their lives waiting on MySQL, the flame graph is dominated by I/O. Database queries. HTTP calls to third party APIs. Cache round trips. An AOT-compiled binary waits on the same Postgres at exactly the same speed as interpreted code. I wrote about how I catch N+1 queries in Laravel because that single class of bug has bought my clients more milliseconds than every engine-level optimization I’ve ever applied, combined.
So when someone says “compiled PHP will make your app fast”, my first question is: what did the profiler say? If your bottleneck is a 300ms query, compilation buys you nothing you’d notice. If you’ve never profiled, you don’t know what your bottleneck is, and neither does the person selling you a compiler.
The before and after
What compilation does change is the shape of what you ship. Here’s a typical PHP CLI tool deployment today:
# Before: the whole interpreter comes along for the ride
FROM php:8.4-cli
RUN apt-get update && apt-get install -y libzip-dev \
&& docker-php-ext-install zip pcntl
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader
COPY . .
ENTRYPOINT ["php", "bin/tool.php"]
That image weighs hundreds of megabytes, and every user of your tool needs either Docker or a compatible PHP version installed. With an AOT binary, the endgame looks like what Go developers have enjoyed forever:
# After: build once, copy one artifact
FROM debian:bookworm-slim
COPY ./build/tool /usr/local/bin/tool
ENTRYPOINT ["tool"]
Full disclosure: I haven’t shipped TypePHP to production, and I won’t pretend I’ve memorized its build flags after a few days. The exact commands are in the project docs and they’ll change as the project matures. The point is the artifact. One file, no interpreter dependency, no “which PHP version does the server have” conversation. It also simplifies CI in a way I appreciate more each year: you build once, checksum the artifact, and promote the same file from staging to production. No reinstalling dependencies per environment and hoping the resolver picks the same versions twice. If you’ve ever distributed a PHP CLI tool to people who don’t use PHP, you know exactly how much friction that removes.
What AOT won’t fix
Some things a native binary will not do for you, learned from watching this same cycle play out in other ecosystems.
It won’t speed up I/O. I covered this above, but it bears repeating because it’s most of what real apps do all day.
It won’t beat the JIT where the JIT already works. PHP’s JIT handles numeric hot loops reasonably well. AOT’s edge is cold start and predictability, not raw throughput on code that’s already hot.
It won’t replace worker mode. Long-running PHP processes that skip per-request bootstrap already exist, and they come with sharp edges around state. I ran into one of those edges myself and wrote up how a static variable outlived my request in FrankenPHP worker mode. A compiled binary running a long-lived process inherits every one of those concerns. Compilation changes how code loads, not how state behaves.
And the honest unknown: debugging. Stack traces, Xdebug, error pages, the whole observability story for compiled PHP is unproven. Young compilers always underestimate how much of a language’s value lives in its tooling. When something breaks at 2am, “attach a debugger” needs to still mean something.
The ecosystem question nobody has answered yet
PHP isn’t just the language; it’s Composer and a hundred thousand packages, plus C extensions like pdo_mysql, redis, and imagick that half of them lean on. What happens when your dependency tree hits a package that needs a C extension the compiler doesn’t bundle? What about code that uses eval, or reflection-heavy frameworks like Laravel, which do a lot of dynamic dispatch that an AOT compiler has to either support or reject?
I don’t have answers, and as far as I can tell from the announcement, the honest state of things is “CLI tools and libraries first, full frameworks eventually, maybe.” That’s a reasonable roadmap. It’s also why my Laravel clients shouldn’t be rearchitecting anything this quarter. The boring stack (FPM, opcache, a sane database) is boring because it works.
Try this before you compile anything
Here’s the thing to do this week. It costs 20 minutes. Profile one production endpoint. If you don’t have a profiler set up, even the crude version teaches you something:
$start = hrtime(true);
$result = $slowThing();
error_log(sprintf('%s took %.1fms', 'slowThing', (hrtime(true) - $start) / 1e6));
Wrap your three most suspicious calls. Look at where the milliseconds actually are. If they’re in the database, you have work to do that no compiler will ever do for you. If they’re in CPU, congratulations: you’re in the small club for whom TypePHP might be the most interesting PHP release of 2026, and you should go star the repo and follow the extension target closely.
Either way, you’ll know. Knowing beats hype, and this week hype is in generous supply.