Swoole-Compiler 将提供 PHP Native AOT 编译器,支持将 PHP 代码编译为可执行文件,运算性能提高 150 倍
2026/4/24 1:32:31 网站建设 项目流程

概述

长期以来PHP作为解释型脚本语言,虽然在Web开发领域占据主导地位,但在性能表现上始终无法与RustGolang等编译型语言相提并论。这一局限性使得PHP在高性能计算、系统编程等领域逐渐式微,甚至一度被认为是过时的技术选择。

Swoole-Compiler v4版本推出Native AOT(Ahead-of-Time)编译器,将彻底改写这一现状。AOT编译器突破了PHP传统的解释执行模式,支持将PHP代码直接编译为原生二进制可执行文件。运算性能相比传统PHP解释器提升高达上百倍,性能表现已达到RustGolang等现代编译型语言的同一水平线。

这不仅仅是性能的提升,更是PHP语言范式的根本性变革:

  • 从解释执行到原生编译 -PHP代码可直接生成独立的二进制程序,无需依赖运行时环境

  • 从脚本语言到系统级编程 - 性能瓶颈的消除使PHP具备了开发高性能服务、系统工具的能力

Swoole-Compiler Native AOT编译器不仅为数百万PHP开发者提供了性能优化的终极方案,更重要的是,它为PHP生态注入了新的生命力,在云原生、微服务、高性能计算等现代技术领域占据一席之地。

运行机制

AOT编译器与HHVMKPHP等项目不同,它并不是PHP的另外一个实现。而是直接使用了ZendPHP的底层库,使用PHPX库作为兼容层,与PHPABI层面是完全互通的。但不使用ZendVMopcode handler以及zend_execute,而是将PHP代码编译为机器指令,与C++Golang的运行机制一致。因此AOT编译器完全兼容PHP的所有生态:

  • 可调用所有PHP扩展提供的内置函数、类、常量等

  • 同时支持静态编译和动态解释执行,支持evalinclude/require等动态脚本执行

  • 支持Composer,支持autoload,可动态调用其他第三方的composer包提供的函数和类

语法兼容性

AOT编译器支持绝大部分PHP的语法。不过由于AOT是静态编译,某些依赖运行时确定的特性是无法支持的:

  1. 不支持$$语法,局部变量为编译器符号,无法在运行时使用

  2. 不支持extract函数,无法运行时创建局部变量

  3. 不支持yield/generator生成器语法,建议使用fiber/swoole/swow协程,AOT编译器支持协程

  4. 不支持多层break或者continue语法,需要改成gototry/catch,这个特性只能在虚拟机模式中实现

  5. 禁止字面量字符串包含\0,例如$a = "hello \0 world;",与C++不兼容

  6. 不支持参数数量不匹配的函数调用,例如某个函数的参数是3个,但是实际运行的代码传入了4个,这在PHP动态执行阶段是允许的,但是AOT编译器无法支持

  7. 不支持Property Hook语法

  8. 不支持动态调用中使用引用,例如Closure闭包函数的参数是引用类型,在运行时才能确定,在AOT编译器中不支持,需要显式使用refval()函数转为引用

// 运行时才能得到函数的参数和返回值 $fn = getClosure(); // 编译器无法确定参数应该使用值还是引用,默认使用值传递 $fn($a, $b, $c); // $c 将显式地使用引用传递,而不是值 $fn($a, $b, refval($c));

编译器

./swoole_compiler -h Swoole-Compiler (AOT) v0.1.0 USAGE: ./bin/compiler.php <file/dir> [options] ARGUMENTS: <file> Input PHP file/directory to compile OPTIONS: -O <level> Optimization level (0-3, default: 0) -p, --profile Enable performance profiling -o, --output <file> Output binary name (default: input basename) -v, --verbose Verbose output -h, --help Show this help message -f, --force Force compile even if cache exists -m, --mode <mode> Compilation mode, -m bin(binary) or -m ext(extension), default: bin -j, --job <num> Number of parallel compilation jobs (default: 4) --no-literal-strings Disable literal strings optimization EXAMPLES: ./bin/compiler.php examples/hello.php ./bin/compiler.php examples/bench.php -O2 ./bin/compiler.php examples/bench.php -O2 ./bin/compiler.php examples/extension -O2 -o myapp -m ext ./bin/compiler.php examples/app.php -O3 -o myapp -v

main 函数

AOT编译器不支持游离代码,所有PHP代码必须放置在function中,因此不支持PHP模版、配置文件,仍然需要动态加载。AOT编译器支持两种模式:

  • ext模式:将PHP项目编译为PHP扩展,用于PHP-FPM/Apache模式

  • bin模式:将PHP项目编译为二进制可执行程序

bin模式,PHP项目必须提供一个main函数作为入口。

代码

<?php function main(int $argc, array $argv): void { echo "Hello World!\n"; var_dump(PHP_VERSION); }

编译

./swoole-compiler examples/code/main.php prepare: examples/code/main.php prepare completed: 1 source files in total convert: examples/code/main.php format: build/examples/code/main.cc cd /home/swoole/workspace/aot && clang-format -i /home/swoole/workspace/aot/build/examples/code/main.cc generate stub file: examples/code/main.php format: build/extension-main.cc cd /home/swoole/workspace/aot && clang-format -i /home/swoole/workspace/aot/build/extension-main.cc Starting parallel compilation with 4 jobs for 5 files Successfully compiled 5 files g++ /home/swoole/workspace/aot/src/Php/../cpp/php_cli_process_title.o /home/swoole/workspace/aot/src/Php/../cpp/ps_title.o /home/swoole/workspace/aot/src/Php/../cpp/main.o /home/swoole/workspace/aot/build/examples/code/main.o /home/swoole/workspace/aot/build/extension-main.o -o main $(php-config --includes) -I ~/workspace/projects/phpx/include -I /home/swoole/workspace/aot/build/include -I /home/swoole/workspace/aot/src/cpp -O0 -g -Wall -L $(php-config --prefix)/lib -L ~/workspace/projects/phpx/lib -lphpx -lphp

编译完成后,将生成一个main的二进制可执行文件,不包含PHP源代码,而是直接执行机器指令。

file main main: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=****, for GNU/Linux 3.2.0, with debug_info, not stripped ll main -h -rwxrwxr-x 1 swoole swoole 377K 4月 22 17:16 main

文件的尺寸为377K,可使用upx或者strip压缩裁剪。

./strip main ll main -h -rwxrwxr-x 1 swoole swoole 39K 4月 22 17:20 main

执行

./main Hello World! string(6) "8.4.14"

运算性能大幅提升

PHPZendVM+JIT执行机制不同,AOT编译器是将PHP代码编译为机器指令,在密集运算方面得到了数百倍的提升。

斐波那契数列

function fib(int $n): int { if ($n == 1 || $n == 2) { return1; } else { return fib($n - 1) + fib($n - 2); } } function main(int $argc, array $argv): void { $n = $argv[2]; $begin = microtime(true); echo fib($n) . "\n"; echo"Time: " . (microtime(true) - $begin) . "\n"; }

执行结果:

$ ./cli.php examples/fib.php 40 102334155 Time: 14.816216945648193 $ ./fib examples/fib.php 40 102334155 Time: 0.10961484909057617
  • cli.php是通过PHP ZendVM执行脚本,仅用于进行性能对比

  • fib使用了O3编译优化,命令是:./swoole_compiler examples/fib.php -O3

#!/usr/bin/env php <?php include $argv[1]; main($argc, $argv);

斐波那契数列的测试中,AOT编译器相比PHP提升了150倍。

PI 值计算(Leibniz 公式)

use native_types; function main() { ini_set("precision", 17); $rounds = (int) file_get_contents("./rounds.txt", true); $stop = $rounds + 2; var_dump($stop); $begin = microtime(true); $x = 1.0; $pi = 1.0; for ($i = 2; $i <= $stop; $i++) { $x = -1.0 + 2.0 * ($i & 0x1); $pi += $x / (2 * $i - 1); } $pi *= 4.0; print $pi . "\ntime: " . (microtime(true) - $begin) . "\n"; }
./cli.php examples/pi.php int(100000002) 3.141592643589326 time: 6.5242888927459717 ./pi examples/pi.php int(100000002) 3.141592643589326 time: 0.09414982795715332

AOT编译器提升了72倍。

JIT测试

zend_extension=opcache opcache.enable_cli=On opcache.jit=On

测试结果

./cli.php examples/fib.php 40 102334155 Time: 2.3740291595458984 ./cli.php examples/pi.php int(100000002) 3.141592643589326 time: 2.2408719062805176

启用JIT后,两个测试程序的性能得到了数倍提升,但相比AOT编译器,依然相差了两个数量级。

PHP自带的bench.phpmicro_bench.php部分测试开启opcache+JIT的性能比AOT编译器更好,主要原因是opcache拥有死代码消除优化器,没有实际作用的代码会被移除,导致部分PHP逻辑并未执行,无法真正比较执行性能,因此暂时不作为对比脚本。

use native_types

PHPInt类型在溢出时会自动转浮点,例如INT_MAX + 1会丢失精度转为浮点,10➗️3无法整除时自动转为浮点。

PHP为了支持这些特性牺牲了较多性能,但实际项目中几乎很少使用。使用整型时为精确计算,不应该转为浮点丢失精度,若非精确计算则应该直接使用浮点,而不是在运行时进行判断、检测和转换。

为了兼容已有的PHP代码,默认AOT编译器不会将整数声明为Native类型,保持了溢出自动转换的特性。在密集运算场景中,性能较差。在源文件中使用use native_types,则会告诉编译器,将整数、浮点型声明为原生类型,性能将大幅提升,但在溢出时则会丢弃精度,而不是转为浮点。

use native_types; function main() { $a = 10; $b = $a / 3; // $b 的值将是 3 ,而不是 3.333... }

全新的 C/C++ 互调用设计

PHP项目中若需要调用C++的类或者函数,通常需要编写一个PHP扩展,或者使用FFIAOT编译器提供了全新的C/C++互调用设计,允许在PHP代码中直接调用C++函数,编译器会将C++.cpp文件与.php文件一起编译并连接为一个可执行文件。

素数计算

function sieveOfEratosthenes(int $limit) { if ($limit < 2) return []; // 初始化布尔数组,索引代表数字,值代表是否为素数 $isPrime = array_fill(0, $limit + 1, true); $isPrime[0] = false; $isPrime[1] = false; // 0 和 1 不是素数 for ($i = 2; $i * $i <= $limit; $i++) { if ($isPrime[$i]) { // 标记 i 的所有倍数为非素数 for ($j = $i * $i; $j <= $limit; $j += $i) { $isPrime[$j] = false; } } } // 收集所有素数 $primes = []; for ($num = 2; $num <= $limit; $num++) { if ($isPrime[$num]) { $primes[] = $num; } } return $primes; } function main() { global $argv; $n = isset($argv[2]) ? (int)$argv[2] : 10000000; $begin = microtime(true); // 参数校验 if ($n < 2) { fwrite(STDERR, "请输入一个大于等于2的整数作为上限。\n"); exit(1); } $primes = sieveOfEratosthenes($n); var_dump(count($primes)); var_dump(microtime(true) - $begin); }

在素数计算的程序中,PHP只有数组可以作为容器,数组的元素存储的类型为boolPHP即使存储一个bitbool值,也至少需要16个字节,存储10亿个数字,需要40 GB+内存。如果能够直接使用C++std::vector<bool>则仅需120M字节。

C++std::vector<bool>是一个bitmap,占用的内存极低

但在PHP代码中不能直接调用C++函数,而编写一个PHP扩展项目,又是一件非常麻烦的事情。在AOT编译器中可以直接调用C++代码,可以轻松解决此问题。

代码修改

function sieveOfEratosthenes(int $limit) { if ($limit < 2) return []; // 初始化布尔数组,索引代表数字,值代表是否为素数 $isPrime = vector_new($limit + 1, true); // 0 和 1 不是素数 vector_set($isPrime, 0, false); vector_set($isPrime, 1, false); for ($i = 2; $i * $i <= $limit; $i++) { if (vector_get($isPrime, $i)) { // 标记 i 的所有倍数为非素数 for ($j = $i * $i; $j <= $limit; $j += $i) { vector_set($isPrime, $j, false); } } } // 收集所有素数 $primes = []; for ($num = 2; $num <= $limit; $num++) { if (vector_get($isPrime, $num)) { $primes[] = $num; } } return $primes; }

新的代码使用vector_set()vector_get()替代数组来管理数字。接下来需要编写一个vector.stub.php存根文件来声明函数。

function vector_new(int $size, bool $init = false): mixed{} function vector_get(mixed $vector, int $offset): bool {} function vector_set(mixed $vector, int $offset, bool $value): void {}

.stub.php文件只有函数的声明,但没有实现,具体实现的代码将在.cc文件中编写。

#include <phpx.h> usingnamespace php; class VectorBox :public Box { public: std::vector<bool> vec; VectorBox(size_t size, bool init) { vec.resize(size, init); } void checkOffset(Int offset) { if (offset >= vec.size()) { throwError("index[%ld] is out of range()", offset); } } }; var php_vector_new(Int size, Bool init) { return {new VectorBox(size, init)}; } Bool php_vector_get(var box, Int offset) { auto vecbox = box.toBox<VectorBox>(); vecbox->checkOffset(offset); return vecbox->vec.at(offset); } void php_vector_set(var box, Int offset, Bool value) { auto vecbox = box.toBox<VectorBox>(); vecbox->checkOffset(offset); vecbox->vec.at(offset) = value; }

C++代码的编写可参考PHPX相关文档

导出C++函数为PHP函数,在PHP代码中调用,需要满足以下条件:

  1. 必须以php_为前缀

  2. 必须以PHP类型作为参数和返回值

  3. 需要在.stub.php文件中声明该函数

类型映射关系表:

PHP 类型

C++ 类型

int

php::Int

bool

php::Bool

array

php::Array

mixed

php::Var

float

php::Float

string

php::Str

object

php::Object

resource

php::Resource

void

void

例如在.stub.php中声明一个函数

function fn_test(int $a, int $b): string {}

等价于

php::Str php_fn_test(php::Int a, php::Int b) { }

在其他的PHP文件中就直接调用该函数:

echo fn_test(100, 999);

除了函数之外,也可以实现PHP类,方法、静态方法、属性、常量、静态属性在stub文件中声明,在C++文件中只实现方法和静态方法。

  • 命名空间和类名使用双下划线(__)分割作为前缀

  • 名称需要全部转为小写

class ClassFoo { protected string $prop; public function __construct(string $name); public function bar(int $a): int; }
void php_classfoo____construct(php::Object &this_, php::Str name) {} php::Int php_classfoo__bar(php::Object &this_, php::Str name) {}
  • 类方法函数的第一个参数是this_,表示当前对象,若是静态方法,则第一个参数为NULL

  • 可使用this_.attr()读写对象属性,使用this_.call()调用对象方法

实际项目测试

本次AOT选择了Workerman作为测试项目。Workerman项目的代码需要少量修改才能够通过编译。包括游离代码处理,retval()修改等。可根据编译器的错误提示修改代码实现适配。

除此之外还需要编写一个main.php文件作为入口:

use Workerman\Protocols\Http\Session; useWorkerman\Worker; function main() { require__DIR__ . '/../functions.php'; $http_worker = new Worker('http://0.0.0.0:2345'); $http_worker->count = 1; $http_worker->onMessage = function ($connection, $request) { $connection->send("Hello World"); }; Session::init(); Worker::runAll(); }
./swoole_compiler projects/workerman/src/ -o workerman

运行

./workerman start Workerman[worker.php] start in DEBUG mode -------------------------------------------- WORKERMAN --------------------------------------------- Workerman/5.1.9 PHP/8.4.14 (JIT off) Linux/6.8.0-107-generic --------------------------------------------- WORKERS ---------------------------------------------- event-loop proto user worker listen count state select tcp swoole none http://0.0.0.0:2345 1 [OK] ---------------------------------------------------------------------------------------------------- Press Ctrl+C to stop. Start success.
curl http://127.0.0.1:2345 Hello World

Python 互调用支持

AOT编译器将支持在PHP代码中直接调用Python语言的特性。

use sys; useos; usenumpyasnp; function np() { print(np::$module); print(np::version->full_version); } function main() { var_dump(sys::api_version); var_dump(os::getpid()); var_dump(os::environ->get("PATH")); }

此特性在预览版中尚未实现,将在测试版中提供

版本计划

虽然识沃团队已经对AOT编译器进行了大量内部测试和优化,但项目本身依然处于产品打磨期,未来依然有很长的路要走。AOT编译器整体的发版计划如下:

  • 预览版:在2026.5.1前发布,仅用于功能体验,不可用于生产环境

  • 测试版:在2026.10.1前发布,解决绝大部分问题,可用于非核心项目

  • 正式版:在2027.5.1前发布,可用于正式的生产环境

识沃团队将同时维护3.24.0两个版本,3.2版本依然是围绕着PHP语言的支持迭代,4.0版本则将逐步发展为一门与PHP语言相似的全新静态编译语言。

Swoole-Compiler 版本

技术方向

PHP 版本

v3.2

Opcode

字节码加密与混淆

7.2~8.4

v4.0

AOT Native

编译器

8.2 ~ 8.5

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询