19 KiB
AOT 编译器类型系统说明
⚠️ 重要提示
AOT 编译器支持 6 种原生/高精度类型:
基础原生类型
- ✅
std::int- 原生整数类型 (zend_long, 8 字节) - ✅
std::float- 原生浮点类型 (double, 8 字节) - ✅
std::bool- 原生布尔类型 (bool, 1 字节)
高精度数值类型
- ✅
std::bigInt- 任意精度整数 (基于 GMPmpz_class) - ✅
std::decimal- 任意精度十进制数 (基于 libmpdec, ~50 位有效数字) - ✅
std::bigFloat- 任意精度浮点数 (基于 MPFR)
🎯 objval 编译期函数
使用场景
当从数组、函数返回值等来源获取对象时,变量会丢失类型上下文信息。此时需要使用 objval() 显式声明对象的类。
基本语法
<?php
// objval 接收两个参数:
// 1. 对象变量(必须是 PHP variable 表达式)
// 2. 类名(必须是字面量字符串)
$obj = objval($array['object'], 'ClassName');
典型场景
场景一:从数组提取对象
<?php
$data = [
'user' => new User(),
'product' => new Product(),
];
// ❌ 错误:类型丢失
$user = $data['user']; // AOT 无法推断类型
// ✅ 正确:使用 objval 声明类型
$user = objval($data['user'], 'User');
$product = objval($data['product'], 'Product');
场景二:函数返回对象
<?php
function get_object() {
return new stdClass();
}
// ❌ 类型丢失
$obj = get_object();
// ✅ 使用 objval 声明
$obj = objval(get_object(), 'stdClass');
场景三:工厂模式
<?php
class Factory {
public function create($type) {
switch ($type) {
case 'user':
return new User();
case 'product':
return new Product();
default:
throw new InvalidArgumentException("Invalid type");
}
}
}
$factory = new Factory();
// ✅ 明确指定返回的对象类型
$user = objval($factory->create('user'), 'User');
$product = objval($factory->create('product'), 'Product');
注意事项
⚠️ 必须使用字面量字符串:
<?php
// ✅ 正确:字面量类名
$obj = objval($value, 'MyClass');
// ❌ 错误:变量类名(编译期无法分析)
$className = 'MyClass';
$obj = objval($value, $className); // 编译错误
// ❌ 错误:常量类名(编译期可能无法解析)
const CLASS_NAME = 'MyClass';
$obj = objval($value, CLASS_NAME); // 可能失败
⚠️ 第一个参数必须是 variable 表达式:
<?php
// ✅ 正确:variable 表达式
$obj = objval($array['key'], 'MyClass');
$obj = objval($object->property, 'MyClass');
$obj = objval(get_object(), 'MyClass');
// ❌ 错误:非 variable 表达式
$obj = objval(new MyClass(), 'MyClass'); // 不需要
性能影响
- ✅
objval()是编译期函数 - ✅ 不会产生运行时开销
- ✅ 仅在编译阶段进行类型推断
- ✅ 生成的 C++ 代码与普通变量赋值相同
与 std:: 类型的区别
| 特性 | std::int/float/bool | objval |
|---|---|---|
| 用途 | 数值/布尔类型优化 | 对象类型声明 |
| 性能 | ⚡ 高性能(原生类型) | 🐢 标准(ZVAL) |
| 内存 | 8B/1B | 指针(16B+) |
| 时机 | 运行时优化 | 编译期推断 |
| 语法 | std::int(值) |
objval(变量,'类名') |
❌ 不支持的类型
以下类型不使用原生类型,仍然使用 ZVAL:
- ❌
std::string- 字符串使用 ZVAL (php::Str) - ❌
std::array- 数组使用 ZVAL (php::Array) - ❌
std::object- 对象使用 ZVAL (php::Object) - ❌ 其他所有类型 - 使用 ZVAL (php::Var)
类型映射表
| PHP 类型声明 | C++ 类型 | 底层实现 | 内存 | 性能 | 状态 |
|---|---|---|---|---|---|
int |
php::Int |
zend_long |
8B | ⚡ 高性能 | ✅ 原生 |
float |
php::Float |
double |
8B | ⚡ 高性能 | ✅ 原生 |
bool |
php::Bool |
bool |
1B | ⚡ 高性能 | ✅ 原生 |
bigInt |
php::Var (Box<BigInt>) |
mpz_class (GMP) |
~32B+ | 🐢 标准 | ✅ 装箱 |
decimal |
php::Var (Box<Decimal>) |
decimal::Decimal (libmpdec) |
~64B+ | 🐢 标准 | ✅ 装箱 |
bigFloat |
php::Var (Box<BigFloat>) |
mpfr_t (MPFR) |
~32B+ | 🐢 标准 | ✅ 装箱 |
string |
php::Str |
zend_string* |
指针 | 🐢 标准 | ❌ ZVAL |
array |
php::Array |
zval* |
指针 | 🐢 标准 | ❌ ZVAL |
object |
php::Object |
zend_object* |
指针 | 🐢 标准 | ❌ ZVAL |
mixed/无声明 |
php::Var |
zval |
16B | 🐢 标准 | ❌ ZVAL |
声明方式对比
| 类型 | C++ 实现 | 声明方式 | 内存 | 性能 | 状态 |
|---|---|---|---|---|---|
| int | php::Int |
std::int(值)function foo(int $x) |
8B | ⚡ 高性能 | ✅ 原生 |
| float | php::Float |
std::float(值)function foo(float $x) |
8B | ⚡ 高性能 | ✅ 原生 |
| bool | php::Bool |
std::bool(值)function foo(bool $x) |
1B | ⚡ 高性能 | ✅ 原生 |
| bigInt | php::Var (Box<BigInt>) |
std::bigInt(值) |
~32B+ | 🐢 标准 | ✅ 装箱 |
| decimal | php::Var (Box<Decimal>) |
std::decimal(值) |
~64B+ | 🐢 标准 | ✅ 装箱 |
| bigFloat | php::Var (Box<BigFloat>) |
std::bigFloat(值) |
~32B+ | 🐢 标准 | ✅ 装箱 |
| string | php::Str |
无function foo(string $x) |
指针 | 🐢 标准 | ❌ ZVAL |
| array | php::Array |
无function foo(array $x) |
指针 | 🐢 标准 | ❌ ZVAL |
| object | php::Object |
无function foo(object $x) |
指针 | 🐢 标准 | ❌ ZVAL |
| mixed | php::Var |
无function foo($x) |
16B | 🐢 标准 | ❌ ZVAL |
性能差异
原生类型(高性能)
function calculate(int $a, int $b): int {
return $a + $b; // 使用原生类型,性能提升 100-300 倍
}
ZVAL 类型(标准性能)
function process(string $name, array $data) {
// 使用 ZVAL,标准 PHP 性能
echo $name;
print_r($data);
}
使用建议
✅ 推荐使用原生类型的场景
- 数值密集计算
- 循环计数器
- 递归算法
- 性能关键路径
⚠️ 使用 ZVAL 的场景
- 字符串处理
- 数组操作
- 对象操作
- 通用业务逻辑
高精度数值类型:BigInt / Decimal / BigFloat
AOT 编译器支持三种高精度数值类型,用于处理超出 int64/double 精度的计算。
底层 C++ 库
| 类型 | C++ 库 | 关键头文件 |
|---|---|---|
| BigInt | GMP (libgmp-dev) |
<gmpxx.h>, phpx_big_int.h |
| Decimal | libmpdec (libmpdec-dev) |
<decimal.hh>, phpx_decimal.h |
| BigFloat | MPFR (libmpfr-dev) |
<mpfr.h>, phpx_big_float.h |
BigInt、Decimal、BigFloat 均继承自 php::Box,存储于 php::Variant 内部。它们属于"装箱类型"(Boxed Type),不像 Int/Float 那样直接映射为 C++ 标量,因此在声明和运算上有所不同。
声明与构造
use native_types;
// 从整数字面量构造 BigInt
$a = std::bigInt(100);
$b = std::bigInt("123456789012345678901234567890"); // 超长整数字符串
// 从字符串构造 Decimal(避免浮点精度丢失)
$c = std::decimal("123.456");
$d = std::decimal(42); // 也可从 int 构造
// 从 int / float / 字符串构造 BigFloat
$e = std::bigFloat(100.5);
$f = std::bigFloat(42);
$g = std::bigFloat("3.14159265358979323846");
重要:
std::bigInt()/std::decimal()/std::bigFloat()是编译期函数,在生成的 C++ 代码中直接构造对应类型,无运行时函数调用开销。
算术运算符
所有标准二元运算符均已重载:+、-、*、/、%(取模)、**(幂运算)。编译器将其映射为静态方法调用。
$a = std::bigInt(100);
$b = std::bigInt(200);
$sum = $a + $b; // → php::BigInt::add($a, $b)
$diff = $a - $b; // → php::BigInt::sub($a, $b)
$prod = $a * $b; // → php::BigInt::mul($a, $b)
$quot = $a / $b; // → php::BigInt::div($a, $b)
$mod = $a % $b; // → php::BigInt::mod($a, $b)
$pow = $a ** 3; // → php::BigInt::pow($a, 3)
// 一元取负
$neg = -$a; // → php::BigInt::neg($a)
类型提升:当 Big* 类型与 Int/Float 混合运算时,Int/Float 自动提升为对应的高精度类型。详见下文"二元运算类型提升规则"。
BigInt 除法:BigInt / BigInt 在 parseBinaryOp 中返回 BigInt(整数除法,同 PHP int 语义)。若需要高精度除法,应先将操作数转为 Decimal 或使用 BigInt::div 的 Decimal 结果。
比较运算符
所有标准比较运算符均可使用:<、>、<=、>=、==、!=、<=>(太空船)。
$a = std::bigInt(100);
$b = 200;
echo (int)($a < $b); // → php::BigInt::cmp($a, $b) < 0
echo (int)($a > $b); // → php::BigInt::cmp($a, $b) > 0
echo (int)($a == 100); // → php::BigInt::cmp($a, 100) == 0
echo (int)($a <=> $b); // → php::BigInt::cmp($a, $b)
C++ 实现:比较结果通过 php::BigInt::cmp() / php::Decimal::cmp() / php::BigFloat::cmp() 获得,返回负/零/正 int 表示小于/等于/大于。
通用方法 (Universal Methods)
BigInt、Decimal、BigFloat 支持通过 $value->method() 语法调用一系列零成本抽象方法。
BigInt 方法
| 方法 | 参数 | 返回类型 | C++ 实现 | 说明 |
|---|---|---|---|---|
add($x) |
1 | BigInt | BigInt::add() |
加法 |
sub($x) |
1 | BigInt | BigInt::sub() |
减法 |
mul($x) |
1 | BigInt | BigInt::mul() |
乘法 |
div($x) |
1 | BigInt | BigInt::div() |
整数除法 |
mod($x) |
1 | BigInt | BigInt::mod() |
取模 |
pow($x) |
1 | BigInt | BigInt::pow() |
幂运算 |
neg() |
0 | BigInt | BigInt::neg() |
取负 |
abs() |
0 | BigInt | BigInt::abs() |
绝对值 |
gcd($x) |
1 | BigInt | BigInt::gcd() |
最大公约数 |
cmp($x) |
1 | Int | BigInt::cmp() |
比较 |
toString() |
0 | Str | BigInt::toString() |
转字符串 |
toInt() |
0 | Int | BigInt::toInt() |
转整数 (可能截断) |
toFloat() |
0 | Float | BigInt::toFloat() |
转浮点 (可能丢精度) |
$a = std::bigInt("12345678901234567890");
echo $a->toString(); // "12345678901234567890"
echo $a->add(1)->toString(); // "12345678901234567891"
echo $a->abs()->toString(); // "12345678901234567890"
echo $a->gcd(15)->toInt(); // 15
Decimal 方法
| 方法 | 参数 | 返回类型 | C++ 实现 | 说明 |
|---|---|---|---|---|
add($x) |
1 | Decimal | Decimal::add() |
加法 |
sub($x) |
1 | Decimal | Decimal::sub() |
减法 |
mul($x) |
1 | Decimal | Decimal::mul() |
乘法 |
div($x) |
1 | Decimal | Decimal::div() |
除法 |
mod($x) |
1 | Decimal | Decimal::mod() |
取模 |
neg() |
0 | Decimal | Decimal::neg() |
取负 |
abs() |
0 | Decimal | Decimal::abs() |
绝对值 |
cmp($x) |
1 | Int | Decimal::cmp() |
比较 |
toString() |
0 | Str | Decimal::toString() |
转字符串 |
toInt() |
0 | Int | Decimal::toInt() |
截断取整 |
toFloat() |
0 | Float | Decimal::toFloat() |
转浮点 (约 15 位精度) |
$d = std::decimal("123.456");
echo $d->toInt(); // 123
echo $d->mul(2)->toString(); // "246.912"
echo $d->div(3)->toString(); // "41.152"
BigFloat 方法
| 方法 | 参数 | 返回类型 | C++ 实现 | 说明 |
|---|---|---|---|---|
add($x) |
1 | BigFloat | BigFloat::add() |
加法 |
sub($x) |
1 | BigFloat | BigFloat::sub() |
减法 |
mul($x) |
1 | BigFloat | BigFloat::mul() |
乘法 |
div($x) |
1 | BigFloat | BigFloat::div() |
除法 |
neg() |
0 | BigFloat | BigFloat::neg() |
取负 |
abs() |
0 | BigFloat | BigFloat::abs() |
绝对值 |
cmp($x) |
1 | Int | BigFloat::cmp() |
比较 |
toString() |
0 | Str | BigFloat::toString() |
转字符串 |
toInt() |
0 | Int | BigFloat::toInt() |
截断取整 |
toFloat() |
0 | Float | BigFloat::toFloat() |
转 double (约 15 位精度) |
$bf = std::bigFloat(3.14159265);
echo $bf->mul(2)->toString(); // "6.2831853..."
echo $bf->div(2)->toFloat(); // 1.570796325
echo $bf->cmp(3.0); // > 0
类型转换
// BigInt → Decimal(精确)
$big = std::bigInt("12345678901234567890");
$dec = std::decimal($big->toString());
// 或直接使用内置转换:
// $dec = $big->toDecimal(); // 待实现
// Decimal → BigInt(截断小数部分)
$d = std::decimal("123.456");
$i = std::bigInt($d->toInt()); // 123
// Int → BigInt
$bi = std::bigInt(42);
// Float → BigFloat
$bf = std::bigFloat(3.14);
// BigInt → BigFloat
$bf2 = std::bigFloat($big->toString());
跨类型隐式转换限制:BigFloat 与 BigInt/Decimal 之间不能隐式混合运算。编译器会报错提示使用
std::bigFloat()显式转换。这是为了防止意外的精度损失。
C++ API 参考
Big* 类型在 phpx 库中提供以下核心函数:
// 构造
php::Variant php::newBigInt(const std::string &s);
php::Variant php::newBigInt(php::Int v);
php::Variant php::newDecimal(const String &s);
php::Variant php::newDecimal(php::Int v);
php::Variant php::newBigFloat(const String &s);
php::Variant php::newBigFloat(php::Int v);
php::Variant php::newBigFloat(php::Float v);
// BigInt 算术(均返回 Variant)
php::BigInt::add(a, b) php::BigInt::sub(a, b) php::BigInt::mul(a, b)
php::BigInt::div(a, b) php::BigInt::mod(a, b) php::BigInt::pow(a, b)
php::BigInt::neg(a) php::BigInt::abs(a) php::BigInt::gcd(a, b)
php::BigInt::cmp(a, b)
// Decimal 算术
php::Decimal::add(a, b) php::Decimal::sub(a, b) php::Decimal::mul(a, b)
php::Decimal::div(a, b) php::Decimal::mod(a, b)
php::Decimal::neg(a) php::Decimal::abs(a) php::Decimal::cmp(a, b)
// BigFloat 算术
php::BigFloat::add(a, b) php::BigFloat::sub(a, b) php::BigFloat::mul(a, b)
php::BigFloat::div(a, b)
php::BigFloat::neg(a) php::BigFloat::abs(a) php::BigFloat::cmp(a, b)
// 类型转换
php::BigInt::toString(a) php::BigInt::toInt(a) php::BigInt::toFloat(a)
php::Decimal::toString(a) php::Decimal::toInt(a) php::Decimal::toFloat(a)
php::BigFloat::toString(a) php::BigFloat::toInt(a) php::BigFloat::toFloat(a)
所有静态方法接收 Variant 参数,内部通过 .toBox<BigInt>() / .toBox<Decimal>() / .toBox<BigFloat>() 提取底层对象。若参数类型不匹配,抛出运行时错误。
超长字面量识别
AOT 编译器在解析前会对 PHP 源码进行预扫描,自动识别超出 int64/double 精度的数值字面量:
\d{19,} → 自动转为 std::bigInt("...")
\d+\.\d{16,} → 自动转为 std::decimal("...")
例如源码中写 123456789000000000000000000000000000000000000000000001(54 位),编译器自动处理为 std::bigInt("123456789000000000000000000000000000000000000000000001"),无需手动包装。
二元运算类型提升规则
AOT 编译器在执行 +、-、*、/、% 等二元运算时,按以下优先级确定运算类型:
规则优先级
BigFloat / Decimal / BigInt 参与
→ 提升到最高精度类型进行计算
↓ 未命中
任一边为 Var
→ 两边均转为 Var,使用 ZendVM binary_op 函数
↓ 未命中
任一边为 Float
→ 两边均转为 Float (double)
↓ 未命中
两边均为 Int
→ 使用 Int (int64_t)
规则一:Var 主导
当运算数中至少有一边是 Var 类型(非 use native_types 声明),两边均作为 Var 处理,使用 ZendVM 的 add_function / div_function 等运算函数,完全遵循 PHP 原生类型转换(type juggling)语义。
$a = 10; // Var,存 int(10)
$b = 2.5; // Var,存 float(2.5)
$c = $a + $b; // 两边为 Var → ZendVM 运算 → float(12.5)
C++ 代码生成:int64_t 和 double 值通过 php::Variant 的模板构造函数(phpx.h:557)隐式转为 php::Var,再调用 Variant::operator+() → ZendVM 的 add_function。
规则二:Float 优先于 Int
当两边均为原生类型(通过 use native_types 或 std::int()/std::float() 声明),如果任一边是 Float,则两边均转为 Float 运算。仅当两边都是 Int 才使用整数运算。
use native_types;
$a = 10; // php::Int
$b = 2.5; // php::Float
$c = $a + $b; // Float + Float → double 加法
$d = 5; // php::Int
$e = 3; // php::Int
$f = $d + $e; // Int + Int → int64_t 加法
注意:原生类型变量在运算中不会改变自身类型。如
Int += Float在 C++ 中执行int64_t += double,结果截断为 int64_t,与 PHP 行为不同(PHP 中变量会变为 float)。这是use native_types有意为之的语义。
规则三:大数类型精度提升
当运算数中包含 BigInt、Decimal 或 BigFloat 时,按精度层级提升:BigFloat > Decimal > BigInt > Float > Int。
| 左操作数 | 右操作数 | 结果类型 |
|---|---|---|
| BigInt | BigInt | BigInt(除法 / 得 Decimal) |
| BigInt | Decimal | Decimal |
| Decimal | Decimal | Decimal |
| BigFloat | BigInt | BigFloat |
| BigFloat | Decimal | BigFloat |
| BigFloat | BigFloat | BigFloat |
| BigInt | Int | BigInt |
| BigInt | Float | Decimal |
| Decimal | Int | Decimal |
| Decimal | Float | Decimal |
| BigFloat | Int | BigFloat |
| BigFloat | Float | BigFloat |
类型提升完整矩阵
| Int | Float | Var | BigInt | Decimal | BigFloat | |
|---|---|---|---|---|---|---|
| Int | Int | Float | Var | BigInt | Decimal | BigFloat |
| Float | Float | Float | Var | Decimal | Decimal | BigFloat |
| Var | Var | Var | Var | Var | Var | Var |
| BigInt | BigInt | Decimal | Var | BigInt | Decimal | BigFloat |
| Decimal | Decimal | Decimal | Var | Decimal | Decimal | BigFloat |
| BigFloat | BigFloat | BigFloat | Var | BigFloat | BigFloat | BigFloat |
说明:Var 行/列全部为 Var,因为 Var 主导规则优先级最高(除 Big* 类型外)。Big* 类型参与时,Var 退让,以高精度类型为准。
复合赋值运算符
+=、-=、*=、/=、%= 等复合赋值运算符遵循相同的类型提升规则,但 RHS 会被转换为 LHS 变量的类型。若 LHS 为 Var,RHS 保持原类型(Var 的 operator+= 接管);若 LHS 为原生类型,RHS 显式转换为该类型。
$a = 10; // Var
$a += 2.5; // Var::operator+=(float) → ZendVM → $a 变为 float(12.5)
use native_types;
$b = 10; // php::Int
$b += 2.5; // int64_t += double → C++ 隐式截断 → $b = 12 (Int)
最后更新: 2026 年 5 月 26 日
适用版本: PHP AOT Compiler v1.x