{"id":625,"date":"2026-08-28T13:04:31","date_gmt":"2026-08-28T13:04:31","guid":{"rendered":"https:\/\/abrarqasim.com\/blog\/php-performance-typephp-native-binaries\/"},"modified":"2026-08-28T13:04:31","modified_gmt":"2026-08-28T13:04:31","slug":"php-performance-typephp-native-binaries","status":"publish","type":"post","link":"https:\/\/abrarqasim.com\/blog\/php-performance-typephp-native-binaries\/","title":{"rendered":"PHP Performance and TypePHP: Native Binaries Change the Math"},"content":{"rendered":"<p>Okay, this is going to sound dumb, but I&rsquo;ve spent a decent chunk of my career explaining why &ldquo;compile PHP to a binary&rdquo; wasn&rsquo;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 <a href=\"https:\/\/laravel-news.com\/typephp-compile-php-native-binaries\" rel=\"nofollow noopener\" target=\"_blank\">announcement on Laravel News<\/a> twice. Once out of excitement, once with my consultant hat on, trying to figure out what I&rsquo;d tell a client who asked about it on Monday.<\/p>\n<p>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.<\/p>\n<h2 id=\"what-typephp-actually-is\">What TypePHP actually is<\/h2>\n<p>TypePHP comes from the <a href=\"https:\/\/github.com\/swoole\/swoole-src\" rel=\"nofollow noopener\" target=\"_blank\">Swoole<\/a> team, the people behind the coroutine runtime that&rsquo;s been powering async PHP for years. That pedigree matters. This isn&rsquo;t a weekend project from someone who read a compiler textbook; it&rsquo;s from a team that has spent a decade living in the guts of the Zend engine.<\/p>\n<p>It&rsquo;s an ahead-of-time compiler with three output targets: a native binary, a PHP extension, or a shared library.<\/p>\n<p>The native binary is the headline feature. You take PHP source, you get an executable that runs without a PHP interpreter installed. That&rsquo;s the demo everyone will share.<\/p>\n<p>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&rsquo;t name a project that needs this yet, which either means it&rsquo;s pointless or means nobody could build for it before now. With runtimes, it&rsquo;s usually the second one.<\/p>\n<p>The extension target is the one I keep thinking about, though. Today, if you have a code path that&rsquo;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&rsquo;t know yet how well it works in practice, and I&rsquo;d want to see real projects use it before betting on it. But that&rsquo;s the target I&rsquo;d watch over the next year.<\/p>\n<h2 id=\"where-php-performance-actually-goes\">Where PHP performance actually goes<\/h2>\n<p>Here&rsquo;s the uncomfortable part. Most PHP apps aren&rsquo;t slow because the interpreter is slow. Opcache has been caching compiled bytecode for over a decade, and PHP has shipped a <a href=\"https:\/\/wiki.php.net\/rfc\/jit\" rel=\"nofollow noopener\" target=\"_blank\">JIT compiler since 8.0<\/a>. The engine is fine. It&rsquo;s been fine for years.<\/p>\n<p>When I profile client apps, and most of <a href=\"https:\/\/abrarqasim.com\/work\" rel=\"noopener\">my client work<\/a> 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 <a href=\"https:\/\/abrarqasim.com\/blog\/n-plus-1-query-problem-how-i-catch-it-in-laravel\" rel=\"noopener\">how I catch N+1 queries in Laravel<\/a> because that single class of bug has bought my clients more milliseconds than every engine-level optimization I&rsquo;ve ever applied, combined.<\/p>\n<p>So when someone says &ldquo;compiled PHP will make your app fast&rdquo;, my first question is: what did the profiler say? If your bottleneck is a 300ms query, compilation buys you nothing you&rsquo;d notice. If you&rsquo;ve never profiled, you don&rsquo;t know what your bottleneck is, and neither does the person selling you a compiler.<\/p>\n<h2 id=\"the-before-and-after\">The before and after<\/h2>\n<p>What compilation does change is the shape of what you ship. Here&rsquo;s a typical PHP CLI tool deployment today:<\/p>\n<pre><code class=\"language-dockerfile\"># Before: the whole interpreter comes along for the ride\nFROM php:8.4-cli\nRUN apt-get update &amp;&amp; apt-get install -y libzip-dev \\\n    &amp;&amp; docker-php-ext-install zip pcntl\nCOPY composer.json composer.lock .\/\nRUN composer install --no-dev --optimize-autoloader\nCOPY . .\nENTRYPOINT [&quot;php&quot;, &quot;bin\/tool.php&quot;]\n<\/code><\/pre>\n<p>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:<\/p>\n<pre><code class=\"language-dockerfile\"># After: build once, copy one artifact\nFROM debian:bookworm-slim\nCOPY .\/build\/tool \/usr\/local\/bin\/tool\nENTRYPOINT [&quot;tool&quot;]\n<\/code><\/pre>\n<p>Full disclosure: I haven&rsquo;t shipped TypePHP to production, and I won&rsquo;t pretend I&rsquo;ve memorized its build flags after a few days. The exact commands are in the project docs and they&rsquo;ll change as the project matures. The point is the artifact. One file, no interpreter dependency, no &ldquo;which PHP version does the server have&rdquo; 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&rsquo;ve ever distributed a PHP CLI tool to people who don&rsquo;t use PHP, you know exactly how much friction that removes.<\/p>\n<h2 id=\"what-aot-wont-fix\">What AOT won&rsquo;t fix<\/h2>\n<p>Some things a native binary will not do for you, learned from watching this same cycle play out in other ecosystems.<\/p>\n<p>It won&rsquo;t speed up I\/O. I covered this above, but it bears repeating because it&rsquo;s most of what real apps do all day.<\/p>\n<p>It won&rsquo;t beat the JIT where the JIT already works. PHP&rsquo;s JIT handles numeric hot loops reasonably well. AOT&rsquo;s edge is cold start and predictability, not raw throughput on code that&rsquo;s already hot.<\/p>\n<p>It won&rsquo;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 <a href=\"https:\/\/abrarqasim.com\/blog\/frankenphp-worker-mode-the-static-that-outlived-my-request\" rel=\"noopener\">how a static variable outlived my request in FrankenPHP worker mode<\/a>. A compiled binary running a long-lived process inherits every one of those concerns. Compilation changes how code loads, not how state behaves.<\/p>\n<p>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&rsquo;s value lives in its tooling. When something breaks at 2am, &ldquo;attach a debugger&rdquo; needs to still mean something.<\/p>\n<h2 id=\"the-ecosystem-question-nobody-has-answered-yet\">The ecosystem question nobody has answered yet<\/h2>\n<p>PHP isn&rsquo;t just the language; it&rsquo;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&rsquo;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?<\/p>\n<p>I don&rsquo;t have answers, and as far as I can tell from the announcement, the honest state of things is &ldquo;CLI tools and libraries first, full frameworks eventually, maybe.&rdquo; That&rsquo;s a reasonable roadmap. It&rsquo;s also why my Laravel clients shouldn&rsquo;t be rearchitecting anything this quarter. The boring stack (FPM, opcache, a sane database) is boring because it works.<\/p>\n<h2 id=\"try-this-before-you-compile-anything\">Try this before you compile anything<\/h2>\n<p>Here&rsquo;s the thing to do this week. It costs 20 minutes. Profile one production endpoint. If you don&rsquo;t have a profiler set up, even the crude version teaches you something:<\/p>\n<pre><code class=\"language-php\">$start = hrtime(true);\n$result = $slowThing();\nerror_log(sprintf('%s took %.1fms', 'slowThing', (hrtime(true) - $start) \/ 1e6));\n<\/code><\/pre>\n<p>Wrap your three most suspicious calls. Look at where the milliseconds actually are. If they&rsquo;re in the database, you have work to do that no compiler will ever do for you. If they&rsquo;re in CPU, congratulations: you&rsquo;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.<\/p>\n<p>Either way, you&rsquo;ll know. Knowing beats hype, and this week hype is in generous supply.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Swoole&#8217;s TypePHP compiles PHP into native binaries. Here&#8217;s what that actually changes about PHP performance, and what no compiler will ever fix for you.<\/p>\n","protected":false},"author":2,"featured_media":624,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"","rank_math_description":"Swoole's TypePHP compiles PHP into native binaries. Here's what that actually changes about PHP performance, and what no compiler will ever fix for you.","rank_math_focus_keyword":"php performance","rank_math_canonical_url":"","rank_math_robots":"","footnotes":""},"categories":[147,52],"tags":[684,19,53,683,682],"class_list":["post-625","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-backend","category-php","tag-aot-compilation","tag-performance","tag-php","tag-swoole","tag-typephp"],"_links":{"self":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/625","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/comments?post=625"}],"version-history":[{"count":0,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/625\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media\/624"}],"wp:attachment":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media?parent=625"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/categories?post=625"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/tags?post=625"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}