TypePHP 编译器 https://swoole.com/aot/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

143 lines
6.9 KiB

# yield / Generator
TypePHP 将包含 `yield``yield from` 的函数、方法、匿名函数和箭头函数编译为 Fiber generator。调用时不会立即执行 generator 函数体,而是返回一个全局 `\FiberGenerator` 对象;第一次迭代、调用 `current()`、`valid()`、`send()` 或 `throw()` 时才创建并启动 Fiber。
生成的 C++ 函数体运行在 Fiber 中。每次 `yield` 都通过 `Fiber::suspend()` 将 key/value 交给迭代驱动方,并在 `next()`、`send()` 或 `throw()` 时恢复原来的 C++ 调用栈。运行时使用 `NEW`、`RUNNING`、`SUSPENDED`、`CLOSED_RETURNED` 和 `CLOSED_FAILED` 区分未启动、执行中、挂起、正常返回和异常关闭。
## 迭代互操作
以下组合已经可以工作:
| 驱动方 | 被迭代对象 |
|---|---|
| TypePHP Native `foreach` | TypePHP Native generator |
| TypePHP Native `foreach` | 动态 PHP 返回的 Zend `Generator` |
| ZendVM `foreach` | TypePHP Native generator 返回的 `\FiberGenerator` |
| TypePHP `yield from` | 数组、`Iterator`、`IteratorAggregate`、Zend `Generator`、`\FiberGenerator` |
`\FiberGenerator` 实现了 `Iterator`,其接口签名与 PHP 一致:
```php
rewind(): void
next(): void
valid(): bool
current(): mixed
key(): mixed
send(mixed $value): mixed
throw(Throwable $exception): mixed
getReturn(): mixed
```
当前状态机已经覆盖首次和重复 `rewind()`、未启动时的 `next()`/`send()`/`throw()`、关闭后的调用、自动整数 key、正常及异常 `getReturn()`、异常传播和挂起状态析构时执行 `finally`
## 与 PHP 的差异
### 不是 Zend Generator
TypePHP generator 返回的是全局 `\FiberGenerator`,不是 PHP 内置的 `Generator`
```php
$generator instanceof Iterator; // true
$generator instanceof Generator; // false
```
因此存在以下差异:
- generator 函数不能声明精确返回类型 `Generator`
- 可以使用 `Iterator`、`Traversable`、`iterable`、`object`、`mixed`,或包含这些兼容类型的联合类型。
- `ReflectionGenerator` 只接受 Zend `Generator`,不能用于 `\FiberGenerator`
- `get_class()`、Reflection class 信息和异常栈中的类名与 Zend `Generator` 不同。
- 不保证 `var_dump()`、调试属性及内部对象布局与 Zend `Generator` 一致。
- `\FiberGenerator` 是 final 运行时内部类型,禁止业务代码直接实例化、继承、clone 或序列化。
### 不支持引用 Generator
以下 PHP 语法暂不支持:
```php
function &values(): iterable
{
yield $value;
}
foreach (values() as &$value) {
}
```
TypePHP 不支持:
- generator 函数或方法按引用返回。
- by-reference yield 语义。
- 对 generator 使用按引用 `foreach`
- 通过 generator 维持元素引用身份。
`current()`、`send()`、`throw()` 和 `getReturn()` 都按普通 PHP 值返回;运行时会解除 `INDIRECT``REFERENCE` 包装,不会返回引用容器。
### 参数限制
TypePHP generator 暂不支持以下参数声明:
- 按引用参数,例如 `function values(&$value)`
- 可变参数,例如 `function values(...$values)`
- 按引用可变参数,例如 `function values(&...$values)`
普通参数、默认值、联合类型参数、对象参数和方法中的 `$this` 可以使用。参数类型检查及 constructor property promotion 在 generator 对象创建时执行,函数体仍保持延迟执行。
### Traversable 边界
`yield from` 与 TypePHP Native 对象 `foreach` 使用不同的底层路径。`foreach` 统一通过 PHPX `ForeachIterator` 驱动数组、普通对象和 Zend `Traversable`;`yield from` 仍由 generator 自己完成委托。
这意味着:
- 用户态 `Iterator`、`IteratorAggregate`、Zend `Generator` 以及扩展提供的内部 `Traversable` 均通过类的 `get_iterator` handler 迭代。
- 普通对象直接遍历实时 property table,并按当前 TypePHP 类作用域执行 public、protected、private 可见性检查。
- 不读取 key 的 `foreach ($iterable as $value)` 不会调用 `Iterator::key()`
- TypePHP `yield from` 会检测 `IteratorAggregate::getIterator()` 返回自身或形成对象环,并抛出异常。
- TypePHP Native `foreach``IteratorAggregate` 的展开与环检测交给 Zend iterator handler,行为与当前 PHP 运行时保持一致。
- 非法 `getIterator()` 返回值的异常类型、消息文本和栈信息可能与 ZendVM 不完全一致。
### Fiber 可观察差异
Zend `Generator` 是 ZendVM 的专用执行对象;TypePHP generator 使用 PHP Fiber 保存完整 C/C++ 栈,因此:
- 运行环境必须提供 PHP Fiber。
- 异常栈中可能出现 `Fiber`、内部 closure 或 TypePHP runtime frame。
- 文件名、行号和调用栈形状不保证与 `ReflectionGenerator` 或 Zend Generator 完全相同。
- 正常迭代、异常传播和挂起析构的 `finally` 已有回归测试,但复杂对象环、请求关闭、进程退出及析构函数再次抛出异常时的执行顺序仍可能与 Zend Generator 不同。
- Fiber 被强制关闭时使用 Zend 内部 graceful-exit 展开 C++ 栈;该对象不是业务代码可捕获或依赖的公开异常类型。
### yield from 差异
数组、普通 Iterator 和 generator 的 key/value 转发、generator return value、`send()` 和 `throw()` 委托已经实现,但底层实现不是 Zend `yield from` opcode:
- 委托通过 `rewind()`、`valid()`、`key()`、`current()`、`next()`、`send()`、`throw()` 和 `getReturn()` 方法完成。
- 自定义 Iterator 方法产生的副作用、异常栈和调用次数应避免依赖 Zend Generator 的内部实现细节。
- 非 generator Iterator 的 `yield from` 结果为 `null`;只有 Zend `Generator``\FiberGenerator` 会读取 `getReturn()`
## 性能差异
Fiber generator 不会改变普通数组或普通容器 `foreach` 的生成代码。只有实际创建并驱动 generator 时才产生额外成本。
每个 yield 目前需要:
- Fiber suspend/resume。
- `Iterator` 方法调用。
- generator 状态和对象属性读写。
- key/value payload 数组的创建与释放。
- `yield from` 场景中的额外委托调用。
因此 TypePHP Fiber generator 通常比 Native C++ 数组 `foreach` 慢,也可能比 Zend 专用 Generator opcode 慢。高频、短元素迭代应优先使用数组或 Native 容器;generator 更适合延迟计算、流式处理和需要保存完整 Native 调用栈的场景。
## 不兼容清单
当前不能依赖以下 PHP 行为:
- 返回对象是 Zend `Generator`
- `instanceof Generator`
- 返回类型声明为精确的 `Generator`
- `ReflectionGenerator`
- generator 按引用返回、by-reference yield 或按引用 foreach。
- generator 的按引用参数或可变参数。
- 调试输出及内部属性与 Zend Generator 相同。
- 所有内部扩展 `Traversable` 都能通过 Zend iterator handlers 迭代。
- Fiber 关闭、复杂析构和进程退出时与 Zend Generator 完全相同的栈及析构顺序。