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.
3.9 KiB
3.9 KiB
TypePHP 兼容性工程准则
TypePHP 的目标是保证常规应用代码正确、类型安全、稳定且高效,而不是复刻 ZendVM 每一个可观察的边缘行为。当 PHP 的精确兼容性与 PHPX 封装性、长期 可维护性或静态编译模型冲突时,应优先保护架构,并明确记录行为差异。
优先级
| 优先级 | 必须处理的范围 | 示例 |
|---|---|---|
| P0 | 崩溃、UAF、内存破坏、生命周期错误、生成非法 C++、静默破坏数据 | Cache 域混淆、对象生命周期错误、特定后端崩溃 |
| P1 | 常用语法产生明显错误结果、类型检查被绕过、普通副作用表达式被重复求值 | ArrayAccess 赋值错误写入 offsetGet() 临时值;receiver 被执行两次 |
| P2 | 依赖动态引用、回调、警告或废弃转换的低频组合 | 操作期间由回调把 array 改成 object;警告产生顺序 |
| 非目标 | 只为低频兼容细节而镜像 ZendVM 内部实现 | 向 PHPX 复制大量 zend_execute.c 或 zend_vm_def.h 代码 |
P0 和 P1 必须修复。P2 只有在方案足够小、通用且符合现有架构时才修复; 否则应归类为 Partial、Intentional Rule 或 Hard Limit。
架构边界
- 不向 PHPX 复制大段 ZendVM executor 逻辑。
- 不为单个编译器边缘场景增加 PHPX 公共 API。
- 不向生成的项目代码暴露
zval *、HashTable slot 等 Zend 存储细节。 - 新 PHPX API 必须表达通用操作,符合现有命名和所有权模型,并且不只服务于 某一个 PHPT 复现代码。
- 只有编译器能够证明安全时,才生成原生快速路径。
- 与其引入庞大的动态兼容层,更应选择清晰的 TypePHP 专有规则或诊断。
- 静态阶段能够确定的非法操作应在编译期失败。只能在运行期确定的非法操作 可以抛出稳定、统一的 PHPX 错误,不承诺错误级别、文本和时序与 Zend 完全一致。
Code Review 决策流程
接受兼容性修改前,应依次回答:
- 问题是否会导致崩溃、内存或数据破坏、非法 C++、类型规则穿透,或者破坏 常用语法?
- 编译器能否证明所需的派发方式和求值顺序?
- 是否能够使用现有 TypePHP/PHPX 抽象完成修复?
- 如果需要新增 API,它是否具有独立、通用的价值,并位于正确的层次?
- 实现是否能够跨受支持的 PHP 版本长期工作,而不需要跟踪 ZendVM 私有代码?
如果第一项是否定的,而后续问题显示实现复杂度与收益明显不成比例,应记录 兼容边界,而不是继续实现。
数组维度赋值案例
对于 $container[$key] ??= $value,需要保证的主要行为是:
- 确定为数组时写入真实 bucket;
- 确定为
ArrayAccess对象时,在正确分支调用offsetExists()、offsetGet()和offsetSet(); - 普通 receiver、key 和 RHS 表达式不会被无故重复执行;
- 不能把
offsetGet()返回的临时值当成对象维度写入目标。
这些属于常用路径的正确性。相比之下,回调在各阶段之间重新绑定 container/key、
false 转数组的废弃警告、字符串 offset 的全部返回值细节,以及警告的精确顺序,
属于边缘兼容问题。不能因此让 PHPX 复制 ZendVM 的维度处置逻辑。
TypePHP 明确将 null 数组 key 视为追加。除非经过单独的设计决策,否则必须保留 这一既有不兼容行为。
文档与测试
- 有意或部分不兼容行为必须写入
INCOMPATIBLE_PHP_FEATURES.md,必要时同时在PHP_INCOMPATIBILITY_CLASSIFICATION.md中分类。 - 回归测试应保护项目承诺支持的边界,不应强制尚未承诺的 Zend 边缘行为。
- 除非诊断文本已经成为稳定的 TypePHP 契约,否则测试不应锁死完整报错文本。
- 仅缺少测试通常可以在可靠实现合入后补齐,但不能为了满足测试接受错误的架构。