📌 项目地址:swoole/typephp | ⭐ 766 颗星 | 🔧 PHP | 📜 未标注
项目地址:https://github.com/swoole/typephp | 766 Star | PHP | Swoole 官方出品
PHP 的性能优化,社区过去二十年基本走的是两条路:OPcache 缓存字节码,PHP 8 的 JIT 在运行时把热点 opcode 编译成机器码。两条路都没有跳出 VM 执行模型。Swoole 团队的 TypePHP 走了第三条:AOT(Ahead-Of-Time)编译,在构建期直接把 PHP 源码降到 C++17,再编译成原生机器码。产物可以是原生可执行文件、PHP 扩展或共享库,架构图里还提到 WASI component。
冷启动即巅峰。没有解释器,没有 opcode cache,没有 JIT 预热。
自举:编译器编译了自己
我觉得这个项目最硬的一点是自举。
TypePHP 编译器本身完全用 PHP 写成,bootstrap 链路里没有任何 C/C++ 胶水代码。它的 tpc 编译器二进制,是用 TypePHP 编译它自己的 PHP 源码得到的。
这意味着什么?编译器要处理的每一种语言特性,都先在自己身上跑通了一遍。能编译自己的编译器,是编译器工程里公认的验证标准——GCC、LLVM、Rust 都走过这条路。一个 766 Star 的新项目直接以自举状态发布,说明作者对支持子集的定义是认真的。
不是把整个 PHP 静态化
第二点值得仔细看:TypePHP 是混合模型。
编译后的用户函数不再以 Zend opcode 执行,直接跑原生代码。但动态 PHP 值、内部函数、反射、对象元数据,仍然通过 PHPX 与 Zend 运行时互操作。换句话说,它做的是“热路径静态化 + 动态部分走 Zend”,而不是把 PHP 强行变成一门纯静态语言。
代价是要提供编译期类型信息。源码需要配合 .stub.php 声明文件,还可以混入可选的 C/C++ 源码一起构建。
两阶段编译,为了确定性
编译流程里有个设计细节值得一提。
prepare 阶段构建完整符号模型,但不分配运行时缓存 ID;常量和声明默认值在这个阶段保留 AST。等到 convert 阶段——所有项目符号都已知之后——才统一降级到 C++17。
为什么要这么绕?因为单文件编译时你不知道别的文件里定义了什么。两阶段设计保证了多文件构建和自举构建的确定性。配合可复用的 object/PCH 缓存,增量编译也能提速。
跨平台构建
CI 覆盖四个平台:Linux x64、Linux ARM64、macOS ARM64、Windows,各有独立 workflow。对一个编译器项目来说,这个覆盖范围算扎实。
使用前必须知道的事
README 没有给出具体的安装命令、tpc 调用参数或 .stub.php 的写法,实际用法需要去仓库的 docs/ 目录找。它明确声明:有意只支持一个定义清晰、可测试的 PHP 子集,不承诺对任意动态 PHP 程序开箱即用。
迁移现有项目前,先读 docs/en/INCOMPATIBLE_PHP_FEATURES.md。这不是免责声明式的敷衍——高度动态的代码(大量 eval、运行时魔术方法调用)大概率不在支持范围内。
项目处于活跃开发期,API 和行为可能变动,生产采用前自己评估。
同类方案的参照
走“PHP 编译成原生代码”这条路的前辈不少:Phalanger 编译到 .NET,早已停滞;Hack 则干脆另起了一套语法,脱离了 PHP 生态。TypePHP 的差异点有三个:保留 PHP 语法、由 Swoole 官方维护、自举。前两个决定了它对存量 PHP 开发者的迁移成本,第三个决定了工程质量下限。
适合谁关注:想把 CLI 工具做成单二进制分发的 PHP 开发者,用 Swoole 跑常驻服务、在意启动延迟的团队,以及单纯对编译器自举过程感兴趣的工程爱好者。最后一类人光是把仓库 clone 下来读 bootstrap 流程,就值回时间。