Last week I moved the PHP on my server from 8.1 to 8.5 and wrote it up as a maintenance job. This week’s radar entry demands that version as a precondition. TypePHP is an AOT (ahead-of-time) compiler that turns PHP source into C++ and then into native machine code. Your PHP stops being a script that gets interpreted and becomes an executable binary.
The metaphor writes itself: molten metal. Everything attractive about PHP lives in how fluid it is. TypePHP pours that fluid into a mould and lets it cool. The part that comes out is far faster and far stronger. It just does not take any shape you want any more.
Up front: this round I cloned the repository and read the source and the incompatibility list. I did not run the compiler or take my own measurements. Every number below comes from the project’s own repository, with a date on it.
What does TypePHP actually do?
TypePHP is an open source AOT compiler that lowers PHP source to C++17 and hands it to a native compiler. It produces three kinds of output: an executable binary, a PHP extension (.so / .dll), and a shared library.
The interesting part is that the compiler itself is written entirely in PHP and is self-hosting. The tpc binary is produced by compiling TypePHP’s own PHP source with TypePHP; there is no C or C++ glue in the bootstrap chain. That is not showmanship, it is a test: a compiler that cannot compile its own compiler will not compile yours either.
Dynamic values, internal functions and reflection still go through the Zend runtime over the PHPX bridge. Nobody is claiming PHP has been left behind: your user functions stop running as Zend opcodes once compiled, and the rest stays where it was.
Installation: three commands and a toolchain
The Composer side really is three lines. The actual work is having a C++ toolchain and PHP development headers on the machine.
# prerequisites on Ubuntu/Debian
sudo apt install build-essential cmake pkg-config libgmp-dev libmpfr-dev
# add the compiler to the project
composer require --dev swoole/typephp
# compile
vendor/bin/tpc.php project.yml
The official requirement list is specific: PHP 8.4–8.5 CLI with development headers and php-config; a matching libphp.so for binary builds; GCC 9+ or Clang with C++17; CMake 3.24+; Composer 2; GMP and MPFR. If your server is still on PHP 8.1, you do not get through the door.
Binary mode requires a main() function. The smallest example looks like this:
<?php
function main(): void
{
echo "Hello World!\n";
var_dump(PHP_VERSION);
}
The shape of the mould: what do you give up?
This is the section worth reading twice, because the speed table on the repository page will not tell you any of it. TypePHP supports a defined, testable subset of PHP rather than all of it. From the project’s incompatibility list:
- No executable code at global scope. Only declarations,
use,declareand constant definitions are allowed there. Every statement has to sit inside a function. - A strict
main()signature. Either no parameters or(int $argc, array $argv), returningvoid. - No variable variables.
$$varis not supported. - Closures lose their flexibility.
Closure::bind(),bindTo()andcall()are unsupported; closures and arrow functions take no reference parameters. - Reference tricks end here. Variadic by-reference parameters (
&...$args) are not supported. - FiberGenerator instead of Generator. Which breaks
instanceofchecks and reflection compatibility. - Type inference is one-way. Since 0.8.0, inferred
int,floatandboollocals use fixed native storage. Write$i = 100and that local is permanently an integer; reusing it as aforeachkey is rejected.
The repository’s own warning is no gentler: a feature missing from the README does not mean it is supported, and you are told to consult the incompatible-feature list instead. That is an honest stance. In practice it means taking an existing PHP project and trying to compile it is not an experiment, it is a rewrite.
Whose measurements are those speed numbers?
The table in the repository uses the official language benchmarks shipped with php-src: bench.php drops from 5.034 s to 0.603 s (roughly 8×) and micro_bench.php from 13.045 s to 2.021 s (roughly 6.5×), compiled with -O3. On the container side, a 10000×100000 element update loop takes 67.6 s with a PHP array, 6.4 s with TypePHP’s std::array, and 6.2 s with hand-written C++ std::vector.
Impressive numbers. But the project measured them itself, and the README says so plainly: these are a project measurement snapshot, not a performance guarantee; PHP version, compiler, CPU and flags all change the result, so compare the same workload on the same machine before deciding anything. Putting that on your own shop window is rare enough to deserve credit.
Licence, version and maintenance
As of 29 September 2026 the repository shows 1,417 stars, 79 forks and 0 open issues. It was created on 11 May 2026 and the last commit landed the same day I am writing this, 29 September 2026. The latest release on Packagist is v0.9.3, published 24 September 2026, and the version constant in the source agrees. Install count: 19,091. So: four and a half months old, still on 0.x.
The licence is GPL-3.0-only. That is usually a one-line detail in a radar piece and here it should not be: every alternative in the comparison below is MIT or Apache-2.0. The project’s own page says it is free, open source under the GPL, and free for commercial use. If you ship a closed-source product, do not stop at that sentence — read the licence.
Credit where it belongs: the project comes from the Swoole team. The copyright holder in composer.json is 上海识沃网络科技有限公司 (Swoole), and the package maintainer on Packagist is Tianfeng Han (matyhtf). The same team’s swoole-src sits next to it with 18,923 stars under Apache-2.0, so this is not a first attempt by a team formed yesterday.
Where does it differ from the alternatives?
“Turning PHP into a binary” describes at least four different things, and people pick the wrong tool because the four get mixed up. Star counts as of 29 September 2026.
| Tool | What it actually does | Licence | Stars | Reach for it when |
|---|---|---|---|---|
| TypePHP | Compiles PHP to machine code via C++ | GPL-3.0-only | 1,417 | Compute loops are eating the CPU |
| FrankenPHP | Embeds the interpreter and the app in one binary; no compilation | MIT | 11,371 | You want deployment down to one file |
| static-php-cli | Builds a portable static PHP binary; your code is still interpreted | MIT | 1,947 | Shipping a dependency-free CLI tool |
| Zephir | Builds a C extension from a separate PHP-like language | MIT | 3,385 | Writing an extension from scratch |
| Swoole | Coroutine-based concurrency library; no compilation | Apache-2.0 | 18,923 | Services drowning in I/O wait |
The split is this: FrankenPHP and static-php-cli make deployment easier, Swoole shortens waiting, TypePHP speeds up arithmetic. If the problem is “make it one file”, you do not need TypePHP; if it is “this loop is eating the CPU”, the others give you nothing.
Where does it fit on my side?
My day-to-day is mostly framework-free plain PHP, MariaDB, some Vue and the Linux side. In that stack TypePHP has one clear address: overnight batch jobs. Catalogue imports, price and margin recalculation, report generation — the places where a single CLI script walks over millions of rows. Their shape is already close to the mould TypePHP asks for: everything in functions, types declared, no dynamic tricks.
<?php
function addVat(float $net, float $rate): float
{
return $net * (1.0 + $rate / 100.0);
}
function total(array $nets, float $rate): float
{
$sum = 0.0;
foreach ($nets as $net) {
$sum += addVat((float) $net, $rate);
}
return $sum;
}
function main(): void
{
$nets = [1299.0, 89.9, 4550.0];
printf("%.2f\n", total($nets, 20.0));
}
This is valid plain PHP (checked with php -l) and it is shaped according to the documented rules: no executable code at global scope, explicit types, main() as the entry point. Since I did not compile and measure it, I am not telling you how much faster it got.
An honest caveat: in scripts like these the bottleneck is usually not PHP. With an N+1 query problem or badly ordered WHERE conditions (in Turkish) still in place, buying a compiler is fitting a stronger tap to a blocked drain.
One last note for anyone handing their codebase to agents: this incompatibility list is not a style preference, it is the compiler’s contract. An agent will confidently produce code using $$field, that code will run as PHP, and tpc will reject it. Write the rule into your project rules file before you start.
When it helps and when it does not
It helps: compute-heavy batch jobs; a CLI tool you are about to write from scratch; turning one hot path into an extension called from PHP; your own server with PHP 8.4/8.5 and a full toolchain.
It does not: compiling an existing WordPress, Laravel or legacy enterprise app as-is; anything on shared hosting; workloads bottlenecked on the database or the network; closed-source products where the GPL is a problem; critical systems where a 0.x version number is unacceptable.
In this week’s series chapter I wrote that running a model locally does not remove the bill, it only changes the line items. TypePHP belongs to the same family: the part you cast comes out faster, and in exchange you leave the flexibility that makes PHP PHP inside the mould. Not a loss, a trade. Just know what you are handing over.
Do not decide without measuring, pin the version, read the licence. Stay well.