Swoole 的 typephp:让 PHP 编译成原生二进制的自举实验
PHP 的第三条路
2026 年 8 月 26 日,Swoole 团队开源了一个叫 typephp 的项目。它的描述只有一句话:
> Compile PHP source code into native machine code ahead of time.
翻译过来就是:把 PHP 代码提前编译成原生机器码。不是字节码缓存(OPcache),不是 JIT(HHVM),是真正的 AOT——PHP 源码 → C++17 → 原生可执行文件。
这件事本身不新鲜。新鲜的是:typephp 是用 PHP 写的,而且能编译自己。
自举:编译器的终极考试
在编译器世界有一个词叫”自举”(self-hosting)——编译器能编译自己的源码。这是编译器成熟度的终极测试。
C 编译器用 C 写,Go 编译器用 Go 写,Rust 编译器用 Rust 写。这些语言在 1.0 版本发布前都要完成自举——用自己的编译器编译自己的源码,产出能用的二进制。
PHP 从来没有过自举的编译器。原因很简单:PHP 是解释型语言,运行时是 Zend Engine,执行的是 opcode(字节码)。PHP 的”编译”只是把源码解析成 opcode 数组,运行时还是解释执行。这就像 Python 的 .pyc 文件——只是加速了加载,没有改变执行方式。
typephp 改变了这一点。它的编译器 tpc 是用 PHP 写的,但编译后的 tpc 是原生二进制。编译流程是:
PHP 源码(tpc 自己的源码) ↓ typephp 编译 C++17 代码 ↓ 系统 C++ 编译器 原生 tpc 二进制
这个二进制能再编译其他 PHP 代码。这就是自举——PHP 第一次有了能编译自己的原生编译器。
技术路线:PHP → C++ → 原生
typephp 的编译流程分三步:
第一步:解析 + 收集声明
PHP source + .stub.php declarations + optional C/C++ sources ↓ parse, validate, and collect declarations
这一步建立完整的符号模型,但不分配运行时缓存 ID。常量和默认值保留 AST 形式,等所有符号都收集完再处理。
第二步:降级到 C++17
↓ lower function bodies and constants to C++17
这一步把 PHP 的动态语义翻译成 C++ 的静态语义。int 变成 int64_t,float 变成 double,bool 变成 bool。动态值(比如关联数组)通过 PHPX 和 Zend 运行时互操作。
第三步:编译成原生产物
↓ native compiler + reusable object/PCH caches ↓ executable | PHP extension | shared library | WASI component
这一步调用系统的 C++ 编译器(gcc/clang/MSVC),生成四种产物之一:可执行文件、PHP 扩展、共享库、WASI 组件。
三种产物,一个代码库
typephp 最有意思的设计是:同一份 PHP 代码可以编译成三种不同的原生产物。
1. 可执行文件(bin)
tpc build –target=bin myapp.php
产出: myapp (原生可执行文件)
这是最直接的用法。PHP 代码变成一个不依赖 PHP 运行时的二进制。部署时不需要装 PHP,不需要装 Composer 依赖,一个文件搞定。
2. PHP 扩展(ext)
tpc build –target=ext mylib.php
产出: mylib.so (PHP 扩展)
把 PHP 代码编译成 PHP 扩展,然后在其他 PHP 项目里 dl('mylib.so') 加载。这相当于把热路径代码用 C++ 重写,但源码还是 PHP。
3. 共享库(lib)
tpc build –target=lib mylib.php
产出: libmylib.so (C 共享库)
把 PHP 代码编译成 C 共享库,可以被其他语言(Python、Go、Rust)通过 FFI 调用。这意味着 PHP 可以成为”系统级库”的编写语言——这在以前是不可想象的。
类型系统:PHP 语法,C++ 语义
typephp 的核心创新是:在保持 PHP 语法的同时,引入编译时类型信息。
// 传统 PHP
function add(b) {
return
b;
}
// typephp
function add(int b): int {
return
b;
}
第二种写法在传统 PHP 里只是类型提示(运行时检查),在 typephp 里会被编译成:
int64_t add(int64_t a, int64_t b) { return a + b; }
没有运行时类型检查,没有 zval 转换,没有 opcode 解释。就是两个整数相加,和手写 C++ 一样快。
更有意思的是容器类型:
// typephp 的强类型数组
arr->push(6);
这会被编译成 std::vector,比 PHP 原生数组快 10 倍。因为 PHP 数组实际上是有序哈希表,每次访问都要算哈希;而 std::vector 是连续内存,直接索引。
诚实的”子集”策略
typephp 最让人欣赏的一点是它的诚实。README 里明确写着:
> TypePHP intentionally supports a defined, testable subset of PHP rather than claiming drop-in compatibility with every dynamic PHP program.
它不支持所有 PHP 特性。有些动态特性(比如 extract()、variable variables、运行时修改类定义)和 AOT 编译天然冲突,被列在 不兼容特性文档 里。
这种诚实很重要。很多”把 X 语言编译成 Y”的项目(比如把 Python 编译成 C++ 的 Cython、把 JS 编译成 WebAssembly 的 AssemblyScript)都死在了”声称完全兼容”上——一旦声称完全兼容,用户就会拿生产代码来试,然后发现 90% 的库不能用,项目就凉了。
typephp 的策略是:明确告诉你哪些能用,哪些不能用。能用的是强类型代码、数值计算、算法密集型逻辑。不能用的是高度动态的元编程。这和 TypeScript 的思路一样——不是替代 JavaScript,而是给愿意写类型注解的人一个更快的选项。
Swoole 的角色:为什么是他们做这件事
Swoole 是 PHP 生态里做异步运行时的团队,他们的 swoole 扩展让 PHP 能做协程、TCP 服务、WebSocket。现在他们做 typephp,逻辑是连贯的:
– swoole 扩展:让 PHP 能做异步 I/O(解决并发问题) – typephp:让 PHP 能编译成原生代码(解决性能问题)
这两个方向加起来,PHP 就有了对抗 Go 和 Rust 的本钱——异步运行时 + 原生编译。当然,typephp 还在早期阶段,不能和 Go 的编译器或 Rust 的 LLVM 优化比。但它的存在本身就说明:PHP 生态不再满足于”够用就行”。
和其他 AOT 方案的对比
| 方案 | 语言 | 产物 | 自举 | 类型系统 |
|---|---|---|---|---|
| OPcache | PHP | opcode 缓存 | N/A | 动态 |
| HHVM/Hack | PHP 方言 | JIT 执行 | 否 | 渐进式 |
| PyPy | Python | JIT 执行 | 是(RPython) | 动态 |
| Cython | Python | C 扩展 | 否 | 渐进式 |
| typephp | PHP | 原生二进制 | 是 | 渐进式 |
typephp 的独特之处是:它是唯一一个用被编译语言本身写的 AOT 编译器。Cython 用 Python 写但编译成 C 扩展,不能自举。HHVM 用 C++ 写,编译 PHP 但不产出独立二进制。typephp 用 PHP 写,编译 PHP,产出独立二进制——这是完整的自举闭环。
限制和未来
typephp 不是银弹:
– 不是所有 PHP 代码都能编译。动态特性(eval、variable variables、extract)不支持。
– 不是所有 PHP 扩展都能互操作。PHPX 覆盖了 Zend API,但第三方扩展的 C API 各不相同。
– 编译时间不短。AOT 编译需要调用 C++ 编译器,大型项目可能需要几分钟。
– 生态为零。没有 Composer 包支持 typephp 的类型系统,没有 IDE 插件,没有调试器。
但这些限制恰恰是它的定位:不是替代 PHP,而是给性能敏感的代码一个原生编译的选项。你可以把热路径函数用 typephp 编译成扩展,其他代码还是用普通 PHP。这种”渐进式编译”的思路和 Cython 一样——不要求你重写整个项目,只要求你标注关键函数的类型。
一个更大的问题:PHP 还需要 AOT 吗
这是最根本的问题。PHP 的性能瓶颈通常不在 CPU,而在 I/O——数据库查询、缓存、HTTP 请求。这些用 Swoole 的协程已经能解决。typephp 解决的是 CPU 密集型场景:图像处理、加密、数值计算、机器学习预处理。
这些场景在 PHP 生态里占比不大。但 typephp 的价值不在于”让 PHP 更快”,而在于让 PHP 能进入它以前进不了的领域:
– 写一个 PHP 扩展,不需要学 C – 编译成共享库,给 Python/Go 调用 – 打包成单二进制,部署到没有 PHP 的环境
这些场景以前需要换语言,现在可以在 PHP 生态内完成。这对 PHP 社区的意义远大于性能数字——它在扩展 PHP 的边界。
—
项目地址:github.com/swoole/typephp
官方文档:swoole.com/aot/docs
Laravel News 报道:laravel-news.com/typephp-compile-php-native-binaries

用 PHP 写了个编译器,把 PHP 编成 C++,再拿它把自己编译一遍。蛇吃尾巴,吃圆了。README 原话:written entirely in PHP, fully self-hosting,连一行 C 胶水都不留。我最欣赏的倒是个负面的决定——明说只支持 “a defined, testable subset”,eval 和变量变量一律挡在门外。前辈们死在”号称全兼容”上的够多了,主动认怂反而显得能活。顺手算了笔账:PHP 数组是有序哈希表,取个值得先过一遍哈希;std::vector 连续内存直接下标,README 说快到 10 倍,我信,这不是魔法,是缓存行。至于 PHP 需不需要它——I/O 归 Swoole 管,CPU 密集的活以前只能换语言,现在多一个选项。挺好。