Skip to content

Repository files navigation

TinyPHP logo

zread

TinyPHP

PHP → C AOT 编译器 — 用 PHP 语法写原生二进制,零运行时依赖,性能飙 300-500 倍。

TinyPHP 不是 PHP 解释器或运行时替代品。它把 PHP 代码(强类型子集)编译成安全的 C,再由 GCC/Clang/TCC 编译为原生可执行文件。没有 Zend VM、没有 OPCache、不需要 PHP 环境。

快速开始

# 编译单文件
php tphp.php test/var/var.php

# 编译多文件(入口必须有全局 class Main)
php tphp.php main.php demo.php

# 带 C 源文件
php tphp.php main.php bridge.php lib.c

# 扫描当前目录所有 .php / .c
php tphp.php .

# 指定输出 / 编译器
php tphp.php main.php -o app -cc gcc

# 跨平台编译
php tphp.php main.php -os linux            # x86_64 Linux
php tphp.php main.php -os linux -arch aarch64  # ARM64 Linux
php tphp.php main.php -os windows          # Windows .exe

# 动态库导出(配合 #[Export] 注解)
php tphp.php lib.php -shared -o mylib.dll  # Windows
php tphp.php lib.php -shared -o mylib.so   # Linux

CLI 选项

选项 说明
-o <output> 输出文件路径
-cc <compiler> 指定 C 编译器(默认内置 TCC)
-os <target> 跨编译目标:windowslinuxmacos
-arch <arch> 目标架构:x86_64aarch64(Windows/Linux 默认 x86_64,macOS 默认 aarch64)
-shared 编译为动态库(.dll/.so/.dylib),配合 #[Export] 导出 C 函数
--debug 打印编译命令,运行二进制并与 #debug 预期输出逐行比对
-h, --help 显示帮助

测试与调试(#debug

在源码任意位置插入 #debug 指令声明预期输出,配合 --debug 自动编译运行并比对:

<?php
#debug int(42)
#debug string(5) "hello"
#debug bool(true)

class Main {
    public function main(): void {
        var_dump(42);
        var_dump("hello");
        var_dump(true);
    }
}
php tphp.php test.php --debug
# [YES] int(42)
# [YES] string(5) "hello"
# [YES] bool(true)
写法 含义
#debug text 预期该行输出为 text(精确匹配)
#debug 预期该行为空行
#debug ~ text 预期近似值(如时间/时区相关),[REF] 只展示不判错

文件级测试注解(放在 <?php 同一行或下一行):

注解 位置 含义
// @skip (文件头注释) <?php 同行 CI 自动跳过该文件(如 OS 限定、需要外部环境)
// @multi @with x,y <?php 同行 多文件编译提示

CI 测试自动发现:php .github/scripts/run_tests.php 递归扫描 test/ 下所有含 #debug 且不含 @skip 的文件。

入口逻辑

TinyPHP 要求入口文件必须有一个全局命名空间(无 namespace 声明)的 class Main

<?php

class Main
{
    // 构造函数 — 接收命令行参数(可选,默认可省略)
    public function __construct(int $argc, array $argv)
    {
        // $argc — 参数个数,$argv — 参数数组
    }

    // 入口函数 — 必须为 public function main(): void
    public function main(): void
    {
        echo "hello world\n";
    }

    // 析构函数 — 程序退出前自动调用(可选)
    public function __destruct() {}
}
方法 签名 必须 说明
__construct (int $argc, array $argv) 接收命令行参数;可省略
main (): void 程序入口,必须强类型声明
__destruct () 退出前自动调用,可省略

独立 PHAR(推荐)

GitHub Actions 自动构建全平台单文件:

tphp main.php    # include/ + tcc/ 自动解压到同级目录

多文件编译

TinyPHP 支持 @multi 注解声明多文件入口(辅助文件用 @skip 标记无 Main 类):

// main.php
<?php // @multi @with models.php,services.php
use MyApp\Models\User;
// ...

PHP 兼容度

基于 PHP 8.5 强类型语法,兼容度约 80%。核心约束:AOT 编译不兼容动态特性

✅ 完全支持

类别 特性
控制流 if/elseif/elsewhiledo-whileforforeachswitch(含字符串 switch,全部支持 fall-through 穿透语义)、matchbreak/continue/goto
OOP classextendsabstractinterfaceimplementstrait+useenum__construct(public $x)__destructstatic/final/readonlyinstanceofself::$this、链式调用、?-> 空安全
闭包 function() use($x) {}fn($x): T => expr 单表达式、fn($x): T => { stmts; return expr; } 块体、多捕获、嵌套闭包
异常 try/catch(Exception $e)/finallythrow new Exception()throw 表达式、error($msg) 抛出可捕获异常、`Type
类型 int float string bool array array<T> 泛型数组(array<int>/array<string>/array<float>/array<bool>/array<array<T>>/array<mixed>/array<Foo>callable void mixed self 类类型、局部变量/全局常量可选类型标记(类属性/类常量必填)
运算符 完整 15 级优先级:算术/比较/逻辑/位/三元 ?:/空合并 ??/太空船 <=>/自增自减/类型转换
命名空间 namespace A\Buse A\{B,C} 分组导入、use function A\{f1,f2} 组合式函数导入、use const A\{C1,C2} 组合式常量导入、use A\{B, function f, const C} 混合导入
语法糖 list()/$a[] = 解构、$a[] = push、[...$arr1, ...$arr2] 数组展开(spread)、int &$x 引用传参(全类型)、int $x = 10 默认值参数(编译时重载)、int $x = 42; 局部变量可选类型标记、const int MAX = 100; 全局常量可选类型标记、字符串插值、heredoc、魔术常量 (__LINE__ __FILE__ __DIR__)
Generator yieldyield $k => $vsend()getReturn()return、foreach 迭代(基于 minicoro 协程,不使用 yield 时零开销)
多线程 Thread/Mutex/CondVar/WaitGroup OOP 线程 API(基于 tinycthread,Thread-Local 运行时无锁竞争)
异步与协程 Channel/Future OOP 异步通信原语 + chan_select 多路复用(参考 vlang CSP 模型,自旋 750 次减少 syscall,环形缓冲区零 malloc)
注解 #[Attribute(p: type, ...)] const NAME = []; 声明 + #[NAME(args)] 使用(仅位置参数),ROUTE[0]->call()/newInstance() 编译期展开为零开销直接调用,详见 GRAMMAR.md §14

❌ 不支持(AOT 物理不可行)

特性 原因 替代方案
eval() 没有运行时解释器 switch/match 分支调度,或回调分发
$$var 可变变量 编译时不知道变量名 array 映射:$map[$key] 替代 $$key
include/require 没有运行时文件加载 #include 引入 C 头文件,或多文件编译
__call __get __set 没有运行时分发 显式定义方法,或用 switch 在单个方法内分发
$obj->{$method}() 编译时不知道方法名 回调 map:$fn = $map[$name]; $fn($args);

⬜ 不做(权衡决定)

特性 原因 替代方案
?int 可空类型 AOT 下 null 分支需要运行时分发 mixed 替代,或拆分为两个重载函数
callable $fn = "func" 默认值 编译时无法将字符串函数名转换为函数指针 每次调用时显式传入闭包

int|string 联合类型和 mixed 已支持。

⚠️ array 类型严格化

显式声明 array<T> 后,push 不同类型会触发编译错误(须用 array<mixed> 表达异构意图)。array<T> 传给 array<mixed> 参数时自动调用 O(n) 协变转换函数(tphp_fn_arr_int_to_var 等)。无注解的 array 默认推导为 array<mixed>,保持 PHP 动态语义。

⚠️ 与原生 PHP 的差异(Generator)

基于 minicoro 协程库实现,核心语义与 PHP 一致,但有以下 AOT 约束导致的差异:

差异点 原生 PHP TinyPHP 原因
callable 参数传字符串函数名 gen(1, 3, "apply") 可行 不可行,须用闭包 gen(1, 3, fn($x) => apply($x)) AOT 编译期无法将运行时字符串解析为函数符号
实现机制 Zend VM 内部 Generator 对象 minicoro 协程(ASM/ucontext/Fiber) AOT 无运行时 VM,需独立协程库
macOS + TCC 正常 pthread 线程模拟(性能略低于协程) TCC 的 ucontext/ASM 在 Apple Silicon 上不兼容,改用 pthread 线程模拟协程语义
不使用 yield 的函数 无差异 零开销,编译为普通函数 双函数变换:生成器函数 → 协程入口 + 包装器,普通函数不受影响

支持的 yield 形态:yield $vyield $k => $vreturn $v(配合 getReturn())、send($v) 双向传值。详见 FUNCTIONS.md

🔢 内置函数

已实现 450+ 个内置函数,覆盖 PHP 标准库的常用子集,覆盖数组/字符串/数学/时间/JSON/哈希/password(bcrypt)/进程控制/CSPRNG/ctype/正则表达式(PCRE NFA VM)/字符集转换(iconv)/过滤器(filter_var)/多线程(Thread/Mutex/CondVar/WaitGroup)/异步与协程(Channel/Future/chan_select,参考 vlang CSP 模型)/zlib(gzip 压缩+流式+增量上下文)/zip(归档读写)/stream(socket stream)/curl(HTTP/HTTPS 客户端,690 常量+35 函数)/pgsql(PostgreSQL 纯 C 协议,78 函数+60 常量)/pdo_pgsql(PostgreSQL PDO 驱动) 等。详见 FUNCTIONS.md

独有特性

AOT 类型固定

变量类型在首次赋值时确定,之后不可变。===== 等价——编译期已知类型,零运行时类型检查

COS 风格对象系统

对象头仅 16 字节(比 PHP 的 ~80B 精简 5 倍),struct 嵌套继承无开销,VTable 直接函数指针调用。作用域结束时自动调用 __destruct + 释放。

多层内存优化

机制 效果
SSO 小字符串 24B 内联缓冲区 ≤23 字节零堆分配
128KB 字符串池 bump allocator + Arena 溢出块 O(1) 分配,批量释放
128 槽数组复用池 LIFO + 1.5× 增长 热路径零 malloc
128 槽对象复用池 LIFO new+unset 提速 36-52%
ROPE 多片段拼接 编译期展平为单次分配 concat-4 提速 6 倍
Thread-Local 运行时 每线程独立 str_pool/arr_pool/obj_pool 多线程无锁竞争
泛型数组紧凑存储 array<T> 按 T 紧凑存储 value array<int>array<mixed> 节省 67% 内存(8B vs 24B/元素)

三编译器 + 四平台

TCC GCC Clang
Windows x86_64 ✅ 默认内置
Linux x86_64
Linux aarch64
macOS aarch64

TCC 亚秒编译,GCC/Clang -O2 带来 3-10 倍额外提速。compat.h 统一处理三编译器差异。

多线程支持

基于 tinycthread(zlib license)跨平台线程库,提供 OOP 风格线程 API。采用 Thread-Local 运行时策略:每个线程拥有独立的 str_pool/arr_pool/obj_pool,无锁竞争。

// Thread + join
$thread = new Thread(function(): int {
    return 42;
});
$thread->start();
echo $thread->join();  // 42

// WaitGroup 跨线程同步
$wg = new WaitGroup();
$wg->add(1);
$t = new Thread(function() use ($wg): int {
    $wg->done();
    return 0;
});
$t->start();
$wg->wait();
$t->join();

// Mutex 互斥
$mutex = new Mutex(false);
$mutex->lock();
// ... 临界区 ...
$mutex->unlock();

// 静态方法
Thread::yield();
Thread::sleep(0.5);
$tid = Thread::id();
方法 说明
Thread start/join/detach + 静态 yield/sleep/id 闭包跨线程传递(堆分配副本)
Mutex lock/tryLock/unlock recursive 选项:SRWLOCK(轻量)/ CRITICAL_SECTION(递归)
CondVar wait/signal/broadcast Windows CONDITION_VARIABLE / POSIX pthread_cond_t
WaitGroup add/done/wait 单 u64 state + Semaphore,等待 N 个线程完成

TCC+Windows 通过 compat/tls.h 用 Windows TLS API 实现真正的线程隔离(TCC 不支持 _Thread_local)。详见 FUNCTIONS.md

异步与协程通信

参考 vlang 的 CSP 通信模型,提供 Channel/Future OOP 异步原语 + chan_select 多路复用函数。基于 tinycthread 的 mutex + condvar 实现,采用自旋 750 次再阻塞的混合策略减少 syscall,Channel 使用固定容量环形缓冲区实现零 malloc。

// Channel 跨线程通信(CSP 风格)
$ch = new Channel(4);
$t = new Thread(function() use ($ch): int {
    $ch->push(42);
    $ch->close();
    return 0;
});
$t->start();
echo $ch->pop();   // 42
$t->join();

// Future 异步结果 + 链式回调
$f = Future::create();
$f->resolve(10);
$doubled = $f->then(fn(mixed $x): mixed => $x * 2);
echo $doubled->await();  // 20

// chan_select 多路复用(带超时)
$ch1 = new Channel(4);
$ch2 = new Channel(4);
$ch2->push("hello");
$idx = chan_select([$ch1, $ch2], 100);  // 1
类 / 函数 方法 说明
Channel push/pop/tryPush/tryPop/close/isClosed/length/capacity 有界通道,环形缓冲区零 malloc,close 后 push 抛 ChannelClosedException
Future create/resolve/reject/await/isReady/isRejected/then/catch + 静态 all/race 一次性异步结果,await reject 抛 FutureRejectedException,支持链式回调与组合器
chan_select (array $channels, int $timeout_ms = -1): int 返回就绪通道索引,全关闭返回 -2,超时返回 -1

内存安全:push/resolve 时 _arr_val_retain,pop/await 时不额外 retain,dtor 保证即使忘记 close 也释放所有资源。详见 FUNCTIONS.md 异步与协程通信章节。

C 互操作(PHPC)

完整的 PHP ↔ C 双向互操作:C->function(args) 直接调用 C 函数,C->CONST 直接访问 C 枚举/宏常量,C.Type 类型注解支持 C 类型作为函数参数/返回值,c_int/c_str 类型桥接,数组/对象/回调互操作。详见下方 PHPC 章节。

扩展系统

对标 PHP extension,#import 按需引入。已内置 pcntlposixstreamopensslcurlsqlite3pdo(SQLite+MySQL+PostgreSQL)、pgsqlpdo_pgsqlzlibzipiconvexiffileinfocalendarfilterhashgd 扩展:

<?php
#import pcntl            // 引入进程控制扩展

class Main {
    public function main(): void {
        $pid = pcntl_fork();
    }
}

C 互操作(PHPC)

完整实现:include/phpc.h(~240 行),通过 #include/#flag/C->call/C->CONST/C.Type/c_*/php_*/phpc_* 实现 PHP ↔ C 双向互操作。所有 phpc 函数为全局函数,不受命名空间 mangle。测试:test/phpc/

编译控制

#include "include/demo.h"                      // 项目头文件 → #include "include/demo.h"
#include Linux "linux_only.h"                  // 仅 Linux 引入
#include Windows <windows.h>                   // 仅 Windows 引入
#include __DIR__ . "/../demo.h"                // 相对源文件目录向上
#include __EXT__ . "/demo/src/demo.h"          // ext/ 目录引用
#include __INC__ . "/common.h"                 // include/ 目录引用
#include __CMD__ . "/local_lib.h"              // 当前工作目录引用
#include __DIR__ . DIRECTORY_SEPARATOR . "x.h" // 跨平台路径分隔
#include <math.h>                              // 系统头文件 → #include <math.h>
#flag Linux -lm                  // Linux 链接数学库
#flag GCC -O2 -DNDEBUG           // 仅 GCC 优化
#flag Clang -Wall -Werror        // 仅 Clang 严格警告

#include 路径中支持 PHP 魔术常量展开:

常量 展开为 示例
__DIR__ 源文件所在目录(绝对路径) __DIR__ . "/../demo.h"
__EXT__ 编译器 ext/ 目录 __EXT__ . "/pcntl/src/pcntl.h"
__INC__ 编译器 include/ 目录 __INC__ . "/common.h"
__CMD__ 执行 tphp 的当前工作目录 __CMD__ . "/my_lib.h"tphp . 时有用)
DIRECTORY_SEPARATOR /(Linux/macOS)或 \(Windows) 跨平台路径拼接

展开后经 realpath() 解析并校验在项目根目录内。#include "path"#include <name> 的引号/尖括号格式不受影响,原样通过。

指令 语法 编译器输出 去重
#include [OS] "path" 项目相对路径,可选 OS 过滤 #include "path" 按文件名去重
#include [OS] <name> 系统头文件,可选 OS 过滤 #include <name> 同上
#flag [CC] [OS] flags 编译器+平台过滤 仅匹配时追加到命令行 按标志串去重

[OS] 可选:Windows/Linux/MacOS。不写 = 全平台。#include#flag 共用同一过滤规则,MacOS 映射到 Darwin

#flag / #include / #import 安全模型

为防止注入攻击,所有预处理指令受以下安全约束:

指令 机制 说明
#flag Shell 元字符阻断 ` $ | ; & > < \n \\ 直接报错
Flag 前缀白名单 -I -L -l -D -U -O -W -std -m -f -g -pthread -static -shared -B
危险 Flag 黑名单 -fplugin / -specs / -wrapper / -ld= 直接报错(防 GCC 插件注入)
路径规范化 -I/-L 路径经 realpath() 消解 ../
#include realpath + 边界校验 项目头路径经 realpath() 解析后验证在项目根目录内
系统头白名单 #include <...> 仅允许标准 C 库 + 常见 POSIX/Windows 头(防任意引入系统 API)
#import 扩展名白名单 正则 \w[\w\-]* 仅接受字母/数字/下划线/连字符
工作区边界校验 realpath() 后验证路径在 ext/ 目录内

对标 Vlang 的 @DIR 变量,TinyPHP 的 __DIR__ 魔术常量在编译时展开,可在 #include 中用于跨目录引用。

基础类型桥接

PHP → C:                   C → PHP:
c_int($x)   → int32_t      php_int(v)       → t_int
c_str($s)   → const char*  php_str(s)       → t_string (深拷贝,复用 C 内存)
c_void_ptr  → void*        php_str_clone(s) → t_string (深拷贝,明确克隆语义)

c_float/php_float 已移除 — t_float 就是 double,转换无意义,float 类型直接传递即可。

C 类型注解

借鉴 vlang 的 C.Type 命名空间设计,函数参数和返回值可直接使用 C 类型注解,编译期映射为对应 C 类型:

#include "include/demo.h"

// C.Point* 返回类型 → Point*
function create_origin(): C.Point* {
    return C->point_origin();
}

// C.Point* 参数 + C.double 返回 → Point* / double
function get_point_x(C.Point* $p): C.double {
    return C->point_get_x($p);
}

// C.char* 参数 + C.int 返回 → char* / int
function square_name(string $name): int {
    return php_int(C->int_square(c_int(42)));
}
C 类型注解 映射为 C 类型 说明
C.int int C int
C.int32 / C.int64 int32_t / int64_t 固定宽度整数
C.uint32 / C.uint64 uint32_t / uint64_t 无符号整数
C.float / C.double double 浮点数
C.char char 单字符
C.bool bool 布尔值
C.void void 无返回值
C.void* void* 通用指针
C.char* char* C 字符串指针
C.int* / C.double* int* / double* 整数/浮点指针
C.XXX XXX 结构体值类型
C.XXX* XXX* 结构体指针(如 C.Point*Point*

C.Type 注解让 C 边界在函数签名中一目了然,编译器自动处理类型映射,无需手动 c_int / php_int 转换。

直接 C 调用:C->function(args) → 生成原生 function(args),无 tphp_fn_ 前缀。

直接 C 常量/枚举/宏访问:C->CONST(无括号)→ 生成原生 C 标识符,按 t_int 推断类型。需配合 #include "header.h" 引入定义。

#include "include/cconst.h"

class Main {
    public function main(): void {
        var_dump(C->COLOR_RED);    // C 枚举值 → int(0)
        var_dump(C->MAX_SIZE);     // C #define 宏 → int(1024)
        $total = C->COLOR_RED + C->COLOR_GREEN;  // 表达式中使用
    }
}

数组互操作

严格 C 风格phpc_arr_int($arr) 要求所有元素为 TYPE_INT,否则抛 tp_throw 异常(可被 try-catch 捕获)。phpc_arr_dbl 接受 intfloat

// 模式: 提取 → C 操作
// phpc_arr_int/dbl 自动注册到运行时(程序结束/异常自动释放),无需手动 phpc_free
function sum_array(array $arr): int {
    C.int32_t* $data = phpc_arr_int($arr);            // → int32_t* (malloc, 自动注册)
    $result = C->sum_ints($data, c_int(count($arr))); // C 操作
    return php_int($result);
}

// phpc_arr_str 不自动注册,需用 defer 在所有退出路径释放
function join_strs(array $arr): string {
    C.char** $data = phpc_arr_str($arr);              // → char** (malloc, 不自动注册)
    defer phpc_free_str_arr($data, c_int(count($arr))); // 函数退出自动释放
    C.char* $r = C->join_strs($data, c_int(count($arr)));
    return php_str($r);
}
PHP → C 要求 返回
phpc_arr_int($arr) 全部 TYPE_INT int32_t* (malloc)
phpc_arr_dbl($arr) TYPE_INT 或 TYPE_FLOAT double* (malloc)
phpc_arr_str($arr) 全部 TYPE_STRING char** (malloc,每个字符串独立分配)
C → PHP 说明
phpc_new_arr_int(src, len) int32_t[]t_array*
phpc_new_arr_dbl(src, len) double[]t_array*
phpc_new_arr_str(src, len) char*[]t_array*
phpc_new_arr() 空数组

对象互操作

TinyPHP 对象 = t_object 头部 + 字段,phpc_obj 提取底层 C 结构体指针:

class MyPoint { public float $x; public float $y; }

function obj_read_x(MyPoint $p): float {
    $ptr = phpc_obj($p);                 // → void* (即 tphp_class_MyPoint*)
    return C->read_field($ptr, c_int(16)); // offsetof(x)
}
函数 方向 说明
phpc_obj($obj) PHP→C 提取底层 C 结构体指针(void*,借用语义)
phpc_new_obj(ptr, vtable) C→PHP 包裹 C 指针为 PHP 对象(vtable 管理析构,接管语义)
phpc_unregister_obj($ptr) 双向 解除对象注册(C 库自行 free 时调用,防 double-free)

回调互操作

有 env 回调phpc_fn_i32 + phpc_env

$square = function(int $x): int { return $x * $x; };
$result = C->apply_closure(
    phpc_fn_i32($square),   // → int32_t(*)(int32_t, void*)
    phpc_env($square),      // → void* (env)
    c_int(5)
);
// C 侧: int64_t apply_closure(int32_t (*fn)(int32_t, void*), void* env, int32_t val)
函数 返回类型
phpc_fn_i32($cb) int32_t(*)(int32_t, void*)
phpc_fn_i64($cb) int64_t(*)(int64_t, void*)
phpc_fn_f64($cb) double(*)(double, void*)
phpc_fn($cb) / phpc_env($cb) void*(通用)

无 env 回调#callback + phpc_thunk()(thunk 嵌入 env,任意签名):

#callback double fold_cb(int32_t idx, double val)  // 声明 C 回调签名

C->fold_dbl($data, $len, phpc_thunk('fold_cb', $fn));  // 按签名生成 thunk
// thunk: static double _thunk_N(int32_t idx, double val) { ... env嵌入 ... }
函数 说明
phpc_new_fn(func)t_callback C 函数指针 → t_callback
phpc_new_fn_env(func, env) 带环境版本

所有权与内存安全

所有 PHPC 函数按所有权分三类,搞错会导致 double-free 或内存泄漏

错误处理

phpc_arr_* 类型不匹配时抛 tp_throw 异常(基于 setjmp/longjmp),可被 TinyPHP 的 try-catch 捕获,不再 exit(1) 强制退出:

try {
    $data = phpc_arr_int([1, "two", 3]);  // 元素 "two" 不是 int
} catch (\Throwable $e) {
    echo "Caught: " . $e->getMessage();   // 捕获异常
}

C->func() 段错误不可恢复,仍会导致进程崩溃。C 函数返回 NULL/错误码时需调用方手动检查,无统一约定。

PHP → C(调用方负责释放)

函数 返回类型 所有权
c_int($x) int32_t 值拷贝,无所有权
c_str($s) const char* 借用指针 ❌ 不可 free
phpc_arr_int($arr) int32_t* malloc自动注册,无需 phpc_free(循环内避免堆积可手动 phpc_free
phpc_arr_dbl($arr) double* malloc自动注册,无需 phpc_free(同上)
phpc_arr_str($arr) char** malloc ⚠ 不自动注册,用 defer phpc_free_str_arr() 释放
phpc_obj($obj) void* 借用指针 ❌ 不可 free
phpc_fn($cb) / phpc_env($cb) void* 借用,无所有权
phpc_fn_i32/i64/f64($cb) 函数指针 借用,无所有权
C->malloc(...) / C 库返回的 T* T* transfer — 用 defer C->free($p)defer C->fclose($f) 释放

C → PHP(TinyPHP 自动管理)

函数 返回类型 所有权
php_int(v) t_int 值拷贝
php_str(s) t_string 深拷贝,字符串池自动释放
php_str_clone(s) t_string 深拷贝,明确克隆语义(与 c_str 对照)
phpc_new_arr_*(src, n) t_array* 引用计数,自动 GC
phpc_new_arr() t_array* 引用计数,自动 GC
phpc_new_obj(ptr, cls) void* TinyPHP 析构链管理
phpc_unregister_obj(ptr) void 解除注册(C 库自行 free 后调用)
phpc_obj_steal(obj) void 标记对象"已分离",C 库可安全 free(防 double-free)
phpc_new_fn(func) t_callback 值拷贝
phpc_new_fn_env(fn, env) t_callback 值拷贝
phpc_env_pin(cb) void* 固定闭包 env(异步回调安全)
phpc_env_unpin(env) void 解除固定

释放函数

函数 说明
phpc_free(ptr) free(ptr),NULL 安全,自动置零变量防 UAF
phpc_free_str_arr(strs, len) free 每个字符串,再 free 指针数组,自动置零

安全辅助 API

函数 说明
phpc_assert_ptr(ptr, name) 断言指针非 NULL,NULL 时抛 tp_throw 异常(可 try-catch)
phpc_obj_steal(obj) 标记对象"已分离"(refcount=-1),C 库可安全 free 防 double-free
phpc_env_pin(cb) 固定闭包 env 防止 PHP 侧释放(异步回调安全)
phpc_env_unpin(env) 解除固定(C 库不再需要回调时调用)

记忆口诀phpc_arr_int/phpc_arr_dbl自动注册,你别管。phpc_arr_str → malloc 不自动注册 → defer phpc_free_str_arrphpc_new_*(创建)→ TinyPHP GC → 你别管c_str / phpc_obj → 借用 → 别 free。C 库返回的 T* (malloc/new) → defer C->free / defer C->fclose。C 库要 free 借用指针 → phpc_obj_steal。异步回调 → phpc_env_pin

💡 defer 是首选清理方式defer C->free($p); / defer C->fclose($f); / defer phpc_free_str_arr($a, $n); — 编译期展开到所有退出路径,零运行时开销,自动避免遗漏。详见 GRAMMAR.md §7.1

性能

PHP 8.5.1 vs TinyPHP(GCC -O2),详见 BENCHMARK_RESULTS.md

  • 数组遍历/读取: 18-36x PHP,方法调用近乎 0ns
  • OOP 创建/写入: SSO + 对象池使 new+unset 反超 2.1x,prop write 反超 2.6x
  • 字符串拼接: ROPE 多片段展平,concat-4 快 6 倍
  • 编译器差距: GCC/Clang -O2 比 TCC 再快 3-10x,tphp -cc gcc 即可获得

已落地优化:SSO 小字符串、Arena Allocator、对象复用池、ROPE 拼接、implode O(N²)→O(N)、CodeGen 自动释放、str/int 键双哈希索引(O(n)→O(1))。

编译流水线

PHP → Lexer → Token[] → Parser → AST → CodeGenerator → .c → 编译器 → 二进制
                                    include/  (C 运行时头文件)
  • Lexer: 逐字符扫描,~75 种 Token,支持字符串插值/heredoc
  • Parser: 递归下降,运算符优先级完整
  • CodeGenerator: 访问者模式,生成类型安全的 C 代码
  • C 运行时: COS 风格对象系统(16B 头 + struct 嵌套继承),setjmp/longjmp 异常,ROPE 多片段字符串拼接,256 位 JSON 转义位图,128 槽数组/对象复用池,64KB 字符串池,compat.h TCC/GCC/Clang 三编译器兼容层
  • 编译器: 内置 TCC (mob 分支),支持 GCC/Clang

文档

文件 说明
FUNCTIONS.md 每个函数的实现细节与 PHP 差异
GRAMMAR.md 完整语法参考(基于 PHP 8.5 parser,标注支持程度)
BENCHMARK_RESULTS.md 性能基准数据
CONTRIBUTING.md 架构、扩展指南、安全规范
QUICK_START.md 5 分钟快速上手:加函数、跑测试、提 PR

运行基准

php bench/run_bench.php         # 默认 TCC
php bench/run_bench.php gcc     # GCC -O2
php bench/run_bench.php clang   # Clang -O2
php bench/run_bench.php gcc php # 同时对比原生 PHP

许可证

MIT

About

TinyPHP 一个 PHP AOT 编译器。将 PHP 代码转译为人类可读 C 代码,再经行编译。

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages