From eb31e3584b8ee5f816fcd121689ae19b1b25fb1e Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 26 May 2026 20:03:03 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=20AOT=20=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E5=99=A8=E9=AB=98=E7=B2=BE=E5=BA=A6=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=92=8C=E9=80=9A=E7=94=A8=E6=96=B9=E6=B3=95=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=95=99=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 HIGH_PRECISION_TYPES.md 文档,详细介绍 BigInt、Decimal 和 BigFloat 三种高精度数值类型 - 新增 UNIVERSAL_METHODS.md 文档,全面介绍通用方法系统的使用方式和各类型支持的方法 - 包含高精度类型的构造、运算、转换等完整使用指南 - 提供通用方法的设计原理、Handler 类型和各类原生类型的方法详解 - 添加完整的使用示例和最佳实践说明 --- docs/HIGH_PRECISION_TYPES.md | 755 +++++++++++++++++++++++ docs/UNIVERSAL_METHODS.md | 1112 ++++++++++++++++++++++++++++++++++ 2 files changed, 1867 insertions(+) create mode 100644 docs/HIGH_PRECISION_TYPES.md create mode 100644 docs/UNIVERSAL_METHODS.md diff --git a/docs/HIGH_PRECISION_TYPES.md b/docs/HIGH_PRECISION_TYPES.md new file mode 100644 index 00000000..c6d6e05b --- /dev/null +++ b/docs/HIGH_PRECISION_TYPES.md @@ -0,0 +1,755 @@ +# AOT 编译器高精度类型使用教程 + +本教程介绍 AOT 编译器中的三种高精度数值类型——**BigInt**(任意精度整数)、**Decimal**(任意精度十进制数)和 **BigFloat**(任意精度浮点数)——帮助你编写高精度、零开销的数值计算程序。 + +## 目录 + +1. [为什么需要高精度类型](#1-为什么需要高精度类型) +2. [快速开始](#2-快速开始) +3. [三种类型概览](#3-三种类型概览) +4. [构造与声明](#4-构造与声明) +5. [算术运算](#5-算术运算) +6. [比较运算](#6-比较运算) +7. [复合赋值](#7-复合赋值) +8. [通用方法调用](#8-通用方法调用) +9. [类型转换](#9-类型转换) +10. [混合运算与类型提升](#10-混合运算与类型提升) +11. [超长字面量自动识别](#11-超长字面量自动识别) +12. [限制与注意事项](#12-限制与注意事项) +13. [完整示例](#13-完整示例) + +--- + +## 1. 为什么需要高精度类型 + +PHP 的原生 `int` 是 64 位有符号整数,最大值为 `9223372036854775807`(约 9.22×10¹⁸)。超过这个范围的整数字面量会被 PHP 解析器静默转换为 `float`(double),丢失有效位数。 + +PHP 的原生 `float`(IEEE 754 double)最多只能保证约 15–16 位有效数字。对于金融计算、科学计算、密码学等场景,这远远不够。 + +```php +// PHP 原生行为的精度问题 +$a = 123456789012345678901234567890; // 30 位整数 → 被转为 float,精度丢失 +// 实际存储:1.2345678901234568E+29,末尾数字已经不可靠 + +$b = 0.1 + 0.2; // 0.30000000000000004 — 经典的浮点误差 +``` + +AOT 编译器提供了三种高精度类型,底层基于成熟的 C/C++ 数学库,编译为本地机器码,**零运行时开销**: + +| 类型 | 底层库 | 特点 | +|------|--------|------| +| BigInt | GMP (`libgmp`) | 任意精度整数,不会溢出 | +| Decimal | libmpdec | 十进制小数,约 50 位有效数字,无二进制浮点误差 | +| BigFloat | MPFR (`libmpfr`) | 任意精度浮点数,可调精度 | + +--- + +## 2. 快速开始 + +使用高精度类型的前提条件: + +1. 文件头部声明 `declare(strict_types=1)` +2. 导入原生类型声明 `use native_types` +3. 系统已安装对应的 C++ 库(`libgmp-dev`、`libmpdec-dev`、`libmpfr-dev`) + +```php +toString(); +} +?> +``` + +编译运行: + +```bash +php bin/compiler.php my_program.php -o my_program +./my_program +``` + +> **提示**:和所有 native_types 一样,Big* 类型只能在 AOT 编译模式下使用,不能在普通 PHP 解释器中运行。AOT 编译器会对 `std::bigInt()` 等函数进行编译期求值,直接生成 C++ 代码。 + +--- + +## 3. 三种类型概览 + +### BigInt — 任意精度整数 + +适用于大整数计算,不会溢出,不会丢失精度。整数除法结果为整数(截断)。 + +```php +$a = std::bigInt("1234567890123456789012345678901234567890"); // 40 位 +$b = $a * 2; // 80 位,不会溢出 +``` + +### Decimal — 任意精度十进制数 + +适用于金融计算等需要精确十进制表示的场景。`0.1 + 0.2` 精确等于 `0.3`,不存在二进制浮点误差。 + +```php +$price = std::decimal("19.99"); +$quantity = 3; +$total = $price * $quantity; // 59.97,精确 +``` + +### BigFloat — 任意精度浮点数 + +适用于科学计算等需要高精度浮点运算的场景。基于 MPFR,使用二进制浮点但精度远超 IEEE 754 double。 + +```php +$pi = std::bigFloat("3.141592653589793238462643383279502884197"); +$area = $pi * 100 * 100; // 高精度 π × r² +``` + +--- + +## 4. 构造与声明 + +### 4.1 从字面量构造 + +`std::bigInt()`、`std::decimal()`、`std::bigFloat()` 是**编译期函数**,在生成的 C++ 代码中直接构造对应的 C++ 对象,不产生运行时函数调用。 + +```php +// BigInt — 从 int 或字符串构造 +$a = std::bigInt(100); // 普通整数 +$b = std::bigInt("123456789012345678901234567890"); // 超长整数,必须用字符串 + +// Decimal — 建议从字符串构造以避免浮点精度丢失 +$c = std::decimal("123.456"); // ✅ 推荐:精确字符串 +$d = std::decimal(42); // ✅ 可行:从 int + +// BigFloat — 从 int、float 或字符串构造 +$e = std::bigFloat(100.5); // 从 float +$f = std::bigFloat(42); // 从 int +$g = std::bigFloat("3.14159265358979323846"); // 从字符串(精确) +``` + +### 4.2 类型标注 + +在 `use native_types` 下,Big* 类型变量自动获得原生 C++ 存储类型: + +```php +use native_types; + +// 编译器自动推断类型为 php::BigInt / php::Decimal / php::BigFloat +$a = std::bigInt(100); // → C++: php::Variant(new BigInt(100)) +$b = std::decimal("100.50"); // → C++: php::Variant(new Decimal("100.50")) +$c = std::bigFloat(3.14); // → C++: php::Variant(new BigFloat(3.14)) +``` + +> **关键细节**:Big* 类型是**不可变(immutable)**的。每次运算都返回新值,不会修改原变量。详见 [第 7 节:复合赋值](#7-复合赋值)。 + +--- + +## 5. 算术运算 + +### 5.1 标准运算符 + +所有标准二元运算符都可以直接用于 Big* 类型: + +```php +$a = std::bigInt(100); +$b = std::bigInt(200); + +$sum = $a + $b; // 加法 +$diff = $a - $b; // 减法 +$prod = $a * $b; // 乘法 +$quot = $a / $b; // 除法(BigInt 为整数除法) +$mod = $a % $b; // 取模 +$pow = $a ** 10; // 幂运算(BigInt 支持) + +// 一元取负 +$neg = -$a; // 取负 +``` + +生成的 C++ 代码示例(`$a + $b`): + +```cpp +php::BigInt::add(a, b) // BigInt 加法 +php::BigInt::sub(a, b) // BigInt 减法 +php::BigInt::mul(a, b) // BigInt 乘法 +php::BigInt::div(a, b) // BigInt 除法 +php::BigInt::mod(a, b) // BigInt 取模 +php::BigInt::pow(a, b) // BigInt 幂运算 +``` + +### 5.2 与 int / float 混合运算 + +Big* 类型可以自由地与普通 int 和 float 混合运算,编译器自动进行类型提升: + +```php +$a = std::bigInt(100); + +$b = $a + 50; // BigInt + Int → BigInt +$c = 200 + $a; // Int + BigInt → BigInt +$d = $a * 3.5; // BigInt * Float → 编译错误! + // 浮点数无法精确提升为 BigInt, + // 应使用 Decimal 或 BigFloat +``` + +### 5.3 BigInt 除法注意事项 + +`BigInt / BigInt` 是整数除法(截断),类似于 PHP 的 `intdiv()`: + +```php +$a = std::bigInt(100); +$b = $a / 3; // 33(不是 33.333...) +``` + +如果需要精确的小数结果,先将操作数转换为 Decimal: + +```php +$a = std::bigInt(100); +$result = std::decimal($a->toString()) / std::decimal("3"); +// 33.333333333... +``` + +### 5.4 各类型支持的运算符汇总 + +| 运算符 | BigInt | Decimal | BigFloat | +|--------|--------|---------|----------| +| `+` `-` `*` | ✅ | ✅ | ✅ | +| `/` | ✅ 整数除法 | ✅ | ✅ | +| `%` | ✅ | ✅ | ❌ | +| `**` | ✅ | ❌ | ❌ | +| `-` (一元取负) | ✅ | ✅ | ✅ | + +--- + +## 6. 比较运算 + +所有六种比较运算符均可用于 Big* 类型: + +```php +$a = std::bigInt(100); +$b = 200; + +// 比较运算返回 bool(需要 (int) 转换输出) +echo (int)($a < $b); // 1 (true) → cmp(a,b) < 0 +echo (int)($a > $b); // 0 (false) → cmp(a,b) > 0 +echo (int)($a <= 100); // 1 (true) → cmp(a,b) <= 0 +echo (int)($a >= 100); // 1 (true) → cmp(a,b) >= 0 +echo (int)($a == 100); // 1 (true) → cmp(a,b) == 0 +echo (int)($a != 50); // 1 (true) → cmp(a,b) != 0 + +// 太空船运算符 +$cmp = $a <=> $b; // -1 ($a < $b) +echo (int)$cmp; // -1 +``` + +生成的 C++ 代码示例: + +```cpp +php::BigInt::cmp(a, b) < 0 // a < b +php::BigInt::cmp(a, b) == 0 // a == b +php::BigInt::cmp(a, b) != 0 // a != b +php::BigInt::cmp(a, b) // a <=> b(直接返回 -1/0/1) +``` + +--- + +## 7. 复合赋值 + +Big* 类型**支持** `+=`、`-=`、`*=`、`/=`、`%=` 等复合赋值运算符。 + +### 7.1 工作方式 + +Big* 类型是**不可变的**(immutable)。`$a += 50` 在编译时被展开为 `$a = BigInt::add($a, 50)`——创建一个新值然后赋值给原变量。 + +```php +$a = std::bigInt(100); +$a += 50; // → a = php::BigInt::add(a, php::newBigInt(50)) +echo $a->toString(); // "150" + +$a -= 30; // → a = php::BigInt::sub(a, php::newBigInt(30)) +$a *= 5; // → a = php::BigInt::mul(a, php::newBigInt(5)) +$a /= 3; // → a = php::BigInt::div(a, php::newBigInt(3)) +$a %= 7; // → a = php::BigInt::mod(a, php::newBigInt(7)) +``` + +Decimal 和 BigFloat 同样支持: + +```php +// Decimal 复合赋值 +$d = std::decimal("100.50"); +$d += 25.25; // → d = php::Decimal::add(d, php::newDecimal("25.25")) +$d -= 123.45; // → d = php::Decimal::sub(d, php::newDecimal("123.45")) +$d *= 2; // → d = php::Decimal::mul(d, php::newDecimal(2)) +$d /= 4; // → d = php::Decimal::div(d, php::newDecimal(4)) +$d %= 5.0; // → d = php::Decimal::mod(d, php::newDecimal("5.0")) + +// BigFloat 复合赋值(不支持 %=) +$bf = std::bigFloat(100.0); +$bf += 50.0; +$bf -= 30.0; +$bf *= 2.0; +$bf /= 3.0; +``` + +### 7.2 `++` / `--` 不可用 + +由于 Big* 类型是不可变的,`++` / `--` 运算符在语义上不匹配。编译器会给出清晰的错误提示: + +```php +$a = std::bigInt(100); +$a++; // ❌ 编译错误:Cannot use ++ on php::BigInt. Use += 1 instead. +++$a; // ❌ 编译错误:Cannot use ++ on php::BigInt. Use += 1 instead. +--$a; // ❌ 编译错误:Cannot use -- on php::BigInt. Use -= 1 instead. +``` + +正确的替代写法: + +```php +$a += 1; // ✅ 代替 $a++ +$a -= 1; // ✅ 代替 $a-- +``` + +--- + +## 8. 通用方法调用 + +Big* 类型支持通过 `$value->method()` 语法(通用方法/Universal Methods)调用方法。这些调用在编译时被翻译为对应的 C++ 静态函数,**零运行时开销**。 + +### 8.1 BigInt 方法 + +```php +$a = std::bigInt("12345678901234567890"); + +// 算术方法(均返回新的 BigInt) +$b = $a->add(1); // 加法:$a + 1 +$c = $a->sub(1); // 减法:$a - 1 +$d = $a->mul(2); // 乘法:$a * 2 +$e = $a->div(10); // 除法:$a / 10 +$f = $a->mod(1000000); // 取模:$a % 1000000 +$g = $a->pow(3); // 幂运算:$a ** 3 + +// 一元方法 +$h = $a->neg(); // 取负:-$a +$i = $a->abs(); // 绝对值 + +// 特殊方法 +$j = $a->gcd(15); // 最大公约数:gcd($a, 15) + +// 比较方法 +$cmp = $a->cmp(100); // 比较:返回 -1/0/1 +if ($a->cmp(100) > 0) { /* $a > 100 */ } + +// 类型转换方法 +echo $a->toString(); // 转字符串:"12345678901234567890" +echo $a->toInt(); // 转 int(可能截断) +echo $a->toFloat(); // 转 float(可能丢精度) +``` + +### 8.2 Decimal 方法 + +```php +$d = std::decimal("123.456"); + +// 算术方法 +echo $d->add(std::decimal("50.25"))->toString(); // "173.706" +echo $d->sub(std::decimal("50.25"))->toString(); // "73.206" +echo $d->mul(2)->toString(); // "246.912" +echo $d->div(3)->toString(); // "41.152" +echo $d->mod(std::decimal("5.0"))->toString(); // "3.456" + +// 一元方法 +echo $d->neg()->toString(); // "-123.456" +echo $d->abs()->toString(); // "123.456" + +// 比较与转换 +echo $d->cmp(std::decimal("100")) > 0 ? "greater" : "less"; // "greater" +echo $d->toInt(); // 123 +echo $d->toString(); // "123.456" +``` + +### 8.3 BigFloat 方法 + +```php +$bf = std::bigFloat(3.14159265); + +echo $bf->add(1.0)->toString(); // "4.14159265..." +echo $bf->mul(2.0)->toString(); // "6.2831853..." +echo $bf->div(2.0)->toFloat(); // 1.570796325 +echo $bf->neg()->toString(); // "-3.14159265..." +echo $bf->abs()->toString(); // "3.14159265..." + +// 比较 +echo $bf->cmp(3.0); // > 0($bf > 3.0) +``` + +### 8.4 通用方法 vs 运算符 + +运算符和方法调用在功能上等价,选择哪种取决于代码风格: + +```php +$a = std::bigInt(100); +$b = std::bigInt(50); + +// 两种等价写法 +$result1 = $a + $b; // 运算符风格 +$result2 = $a->add($b); // 方法调用风格 + +// 方法调用支持链式调用 +$result3 = $a->add(10)->mul(2)->sub(5)->toString(); // "215" +``` + +--- + +## 9. 类型转换 + +### 9.1 Big* 之间的转换 + +```php +// BigInt → Decimal(精确,推荐方式) +$big = std::bigInt("12345678901234567890"); +$dec = std::decimal($big->toString()); + +// Decimal → BigInt(截断小数部分) +$d = std::decimal("123.456"); +$i = std::bigInt($d->toInt()); // 123 + +// Int → BigInt / Decimal / BigFloat +$bi = std::bigInt(42); +$dc = std::decimal(42); +$bf = std::bigFloat(42); + +// Float → BigFloat(Float → Decimal 不推荐直接使用 float 字面量) +$bf2 = std::bigFloat(3.14); + +// 任意类型 → BigFloat +$bf3 = std::bigFloat($big->toString()); +``` + +### 9.2 Big* 与普通类型的转换 + +```php +// BigInt → 普通类型 +$a = std::bigInt("99999999999999999999"); +$s = $a->toString(); // "99999999999999999999" +$i = $a->toInt(); // PHP_INT_MAX(超出范围时截断) +$f = $a->toFloat(); // 1.0E+20(可能丢失精度) + +// 普通类型 → BigInt(通过编译期函数) +$b = std::bigInt(42); // int → BigInt +$c = std::bigInt("123456..."); // string → BigInt +``` + +### 9.3 跨类型隐式混合的限制 + +编译器会阻止可能导致精度损失的跨类型隐式混合运算: + +```php +$a = std::bigFloat(100.5); +$b = std::bigInt(200); + +$c = $a + $b; // ❌ 编译错误:Cannot mix BigFloat and BigInt implicitly. + // Use std::bigFloat() to convert explicitly. + +// 正确的做法:显式转换 +$c = $a + std::bigFloat($b->toString()); // ✅ +``` + +| 组合 | 是否允许 | 说明 | +|------|---------|------| +| BigInt + BigFloat | ❌ 编译错误 | 精密度量不同,需显式转换 | +| BigInt + Decimal | ❌ 编译错误 | 精密度量不同,需显式转换 | +| BigFloat + Decimal | ❌ 编译错误 | 精密度量不同,需显式转换 | +| BigInt + Int | ✅ 自动提升 Int → BigInt | 无精度损失 | +| BigInt + Float | ❌ 编译错误 | Float 无法精确提升为 BigInt | +| Decimal + Int | ✅ 自动提升 Int → Decimal | 无精度损失 | +| Decimal + Float | ✅ 自动提升 Float → Decimal | 可能有微小误差 | +| BigFloat + Int | ✅ 自动提升 Int → BigFloat | 无精度损失 | +| BigFloat + Float | ✅ 自动提升 Float → BigFloat | 无精度损失 | + +--- + +## 10. 混合运算与类型提升 + +当 Big* 类型与普通 Int/Float 混合运算时,编译器按优先级确定运算类型: + +``` +BigFloat > Decimal > BigInt > Float > Int +``` + +**规则**: + +1. 若任一操作数是 Var(非原生类型),则全部转为 Var,使用 ZendVM 运行时运算 +2. 若两操作数均为 Int/Float,则 Float 优先(Int → Float) +3. 若任一操作数为 Big* 类型,则另一操作数自动提升为同类型(Int → BigInt 等) + +```php +// 类型提升示例 +$a = std::bigInt(100); +$b = 50; // Int + +$c = $a + $b; // BigInt + Int → BigInt + // $b 自动提升为 BigInt + +$d = std::decimal("10.5"); +$e = $d + 3; // Decimal + Int → Decimal + // 3 自动提升为 Decimal + +$f = std::bigFloat(1.5); +$g = $f + 2.0; // BigFloat + Float → BigFloat + // 2.0 自动提升为 BigFloat +``` + +--- + +## 11. 超长字面量自动识别 + +AOT 编译器会自动检测超出原生类型精度的数值字面量,并自动转为对应的 Big* 类型。你**不需要手动包装**。 + +```php +// 19 位以上整数 → 自动转为 BigInt +$a = 12345678901234567890; +echo $a->toString(); // "12345678901234567890" +// 编译器自动处理:等同于 std::bigInt("12345678901234567890") + +// 16 位有效数字以上的小数 → 自动转为 Decimal +$b = 3.14159265358979323846; +// 编译器自动处理:等同于 std::decimal("3.14159265358979323846") +``` + +**识别规则**: + +- 纯数字,19 位及以上 → BigInt +- 含小数点或指数,16 位有效数字以上 → Decimal +- 禁用下划线 `_`(如 `1_234_567_890_123_456_789_0`) + +> **推荐做法**:对于关键精度,仍然建议显式使用 `std::bigInt("...")` 或 `std::decimal("...")`,确保意图明确。自动识别是一个便利特性,适用于快速原型开发。 + +--- + +## 12. 限制与注意事项 + +### 12.1 不可变性 + +所有 Big* 类型是**不可变的**。每次运算创建新值: + +```php +$a = std::bigInt(100); +$b = $a->add(50); // $a 依然是 100,$b 是 150 +$c = $a + 50; // $a 依然是 100,$c 是 150 +``` + +### 12.2 `++` / `--` 不支持 + +参见 [第 7.2 节](#72---不可用)。使用 `+= 1` / `-= 1` 代替。 + +### 12.3 BigFloat 不支持 `%` 和 `**` + +```php +$bf = std::bigFloat(10.0); +$bf %= 3; // ❌ 编译错误 +$bf ** 2; // ❌ 编译错误 +``` + +### 12.4 Decimal 不支持 `**` + +```php +$d = std::decimal("10.5"); +$d ** 2; // ❌ 编译错误 +``` + +### 12.5 跨 Big* 类型不能隐式混合 + +BigFloat、Decimal、BigInt 之间必须显式转换: + +```php +$a = std::bigFloat(100.5); +$b = std::bigInt(200); +$c = $a + $b; // ❌ 编译错误 +// 改为 +$c = $a + std::bigFloat($b->toString()); // ✅ +``` + +### 12.6 不能在普通 PHP 解释器中运行 + +Big* 类型是 AOT 编译器的专有特性,依赖编译期代码生成和 C++ 底层库。源码不能被 `php` 命令直接解释执行。 + +### 12.7 启用 `use native_types` + +忘记添加 `use native_types` 会导致 Big* 变量被当作 Var(通用类型),失去原生类型的大部分性能优势。 + +--- + +## 13. 完整示例 + +### 13.1 大整数阶乘 + +```php +toString() . "\n"; + echo "位数: " . strlen($result->toString()) . "\n"; +} + +function main(): void { + factorial(10); // 10! = 3628800 + factorial(50); // 3041409320171337804361260816606476884... + factorial(100); // 933262154439441526816992388562667004... +} +?> +``` + +### 13.2 金融计算:订单明细 + +```php +toString() . "\n"; + echo "数量: {$quantity}\n"; + echo "小计: " . $subtotal->toString() . "\n"; + echo "税额: " . $tax->toString() . "\n"; + echo "总计: " . $total->toString() . "\n"; +} +?> +``` + +输出: + +``` +单价: 19.99 +数量: 3 +小计: 59.97 +税额: 4.7976 +总计: 64.7676 +``` + +### 13.3 高精度圆周率计算 + +```php +toString() . "\n"; + + // 圆周长 + $circumference = $pi * std::bigFloat(2 * $radius); + echo "圆周长: " . $circumference->toString() . "\n"; + + // 比较 + $earthRadius = 6371; + $earthArea = $pi * std::bigFloat($earthRadius * $earthRadius); + echo "如果半径是 {$earthRadius}km...\n"; + echo "面积约: " . $earthArea->toInt() . " km²\n"; +} +?> +``` + +### 13.4 综合示例:多种类型混合 + +```php +toString() . "\n"; + + // 运算符 + 比较 + $a = std::bigInt(100); + echo "BigInt + Int: " . ($a + 50)->toString() . "\n"; + echo "BigInt * 5: " . ($a * 5)->toString() . "\n"; + echo "BigInt > 50: " . (int)($a > 50) . "\n"; + echo "a == 100: " . (int)($a == 100) . "\n"; + + // Unary minus + $neg = -$a; + echo "-a: " . $neg->toString() . "\n"; + + // Decimal — 精确十进制运算 + $price = std::decimal("99.99"); + $price *= 3; // 复合赋值 + echo "价格 × 3: " . $price->toString() . "\n"; + + // 比较 + $d = std::decimal("100.25"); + echo "d > 50: " . (int)($d > 50) . "\n"; + echo "d != 100: " . (int)($d != 100) . "\n"; + + // BigFloat — 高精度浮点 + $bf = std::bigFloat(3.14159); + $bf *= 2.0; + echo "pi × 2: " . $bf->toString() . "\n"; + + // 方法链式调用 + $result = std::bigInt(100) + ->add(50) + ->mul(3) + ->sub(100) + ->toString(); + echo "100 + 50 × 3 - 100 = " . $result . "\n"; +} +?> +``` + +输出: + +``` +BigInt: 200000000000000000099 +BigInt + Int: 150 +BigInt * 5: 500 +BigInt > 50: 1 +a == 100: 1 +-a: -100 +价格 × 3: 299.97 +d > 50: 1 +d != 100: 1 +pi × 2: 6.2831800000000000 +100 + 50 × 3 - 100 = 350 +``` + +--- + +## 进一步阅读 + +- **类型系统规范**:[`docs/NATIVE_TYPES.md`](NATIVE_TYPES.md) — 完整的类型提升规则、声明语法、C++ API 参考 +- **BigInt PHPT 测试**:[`tests/aot/bigint/`](../tests/aot/bigint/) — BigInt 各项功能的集成测试 +- **Decimal PHPT 测试**:[`tests/aot/decimal/`](../tests/aot/decimal/) — Decimal 各项功能的集成测试 +- **BigFloat 集成测试**:[`tests/aot/bigfloat_operators.phpt`](../tests/aot/bigfloat_operators.phpt) — BigFloat 运算符测试 +- **C++ 运行时头文件**: + - [`phpx/include/phpx_big_int.h`](../phpx/include/phpx_big_int.h) — BigInt C++ API + - [`phpx/include/phpx_decimal.h`](../phpx/include/phpx_decimal.h) — Decimal C++ API + - [`phpx/include/phpx_big_float.h`](../phpx/include/phpx_big_float.h) — BigFloat C++ API diff --git a/docs/UNIVERSAL_METHODS.md b/docs/UNIVERSAL_METHODS.md new file mode 100644 index 00000000..259fc224 --- /dev/null +++ b/docs/UNIVERSAL_METHODS.md @@ -0,0 +1,1112 @@ +# AOT 编译器通用方法(Universal Methods)使用教程 + +本教程全面介绍 AOT 编译器的**通用方法(Universal Methods)**系统——一种在原生类型上通过 `$value->method()` 语法进行零开销方法调用的机制。 + +## 目录 + +1. [什么是通用方法](#1-什么是通用方法) +2. [设计原理](#2-设计原理) +3. [支持的类型一览](#3-支持的类型一览) +4. [Int 整型方法](#4-int-整型方法) +5. [Float 浮点型方法](#5-float-浮点型方法) +6. [Bool 布尔型方法](#6-bool-布尔型方法) +7. [String 字符串方法](#7-string-字符串方法) +8. [Array 数组方法](#8-array-数组方法) +9. [Stream 流方法](#9-stream-流方法) +10. [Big* 高精度类型方法](#10-big-高精度类型方法) +11. [方法链式调用](#11-方法链式调用) +12. [可变方法 vs 不可变方法](#12-可变方法-vs-不可变方法) +13. [Var 类型的方法查找](#13-var-类型的方法查找) +14. [扩展方法:自动发现](#14-扩展方法自动发现) +15. [完整示例](#15-完整示例) + +--- + +## 1. 什么是通用方法 + +通用方法(Universal Methods)允许你在 PHP 原生类型的变量上直接调用方法,就像在对象上调用一样。编译器在编译时将这些方法调用翻译为对应的 C 函数或 C++ 方法调用,**生成零开销的本地代码**。 + +```php +// 字符串上直接调用方法 +$s = "hello world"; +echo $s->length(); // → strlen($s) → 11 +echo $s->upper(); // → strtoupper($s) → "HELLO WORLD" +echo $s->substr(0, 5); // → substr($s, 0, 5) → "hello" + +// 数组上直接调用方法 +$arr = [1, 3, 5, 7, 9]; +echo $arr->count(); // → count($arr) → 5 +echo $arr->contains(3); // → in_array(3, $arr) → true +$arr->push(11); // → array_push($arr, 11) + +// 整数上直接调用方法 +$a = 100; +$a->add(50); // → a += 50 → 150 +echo $a->toString(); // → "150" + +// 高精度类型上直接调用方法 +$big = std::bigInt("12345678901234567890"); +echo $big->mul(2)->toString(); // → "24691357802469135780" +``` + +> **关键理念**:所有通用方法调用在编译时全部消解为直接函数调用。不存在虚表查找、反射、或运行时类型检查——生成的 C++ 代码与手写 C 函数调用完全等价。 + +--- + +## 2. 设计原理 + +### 2.1 编译期消解 + +每个通用方法调用在编译时经过以下步骤: + +1. **类型推断**:编译器推断调用者(receiver)的类型(Int / Float / String / Array / Stream / BigInt / Decimal / BigFloat / Var) +2. **方法查找**:在该类型的方法表中查找方法定义 +3. **参数校验**:检查参数个数是否在 `min_args` ~ `max_args` 范围内 +4. **代码生成**:根据 handler 类型生成对应的 C/C++ 调用代码 + +### 2.2 Handler 类型 + +通用方法系统支持五种内部 handler,各自适用于不同的场景: + +| Handler | 说明 | 示例 | +|---------|------|------| +| `calc_op` | 直接生成 C++ 运算符代码(仅 Int/Float) | `$a->add(1)` → `a + 1` | +| `php_fn` | 调用 PHP 标准库函数 | `$s->length()` → `strlen(s)` | +| `cpp_fn` | 调用 `phpx` C++ 静态方法 | `$big->add(1)` → `BigInt::add($big, 1)` | +| `direct_method` | 调用 Variant 的 C++ 成员方法 | `$s->equals("x")` → `s.equals("x")` | +| `convert_fn` | 类型转换 | `$a->toFloat()` → `php::toFloat(a)` | +| `php_fn_ref` | 调用 PHP 函数并通过引用修改原值(仅 Array) | `$arr->push(1)` → `array_push($arr, 1)` | +| `direct_method_mutate` | 调用 Variant 的变异成员方法 | `$arr->set(0, 1)` → `arr.set(0, 1)` | + +--- + +## 3. 支持的类型一览 + +| 类型 | 方法数量 | 主要分类 | +|------|---------|---------| +| **Int** | 26 | 算术、数学函数、类型转换 | +| **Float** | 26 | 算术、数学函数、三角函数、类型转换 | +| **Bool** | 2 | 类型转换 | +| **String** | 70+ | 字符串操作、搜索、编码、Hash、多字节、序列化 | +| **Array** | 50+ | 增删改查、排序、遍历、集合运算、序列化 | +| **Stream** | 30+ | 读写、定位、锁、Socket、过滤 | +| **BigInt** | 14 | 算术、比较、转换、GCD | +| **Decimal** | 11 | 算术、比较、转换 | +| **BigFloat** | 10 | 算术、比较、转换 | + +--- + +## 4. Int 整型方法 + +### 4.1 算数运算(返回 Int) + +```php +$a = 100; + +$a->add(50); // a += 50 → 150 +$a->sub(30); // a -= 30 → 70 +$a->mul(2); // a *= 2 → 200 +$a->div(4); // a /= 4 → 25 +$a->mod(7); // a %= 7 → 2 + +$a->inc(); // a++ → 101 +$a->dec(); // a-- → 100 +``` + +> **可变性**:`add`/`sub`/`mul`/`div`/`mod`/`inc`/`dec` **会修改原变量**(直接操作 C++ 原生 `int64_t`),同时返回新值。这与 Big* 类型的不可变语义不同。 + +```php +$a = 100; +$b = $a->add(50); // $a 变为 150,$b 也是 150(返回新值) +``` + +### 4.2 数学函数 + +```php +$a = -100; + +$a->abs(); // abs($a) → 100 +$a->ceil(); // ceil($a) → -100.0 (float) +$a->floor(); // floor($a) → -100.0 (float) +$a->round(); // round($a) → -100.0 (float) +$a->sqrt(); // sqrt($a) → NaN(负数无平方根) +$a->pow(3); // $a ** 3 → -1000000 +$a->log(); // log($a) → NaN +$a->log10(); // log10($a) → NaN +$a->exp(); // exp($a) → 3.72e-44 (float) + +$a->max(50); // max($a, 50) → 50 +$a->min(50); // min($a, 50) → -100 +``` + +> **注意**:`ceil`、`floor`、`round` 返回 **Float 类型**(PHP 标准行为)。`pow` 返回 **Var 类型**(因为幂运算结果可能溢出)。 + +### 4.3 三角函数 + +```php +$a = 0; + +$a->sin(); // sin(0) → 0.0 +$a->cos(); // cos(0) → 1.0 +$a->tan(); // tan(0) → 0.0 +$a->asin(); // asin(0) → 0.0 +$a->acos(); // acos(1) → 0.0 +$a->atan(); // atan(0) → 0.0 +$a->atan2(1); // atan2(0,1) → 0.0 +$a->deg2rad(); // deg2rad(0) → 0.0 +$a->rad2deg(); // rad2deg(0) → 0.0 +``` + +### 4.4 类型转换 + +```php +$a = 42; + +$a->toFloat(); // (float) $a → 42.0 +$a->toString(); // (string) $a → "42" +$a->toBool(); // (bool) $a → true +``` + +### 4.5 完整方法列表 + +| 方法 | 参数 | 返回类型 | 说明 | +|------|------|---------|------| +| `add($x)` | 1 | Int | 加法(修改原值) | +| `sub($x)` | 1 | Int | 减法(修改原值) | +| `mul($x)` | 1 | Int | 乘法(修改原值) | +| `div($x)` | 1 | Int | 除法(修改原值) | +| `mod($x)` | 1 | Int | 取模(修改原值) | +| `inc()` | 0 | Int | 自增(修改原值) | +| `dec()` | 0 | Int | 自减(修改原值) | +| `abs()` | 0 | Int | 绝对值 | +| `ceil()` | 0 | Float | 向上取整 | +| `floor()` | 0 | Float | 向下取整 | +| `round()` | 0-2 | Float | 四舍五入 | +| `sqrt()` | 0 | Float | 平方根 | +| `pow($x)` | 1 | Var | 幂运算 | +| `log()` | 0-1 | Float | 自然对数 | +| `log10()` | 0 | Float | 以 10 为底的对数 | +| `exp()` | 0 | Float | e 的指数 | +| `sin()` | 0 | Float | 正弦 | +| `cos()` | 0 | Float | 余弦 | +| `tan()` | 0 | Float | 正切 | +| `asin()` | 0 | Float | 反正弦 | +| `acos()` | 0 | Float | 反余弦 | +| `atan()` | 0 | Float | 反正切 | +| `atan2($x)` | 1 | Float | 二参数反正切 | +| `deg2rad()` | 0 | Float | 角度转弧度 | +| `rad2deg()` | 0 | Float | 弧度转角度 | +| `max($x)` | 1 | Int/Float | 取最大值 | +| `min($x)` | 1 | Int/Float | 取最小值 | +| `toFloat()` | 0 | Float | 转浮点 | +| `toString()` | 0 | String | 转字符串 | +| `toBool()` | 0 | Bool | 转布尔 | + +--- + +## 5. Float 浮点型方法 + +Float 的方法集与 Int 几乎完全相同,但返回类型为 Float(`ceil`/`floor`/`round`/三角函数等仍为 Float)。 + +```php +$f = 3.14; + +$f->add(1.0); // f += 1.0 → 4.14 +$f->sub(1.0); // f -= 1.0 → 2.14 +$f->mul(2.0); // f *= 2.0 → 6.28 +$f->div(2.0); // f /= 2.0 → 1.57 + +$f->abs(); // abs(3.14) → 3.14 +$f->sqrt(); // sqrt(3.14) → 1.772... +$f->sin(); // sin(3.14) → 0.00159... +$f->round(2); // round(3.14, 2) → 3.14 + +// Float 特有的转换 +$f->toInt(); // (int) $f → 3 +$f->toString(); // (string) $f → "3.14" +$f->toBool(); // (bool) $f → true +``` + +Float 与 Int 方法的主要区别: +- Int 有 `mod($x)`,Float 没有(浮点取模无意义) +- 算术方法直接操作 C++ `double`,性能与手写 C 代码一致 + +--- + +## 6. Bool 布尔型方法 + +Bool 类型仅有两个转换方法: + +```php +$b = true; + +$b->toInt(); // (int) $b → 1 +$b->toString(); // (string) $b → "1" +``` + +--- + +## 7. String 字符串方法 + +String 拥有最丰富的方法集,涵盖日常开发的大部分字符串操作需求。 + +### 7.1 基本操作 + +```php +$s = "hello world"; + +$s->length(); // strlen($s) → 11 +$s->isEmpty(); // empty($s) → false +$s->upper(); // strtoupper($s) → "HELLO WORLD" +$s->lower(); // strtolower($s) → "hello world" +$s->upperFirst(); // ucfirst($s) → "Hello world" +$s->lowerFirst(); // lcfirst($s) → "hello world" +$s->upperWords(); // ucwords($s) → "Hello World" + +$s->trim(); // trim($s) → "hello world" +$s->lTrim(); // ltrim($s) → "hello world" +$s->rTrim(); // rtrim($s) → "hello world" +$s->trim(" \t\n\r"); // trim($s, chars) +``` + +### 7.2 搜索与比较 + +```php +$s = "hello world"; + +// 判断 +$s->startsWith("hello"); // str_starts_with(...) → true +$s->endsWith("world"); // str_ends_with(...) → true +$s->contains("lo wo"); // str_contains(...) → true +$s->compare("hello"); // strcmp(...) → >0 +$s->iCompare("HELLO"); // strcasecmp(...) → 0 +$s->isNumeric(); // is_numeric(...) → false + +// 位置查找 +$s->indexOf("world"); // strpos($s, "world") → 6 +$s->lastIndexOf("o"); // strrpos($s, "o") → 7 +$s->iIndexOf("WORLD"); // stripos($s, "WORLD") → 6 +$s->iLastIndexOf("O"); // strripos($s, "O") → 7 + +// 内容查找 +$s->find("world"); // strstr($s, "world") → "world" +$s->iFind("WORLD"); // stristr($s, "WORLD") → "world" +$s->lastCharIndexOf("o"); // strrchr($s, "o") → "orld" +``` + +### 7.3 截取与替换 + +```php +$s = "hello world"; + +// 截取 +$s->substr(0, 5); // substr($s, 0, 5) → "hello" +$s->substr(6); // substr($s, 6) → "world" + +// 统计 +$s->substrCount("l"); // substr_count($s, "l") → 3 +$s->wordCount(); // str_word_count($s) → 2 + +// 替换 +$s->replace("hello", "hi"); // str_replace("hello", "hi", $s) +$s->iReplace("HELLO", "hi"); // str_ireplace(...) +$s->substrReplace("hi", 0, 5); // substr_replace($s, "hi", 0, 5) +$s->stripTags(); // strip_tags($s) +$s->stripTags("

"); // strip_tags($s, tags) +``` + +### 7.4 拆分与连接 + +```php +$s = "hello world"; + +// 拆分 +$words = $s->split(" "); // explode(" ", $s) → ["hello", "world"] +$words->count(); // 2 + +// 连接(在数组上操作) +$words->join(", "); // implode(", ", $words) → "hello, world" + +// 重复 +$s->repeat(3); // str_repeat($s, 3) → "hello worldhello worldhello world" + +// 填充 +$s->pad(20, "-"); // str_pad($s, 20, "-") +``` + +### 7.5 编码与转义 + +```php +$s = "hello world & "; + +$s->htmlEntityEncode(); // htmlentities($s) +$s->htmlEntityDecode(); // html_entity_decode($s) +$s->htmlSpecialCharsEncode(); // htmlspecialchars($s) +$s->htmlSpecialCharsDecode(); // htmlspecialchars_decode($s) + +$s->urlEncode(); // urlencode($s) → "hello+world+%26+%3Ctest%3E" +$s->urlDecode(); // urldecode($s) +$s->rawUrlEncode(); // rawurlencode($s) +$s->rawUrlDecode(); // rawurldecode($s) + +$s->addSlashes(); // addslashes($s) +$s->stripSlashes(); // stripslashes($s) +$s->addCSlashes("A..z"); // addcslashes($s, "A..z") +$s->stripCSlashes(); // stripcslashes($s) + +$s->base64Encode(); // base64_encode($s) +$s->base64Decode(); // base64_decode($s) +``` + +### 7.6 Hash 与校验 + +```php +$s = "hello"; + +$s->md5(); // md5($s) → "5d41402abc4b2a76b9719d911017c592" +$s->sha1(); // sha1($s) → "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d" +$s->crc32(); // crc32($s) → 907060870 +$s->hash("sha256"); // hash("sha256", $s) → "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" +$s->hashCode(); // C++ std::hash → 哈希值 (int) +``` + +### 7.7 正则匹配 + +```php +$s = "hello world"; + +// match(pattern) → 返回匹配数组 +$result = $s->match("/hello/"); + +// matchAll(pattern) → 返回所有匹配结果 +$results = $s->matchAll("/[a-z]+/"); +``` + +### 7.8 序列化 + +```php +$s = '{"name":"John","age":30}'; + +$data = $s->jsonDecode(); // json_decode($s, true) → ["name" => "John", ...] +$obj = $s->jsonDecodeToObject(); // json_decode($s) → stdClass + +$s = 'a:3:{i:0;s:3:"foo";i:1;s:3:"bar";i:2;s:3:"baz";}'; +$arr = $s->unserialize(); // unserialize($s) → ["foo", "bar", "baz"] +``` + +### 7.9 多字节字符串(mbstring) + +以 `mb` 前缀开头的方法对应 PHP 的 `mb_*` 函数族: + +```php +$s = "你好世界"; + +$s->mbLength(); // mb_strlen($s) → 4 +$s->mbUpper(); // mb_strtoupper($s) +$s->mbLower(); // mb_strtolower($s) +$s->mbSubstr(0, 2); // mb_substr($s, 0, 2) → "你好" +$s->mbIndexOf("世界"); // mb_strpos($s, "世界") → 2 +$s->mbFind("世"); // mb_strstr($s, "世") +$s->mbDetectEncoding(); // mb_detect_encoding($s) +$s->mbConvertEncoding("UTF-8"); // mb_convert_encoding($s, "UTF-8") +$s->mbConvertCase(MB_CASE_TITLE); // mb_convert_case($s, MB_CASE_TITLE) +$s->mbTrim(); // mb_trim($s) +$s->mbLTrim(); // mb_ltrim($s) +$s->mbRTrim(); // mb_rtrim($s) +``` + +### 7.10 C++ 原生方法 + +以下方法直接调用 `phpx::Variant` 的 C++ 成员函数,无 PHP 函数对应: + +```php +$s = "hello"; + +$s->equals("hello"); // C++ String.equals() —— 值比较 +$s->append(" world"); // C++ String.append() —— 追加(修改原值) +``` + +--- + +## 8. Array 数组方法 + +Array 方法分为**只读方法**和**变异方法**两类。变异方法会直接修改原数组变量。 + +### 8.1 基本信息 + +```php +$arr = [1, 3, 5, 7, 9]; + +$arr->count(); // count($arr) → 5 +$arr->isEmpty(); // empty($arr) → false +$arr->isList(); // array_is_list($arr) → true +``` + +### 8.2 增删改查 + +```php +$arr = [1, 2, 3]; + +// 变异方法(修改原数组) +$arr->push(4); // array_push($arr, 4) → [1,2,3,4] +$arr->push(5, 6, 7); // 支持多个参数 +$arr->pop(); // array_pop($arr) → 返回 7 +$arr->shift(); // array_shift($arr) → 返回 1 +$arr->unshift(0); // array_unshift($arr, 0) → [0,...] +$arr->set(0, 100); // C++ Array.set(0, 100) → [100,...] +$arr->del(0); // C++ Array.del(0) → 删除索引 0 +$arr->clean(); // C++ Array.clean() → [] + +// 只读方法 +$arr = ['a' => 1, 'b' => 2, 'c' => 3]; +$arr->get('a'); // C++ Array.get('a') → 1 +$arr->keyExists('a'); // array_key_exists('a', $arr) → true +$arr->contains(2); // in_array(2, $arr) → true +$arr->search(2); // array_search(2, $arr) → "b" +``` + +### 8.3 遍历与聚合 + +```php +$arr = [1, 3, 5, 7, 9]; + +$arr->sum(); // array_sum($arr) → 25 +$arr->product(); // array_product($arr) → 945 +$arr->all(fn($v) => ...); // array_all($arr, fn) +$arr->any(fn($v) => ...); // array_any($arr, fn) + +$arr->map(fn($v) => $v * 2);// array_map(fn, $arr) → [2,6,10,14,18] +$arr->reduce(fn($c, $v) => $c + $v, 0); // array_reduce($arr, fn, 0) +$arr->filter(fn($v) => $v > 5); // array_filter($arr, fn) +$arr->walk(fn(&$v) => $v *= 2); // array_walk($arr, fn) +``` + +### 8.4 排序 + +```php +$arr = [3, 1, 4, 1, 5, 9]; + +$arr->sort(); // sort($arr) → [1,1,3,4,5,9] +$arr->sortDesc(); // rsort($arr) → [9,5,4,3,1,1] +$arr->keySort(); // ksort($arr) → 按键排序 +$arr->valueSort(); // asort($arr) → 按值排序(保持键) +``` + +所有排序方法都会**修改原数组**。 + +### 8.5 集合运算 + +```php +$a = [1, 2, 3, 4, 5]; +$b = [4, 5, 6, 7, 8]; + +$a->diff($b); // array_diff($a, $b) → [1,2,3] +$a->intersect($b); // array_intersect(...) → [4,5] +$a->merge($b); // array_merge($a, $b) → [1,2,3,4,5,4,5,6,7,8] +$a->unique(); // array_unique($a) → [1,2,3,4,5] +$a->flip(); // array_flip($a) → {1:0, 2:1, 3:2, ...} +$a->reverse(); // array_reverse($a) → [5,4,3,2,1] +$a->replace($b); // array_replace($a, $b) +$a->values(); // array_values($a) → 重索引 +$a->combine($keys); // array_combine($keys, $a) +$a->fillKeys($value); // array_fill_keys($a, $value) +``` + +> `diff`、`intersect`、`merge`、`replace` 等方法支持**可变参数**:`$a->merge($b, $c, $d)` 可以一次合并多个数组。 + +### 8.6 提取与切片 + +```php +$arr = ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5]; + +$arr->keys(); // array_keys($arr) → ['a','b','c','d','e'] +$arr->slice(1, 3); // array_slice($arr, 1, 3) → ['b'=>2, 'c'=>3, 'd'=>4] +$arr->chunk(2); // array_chunk($arr, 2) → [[1,2],[3,4],[5]] +$arr->column('name'); // array_column($arr, 'name') +$arr->splice(1, 3, [6,7]); // array_splice($arr, 1, 3, [6,7]) —— 修改原数组 +$arr->rand(2); // array_rand($arr, 2) + +$arr->keyFirst(); // array_key_first($arr) → "a" +$arr->keyLast(); // array_key_last($arr) → "e" +$arr->find(fn($v) => $v > 3); // array_find($arr, fn) +``` + +### 8.7 字符串相关 + +```php +$arr = ["hello", "world"]; + +$arr->join(", "); // implode(", ", $arr) → "hello, world" +$arr->replaceStr("hello", "hi"); // str_replace("hello", "hi", $arr) +$arr->iReplaceStr("HELLO", "hi"); // str_ireplace(...) +``` + +### 8.8 序列化 + +```php +$arr = ["name" => "John", "age" => 30]; + +$arr->serialize(); // serialize($arr) → "a:2:{...}" +$arr->marshal(); // serialize($arr) —— 别名 +$arr->jsonEncode(); // json_encode($arr) → '{"name":"John","age":30}' +``` + +### 8.9 类型转换 + +```php +$arr = [1, 2, 3]; + +$arr->toInt(); // (int) 非空数组 → 1 +$arr->toFloat(); // (float) 非空数组 → 1.0 +$arr->toBool(); // (bool) 非空数组 → true +$arr->toString(); // → "Array" +``` + +### 8.10 完整方法分类表 + +| 分类 | 方法 | +|------|------| +| 基本信息 | `count`, `isEmpty`, `isList` | +| 增删改查 | `push`, `pop`, `shift`, `unshift`, `set`, `get`, `del`, `clean`, `keyExists`, `contains`, `search` | +| 遍历聚合 | `sum`, `product`, `all`, `any`, `map`, `reduce`, `filter`, `walk` | +| 排序 | `sort`, `sortDesc`, `keySort`, `valueSort` | +| 集合运算 | `diff`, `diffAssoc`, `diffKey`, `intersect`, `intersectAssoc`, `merge`, `unique`, `flip`, `reverse`, `replace`, `values`, `combine`, `fillKeys` | +| 提取切片 | `keys`, `slice`, `chunk`, `column`, `splice`, `rand`, `keyFirst`, `keyLast`, `find` | +| 字符串 | `join`, `replaceStr`, `iReplaceStr`, `countValues`, `pad` | +| 序列化 | `serialize`, `marshal`, `jsonEncode` | +| 类型转换 | `toInt`, `toFloat`, `toBool`, `toString` | + +--- + +## 9. Stream 流方法 + +Stream 类型表示文件句柄或网络连接。通过 `fopen()` 等函数获取。 + +### 9.1 读写 + +```php +$fp = fopen("test.txt", "w+"); + +$fp->write("hello world\n"); // fwrite($fp, "hello world\n") → 写入字节数 +$fp->write("more data", 4); // fwrite($fp, "more data", 4) → 只写 4 字节 + +$fp->seek(0); // fseek($fp, 0) → 回到开头 +$content = $fp->read(1024); // fread($fp, 1024) +$content = $fp->getContents(); // stream_get_contents($fp) + +$char = $fp->getChar(); // fgetc($fp) → 读取一个字符 +$line = $fp->getLine(); // fgets($fp) → 读取一行 +$line = $fp->getLine(1024); // fgets($fp, 1024) +$line = $fp->getRecord(1024, "\n"); // stream_get_line($fp, 1024, "\n") +``` + +### 9.2 元数据与状态 + +```php +$fp->tell(); // ftell($fp) → 当前位置 +$fp->eof(); // feof($fp) → 是否 EOF +$fp->stat(); // fstat($fp) → 文件状态数组 +$fp->getMetaData(); // stream_get_meta_data($fp) +$fp->isLocal(); // stream_is_local($fp) +$fp->isTTY(); // stream_isatty($fp) +``` + +### 9.3 控制操作 + +```php +$fp->truncate(0); // ftruncate($fp, 0) → 截断文件 +$fp->sync(); // fsync($fp) → 同步到磁盘 +$fp->dataSync(); // fdatasync($fp) → 同步数据 +$fp->close(); // fclose($fp) → 关闭 + +// 锁 +$fp->lock(LOCK_EX); // flock($fp, LOCK_EX) +$fp->lock(LOCK_SH); // flock($fp, LOCK_SH) +$fp->lock(LOCK_UN); // flock($fp, LOCK_UN) + +// 缓冲设置 +$fp->setBlocking(true); // stream_set_blocking($fp, true) +$fp->setChunkSize(8192); // stream_set_chunk_size($fp, 8192) +$fp->setReadBuffer(8192); // stream_set_read_buffer($fp, 8192) +$fp->setWriteBuffer(8192); // stream_set_write_buffer($fp, 8192) +$fp->setTimeout(30); // stream_set_timeout($fp, 30) +$fp->supportsLock(); // stream_supports_lock($fp) +``` + +### 9.4 Socket 操作 + +```php +// 服务端 +$server = stream_socket_server("tcp://0.0.0.0:8080"); +$client = $server->accept(); // stream_socket_accept($server) +$client->accept(30); // 超时 30 秒 + +// 信息 +$client->getSocketName(true); // stream_socket_get_name —— 远程地址 +$server->getSocketName(false); // stream_socket_get_name —— 本地地址 + +// 数据 +$client->sendTo("hello", 0, $addr); // stream_socket_sendto(...) +$client->recvFrom(1024); // stream_socket_recvfrom(...) +$client->recvFrom(1024, 0, $addr); // 带地址 + +// 控制 +$client->enableCrypto(true); // stream_socket_enable_crypto → 启用 TLS +$client->shutdown(STREAM_SHUT_RDWR); // stream_socket_shutdown(...) + +// 过滤器 +$fp->appendFilter("string.toupper"); // stream_filter_append(...) +$fp->prependFilter("string.tolower"); // stream_filter_prepend(...) +``` + +### 9.5 流拷贝 + +```php +$src = fopen("source.txt", "r"); +$dst = fopen("dest.txt", "w"); + +$src->copy($dst); // stream_copy_to_stream($src, $dst) +$src->copy($dst, 4096); // 指定缓冲区大小 +``` + +--- + +## 10. Big* 高精度类型方法 + +### 10.1 BigInt 方法 + +```php +$a = std::bigInt("12345678901234567890"); + +// 算术(均返回新 BigInt,原值不变) +$b = $a->add(1); // $a + 1 +$c = $a->sub(1); // $a - 1 +$d = $a->mul(2); // $a * 2 +$e = $a->div(10); // $a / 10 +$f = $a->mod(1000000); // $a % 1000000 +$g = $a->pow(3); // $a ** 3 + +// 一元方法 +$h = $a->neg(); // -$a +$i = $a->abs(); // abs($a) + +// 特殊方法 +$j = $a->gcd(15); // gcd($a, 15) + +// 比较 +$cmp = $a->cmp(100); // -1/0/1 + +// 类型转换 +$a->toString(); // → "12345678901234567890" +$a->toInt(); // → int(可能截断) +$a->toFloat(); // → float(可能丢精度) +``` + +| 方法 | 参数 | 返回 | 说明 | +|------|------|------|------| +| `add($x)` | 1 | BigInt | 加法 | +| `sub($x)` | 1 | BigInt | 减法 | +| `mul($x)` | 1 | BigInt | 乘法 | +| `div($x)` | 1 | BigInt | 整数除法 | +| `mod($x)` | 1 | BigInt | 取模 | +| `pow($x)` | 1 | BigInt | 幂运算 | +| `neg()` | 0 | BigInt | 取负 | +| `abs()` | 0 | BigInt | 绝对值 | +| `gcd($x)` | 1 | BigInt | 最大公约数 | +| `cmp($x)` | 1 | Int | 比较 | +| `toString()` | 0 | String | 转字符串 | +| `toInt()` | 0 | Int | 转整数 | +| `toFloat()` | 0 | Float | 转浮点 | + +### 10.2 Decimal 方法 + +```php +$d = std::decimal("123.456"); + +$d->add(std::decimal("50.25")); // 加法 +$d->sub(std::decimal("50.25")); // 减法 +$d->mul(2); // 乘法 +$d->div(3); // 除法 +$d->mod(std::decimal("5.0")); // 取模 +$d->neg(); // 取负 +$d->abs(); // 绝对值 +$d->cmp(std::decimal("100")); // 比较 +$d->toString(); // → "123.456" +$d->toInt(); // → 123 +$d->toFloat(); // → 123.456 (double) +``` + +### 10.3 BigFloat 方法 + +```php +$bf = std::bigFloat(3.14159265); + +$bf->add(1.0); // 加法 +$bf->sub(1.0); // 减法 +$bf->mul(2.0); // 乘法 +$bf->div(2.0); // 除法 +$bf->neg(); // 取负 +$bf->abs(); // 绝对值 +$bf->cmp(3.0); // 比较 → >0 +$bf->toString(); // 转字符串 +$bf->toInt(); // → 3 +$bf->toFloat(); // → 3.14159265 +``` + +> **不可变性**:与 Int/Float 不同,Big* 类型的所有方法都**返回新值**,不修改原变量。参见 [第 12 节](#12-可变方法-vs-不可变方法)。 + +--- + +## 11. 方法链式调用 + +通用方法的返回值类型是编译时已知的,因此可以直接链式调用: + +```php +// 字符串链式调用 +$result = " Hello World! " + ->trim() + ->lower() + ->substr(0, 5) + ->upper(); +echo $result; // "HELLO" + +// Int 链式调用 +$result = 100 + ->add(50) // 150(修改原变量) + ->mul(3) // 450 + ->sub(100) // 350 + ->toString(); +echo $result; // "350" + +// BigInt 链式调用(不可变,返回新值) +$result = std::bigInt("100") + ->add(std::bigInt(50)) + ->mul(std::bigInt(3)) + ->toString(); +echo $result; // "450" + +// 跨类型链式调用 +$sum = "123456789012345678901234567890" + ->length(); // String.length() → Int +echo $sum; // 30 + +// 链式调用 + 最终转换 +$result = std::bigInt("99999999999999999999") + ->add(std::bigInt(1)) + ->toString(); +echo "100000000000000000000 = " . $result; +``` + +> **注意**:Int/Float 的链式调用每步**都会修改原变量**。如果需要保留中间值,先用临时变量保存。Big* 类型不存在此问题,因为它们是不可变的。 + +--- + +## 12. 可变方法 vs 不可变方法 + +不同类型的通用方法在可变性上有不同行为: + +### 12.1 可变方法(修改原值) + +**Int 和 Float** 的算术方法直接操作 C++ 原生变量,会**修改原值**: + +```php +$a = 100; +$b = $a->add(50); // 这一步既修改了 $a(变为 150),又返回新值给 $b +echo $a; // 150 —— 已被修改 +echo $b; // 150 + +$a->inc(); // $a 变为 151 +$a->dec(); // $a 变为 150 +``` + +**Array** 的变异方法会**修改原数组**: + +```php +$arr = [1, 2, 3]; + +$arr->push(4); // 修改 $arr → [1,2,3,4] +$arr->pop(); // 修改 $arr → [1,2,3] +$arr->sort(); // 修改 $arr → [1,2,3] +$arr->set(0, 100); // 修改 $arr → [100,2,3] +$arr->clean(); // 修改 $arr → [] +``` + +### 12.2 不可变方法(返回新值) + +**BigInt、Decimal、BigFloat** 的所有方法都**不修改原值**,返回新创建的对象: + +```php +$a = std::bigInt(100); +$b = $a->add(50); // $a 依然是 100,$b 是 150 + +$a = std::bigInt(100); +$a->add(50); // 返回值被丢弃!$a 依然是 100 +``` + +**String** 的大部分方法也返回新值: + +```php +$s = "hello"; +$upper = $s->upper(); // $s 依然是 "hello",$upper 是 "HELLO" +``` + +**例外**:`String.append()` 是变异方法(`direct_method_mutate`),会修改原字符串: + +```php +$s = "hello"; +$s->append(" world"); // $s 变为 "hello world" +``` + +### 12.3 可变方法汇总 + +| Handler 类型 | 影响类型 | 示例 | +|-------------|---------|------| +| `calc_op` | Int, Float | `add`, `sub`, `mul`, `div`, `mod`, `inc`, `dec` | +| `direct_method_mutate` | String, Array | `append`, `set`, `del`, `clean` | +| `php_fn_ref` | Array | `push`, `pop`, `shift`, `unshift`, `sort`, `sortDesc`, `splice`, `walk` | + +--- + +## 13. Var 类型的方法查找 + +当变量的类型为 `Var`(通用 PHP 类型)时,编译器会按以下顺序依次在方法表中查找: + +``` +String → Array → Int → Float → Bool → Stream → BigInt → Decimal → BigFloat +``` + +一旦找到匹配的方法名,就生成对应类型的调用代码。如果找不到,则退化为动态方法调用(通过 ZendVM)。 + +```php +// $x 的类型是 Var(来自函数返回值、数组提取等) +$x = some_func_returning_var(); + +// 编译器按顺序查找: +// 1. 先在 String 方法表中找 length → 找到!→ strlen($x) +echo $x->length(); + +// 2. 先在 String 方法表中找 contains → 找到!→ str_contains($x, ...) +echo $x->contains("test"); +``` + +> **注意**:如果多个类型有同名方法,先匹配到的类型优先。例如 `toInt` 在多个类型中都存在——查找会停在第一个匹配的类型上。 + +--- + +## 14. 扩展方法:自动发现 + +除了内置的通用方法外,编译器还支持**自定义扩展方法**。只要在当前项目中定义了命名符合约定的 PHP 函数,编译器就会自动将其发现为通用方法。 + +### 14.1 命名约定 + +格式:`{类型前缀}_{snake_case方法名}` + +| 类型 | 前缀 | 示例 | +|------|------|------| +| Int | `int_` | `int_is_prime` → `$a->isPrime()` | +| Float | `float_` | `float_normalize` → `$f->normalize()` | +| Bool | `bool_` | `bool_toggle` → `$b->toggle()` | +| String | `str_` | `str_capitalize` → `$s->capitalize()` | +| Array | `array_` | `array_flatten` → `$arr->flatten()` | +| Stream | `stream_` | `stream_rewind` → `$fp->rewind()` | +| BigInt | `bigint_` | `bigint_is_probable_prime` → `$a->isProbablePrime()` | +| Decimal | `decimal_` | `decimal_round_to` → `$d->roundTo()` | +| BigFloat | `bigfloat_` | `bigfloat_truncate` → `$bf->truncate()` | + +### 14.2 实现示例 + +```php +isPrime() + $n = 97; + if ($n->isPrime()) { + echo "$n is prime\n"; + } + + // 自动发现:array_flatten → $arr->flatten() + $nested = [[1, 2], [3, [4, 5]], 6]; + $flat = $nested->flatten(); + var_dump($flat); // [1, 2, 3, 4, 5, 6] +} +?> +``` + +### 14.3 注意事项 + +- 函数**第一个参数**是接收者(receiver),从方法调用中自动传入 +- 扩展方法的返回类型固定为 `Var` +- 需要先定义函数再调用——编译器在分析阶段发现函数,转换阶段使用它们 +- 方法的驼峰命名会**自动转为下划线命名**:`isPrime` → `is_prime`,`flattenNestedArray` → `flatten_nested_array` + +--- + +## 15. 完整示例 + +### 15.1 字符串处理管道 + +```php +Hello World! \n"; + + $processed = $raw + ->trim() // 去头尾空白 + ->stripTags() // 去 HTML 标签 + ->lower() // 转小写 + ->upperWords(); // 首字母大写 + + echo "原始: " . $raw->jsonEncode() . "\n"; + echo "处理: " . $processed . "\n"; + echo "长度: " . $processed->length() . "\n"; + echo "是否为数字: " . (int)$processed->isNumeric() . "\n"; +} +?> +``` + +### 15.2 数组数据处理 + +```php +join(", ") . "\n"; + + // 排序 + $data->sort(); + echo "排序: " . $data->join(", ") . "\n"; + + // 统计 + echo "数量: " . $data->count() . "\n"; + echo "求和: " . $data->sum() . "\n"; + echo "最小值: " . $data->get(0) . "\n"; + echo "最大值: " . $data->get($data->count() - 1) . "\n"; + echo "是否包含 5: " . (int)$data->contains(5) . "\n"; + + // 过滤与映射 + $even = $data->filter(function($v) { return $v % 2 == 0; }); + echo "偶数: " . $even->values()->join(", ") . "\n"; + + $doubled = $data->map(function($v) { return $v * 2; }); + echo "翻倍: " . $doubled->join(", ") . "\n"; + + // 序列化 + echo "JSON: " . $data->jsonEncode() . "\n"; +} +?> +``` + +### 15.3 高精度计算与链式调用 + +```php +toString()); + echo "{$n}! 有 {$digits} 位数字\n"; + + // 方法链:BigInt 算术 + 转换 + $big = std::bigInt(1000); + $val = $big->mul(3)->add(200)->sub(50)->div(10)->toString(); + echo "1000 * 3 + 200 - 50 / 10 = {$val}\n"; + + // Decimal 金融计算 + $price = std::decimal("19.99"); + $qty = 5; + $taxRate = std::decimal("0.08"); + $total = $price * $qty * ($taxRate->add(std::decimal(1))); + echo "总价: " . $total->toString() . "\n"; + + // BigFloat 科学计算 + $pi = std::bigFloat("3.14159265358979323846"); + $area = $pi * 100; + echo "圆面积: " . $area->toString() . "\n"; + echo "取整: " . $area->toInt() . "\n"; +} +?> +``` + +### 15.4 文件处理 + +```php +write("Line 1\n"); + $fp->write("Line 2\n"); + $fp->write("Line 3\n"); + + // 回到开头并读取 + $fp->seek(0); + while (!$fp->eof()) { + $line = $fp->getLine(); + if ($line !== false) { + echo $line->trim(); + echo " (长度: " . $line->trim()->length() . ")\n"; + } + } + + $fp->close(); +} +?> +``` + +--- + +## 进一步阅读 + +- **高精度类型教程**:[`docs/HIGH_PRECISION_TYPES.md`](HIGH_PRECISION_TYPES.md) +- **类型系统规范**:[`docs/NATIVE_TYPES.md`](NATIVE_TYPES.md) +- **通用方法实现**:[`src/Php/UniversalMethodCall.php`](../src/Php/UniversalMethodCall.php) +- **集成测试**: + - [`tests/aot/string_method/`](../tests/aot/string_method/) — String 通用方法测试 + - [`tests/aot/array_method/`](../tests/aot/array_method/) — Array 通用方法测试 + - [`tests/aot/stream_method/`](../tests/aot/stream_method/) — Stream 通用方法测试 + - [`tests/aot/bigint/`](../tests/aot/bigint/) — BigInt 通用方法测试 + - [`tests/aot/decimal/`](../tests/aot/decimal/) — Decimal 通用方法测试