概述

长期以来 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 语法,需要改成 goto 或 try/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

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐