35 KiB
AOT 编译器通用方法(Universal Methods)使用教程
本教程全面介绍 AOT 编译器的**通用方法(Universal Methods)**系统——一种在原生类型上通过 $value->method() 语法进行零开销方法调用的机制。
目录
- 什么是通用方法
- 设计原理
- 支持的类型一览
- Int 整型方法
- Float 浮点型方法
- Bool 布尔型方法
- String 字符串方法
- Array 数组方法
- Stream 流方法
- Big* 高精度类型方法
- 方法链式调用
- 可变方法 vs 不可变方法
- Var 类型的方法查找
- 扩展方法:自动发现
- 完整示例
1. 什么是通用方法
通用方法(Universal Methods)允许你在 PHP 原生类型的变量上直接调用方法,就像在对象上调用一样。编译器在编译时将这些方法调用翻译为对应的 C 函数或 C++ 方法调用,生成零开销的本地代码。
// 字符串上直接调用方法
$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 编译期消解
每个通用方法调用在编译时经过以下步骤:
- 类型推断:编译器推断调用者(receiver)的类型(Int / Float / String / Array / Stream / BigInt / Decimal / BigFloat / Var)
- 方法查找:在该类型的方法表中查找方法定义
- 参数校验:检查参数个数是否在
min_args~max_args范围内 - 代码生成:根据 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)
$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* 类型的不可变语义不同。
$a = 100;
$b = $a->add(50); // $a 变为 150,$b 也是 150(返回新值)
4.2 数学函数
$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 三角函数
$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 类型转换
$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)。
$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 类型仅有两个转换方法:
$b = true;
$b->toInt(); // (int) $b → 1
$b->toString(); // (string) $b → "1"
7. String 字符串方法
String 拥有最丰富的方法集,涵盖日常开发的大部分字符串操作需求。
7.1 基本操作
$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 搜索与比较
$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 截取与替换
$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("<br><p>"); // strip_tags($s, tags)
7.4 拆分与连接
$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 编码与转义
$s = "hello world & <test>";
$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 与校验
$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 正则匹配
$s = "hello world";
// match(pattern) → 返回匹配数组
$result = $s->match("/hello/");
// matchAll(pattern) → 返回所有匹配结果
$results = $s->matchAll("/[a-z]+/");
7.8 序列化
$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_* 函数族:
$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 函数对应:
$s = "hello";
$s->equals("hello"); // C++ String.equals() —— 值比较
$s->append(" world"); // C++ String.append() —— 追加(修改原值)
8. Array 数组方法
Array 方法分为只读方法和变异方法两类。变异方法会直接修改原数组变量。
8.1 基本信息
$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 增删改查
$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 遍历与聚合
$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 排序
$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 集合运算
$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 提取与切片
$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 字符串相关
$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 序列化
$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 类型转换
$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 读写
$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 元数据与状态
$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 控制操作
$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 操作
// 服务端
$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 流拷贝
$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 方法
$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 方法
$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 方法
$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 节。
11. 方法链式调用
通用方法的返回值类型是编译时已知的,因此可以直接链式调用:
// 字符串链式调用
$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++ 原生变量,会修改原值:
$a = 100;
$b = $a->add(50); // 这一步既修改了 $a(变为 150),又返回新值给 $b
echo $a; // 150 —— 已被修改
echo $b; // 150
$a->inc(); // $a 变为 151
$a->dec(); // $a 变为 150
Array 的变异方法会修改原数组:
$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 的所有方法都不修改原值,返回新创建的对象:
$a = std::bigInt(100);
$b = $a->add(50); // $a 依然是 100,$b 是 150
$a = std::bigInt(100);
$a->add(50); // 返回值被丢弃!$a 依然是 100
String 的大部分方法也返回新值:
$s = "hello";
$upper = $s->upper(); // $s 依然是 "hello",$upper 是 "HELLO"
例外:String.append() 是变异方法(direct_method_mutate),会修改原字符串:
$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)。
// $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
declare(strict_types=1);
use native_types;
/**
* 扩展方法:判断 Int 是否为素数
* 命名:类型前缀 int_ + snake_case 方法名 is_prime
*/
function int_is_prime(int $n): bool {
if ($n < 2) return false;
for ($i = 2; $i * $i <= $n; $i++) {
if ($n % $i == 0) return false;
}
return true;
}
/**
* 扩展方法:数组扁平化
* 命名:类型前缀 array_ + snake_case 方法名 flatten
*/
function array_flatten(array $arr): array {
$result = [];
array_walk_recursive($arr, function($v) use (&$result) {
$result[] = $v;
});
return $result;
}
function main(): void {
// 自动发现:int_is_prime → $n->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
declare(strict_types=1);
use native_types;
function main(): void {
$raw = " <h1>Hello World!</h1> \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
declare(strict_types=1);
use native_types;
function main(): void {
$data = [5, 2, 8, 1, 9, 3, 7];
echo "原始: " . $data->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
declare(strict_types=1);
use native_types;
function main(): void {
// 大整数阶乘(使用复合赋值,更简洁)
$n = 50;
$result = std::bigInt(1);
for ($i = 2; $i <= $n; $i++) {
$result *= $i;
}
$digits = strlen($result->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
declare(strict_types=1);
use native_types;
function main(): void {
// 写入文件
$fp = fopen("data.txt", "w+");
$fp->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 - 类型系统规范:
docs/NATIVE_TYPES.md - 通用方法实现:
src/Php/UniversalMethodCall.php - 集成测试:
tests/aot/string_method/— String 通用方法测试tests/aot/array_method/— Array 通用方法测试tests/aot/stream_method/— Stream 通用方法测试tests/aot/bigint/— BigInt 通用方法测试tests/aot/decimal/— Decimal 通用方法测试