parent
e90d6246ec
commit
e4d8cbb798
7 changed files with 168 additions and 1 deletions
@ -0,0 +1,84 @@ |
||||
# TypePHP Compatibility Engineering Policy |
||||
|
||||
TypePHP aims to make ordinary application code correct, type-safe, stable and |
||||
fast. It does not attempt to reproduce every observable ZendVM edge case. When |
||||
exact PHP compatibility conflicts with PHPX encapsulation, maintainability or |
||||
static compilation, the architecture takes precedence and the difference must |
||||
be documented. |
||||
|
||||
## Priority |
||||
|
||||
| Priority | Required work | Examples | |
||||
|---|---|---| |
||||
| P0 | Crashes, use-after-free, memory corruption, lifecycle bugs, invalid generated C++, and silent data corruption | Cache-domain confusion, invalid object lifetime, backend-specific crashes | |
||||
| P1 | Clearly wrong results in common language constructs, bypassed type checks, or repeated evaluation of ordinary side-effecting expressions | `ArrayAccess` assignment writing to an `offsetGet()` temporary; evaluating a receiver twice | |
||||
| P2 | Uncommon combinations whose exact behavior depends on dynamic aliases, callbacks, warnings, or deprecated conversions | A callback rebinding an array to an object during an operation; exact warning order | |
||||
| Non-goal | Mirroring ZendVM internals solely for obscure compatibility | Copying large parts of `zend_execute.c` or `zend_vm_def.h` into PHPX | |
||||
|
||||
P0 and P1 issues must be fixed. P2 behavior is fixed only when the solution is |
||||
small, general, and consistent with the existing architecture. Otherwise it is |
||||
classified as Partial, an Intentional Rule, or a Hard Limit. |
||||
|
||||
## Architectural Boundaries |
||||
|
||||
- Do not copy substantial ZendVM executor logic into PHPX. |
||||
- Do not add a public PHPX API for one compiler edge case. |
||||
- Do not expose raw `zval *`, HashTable slots, or other Zend storage details to |
||||
generated project code. |
||||
- A new PHPX API must describe a general operation, fit the existing naming and |
||||
ownership model, and remain useful outside one PHPT reproducer. |
||||
- Emit a native fast path only when the compiler can prove it is safe. |
||||
- Prefer a clear TypePHP-specific rule or diagnostic over a large dynamic |
||||
compatibility layer. |
||||
- A statically known invalid operation should fail during compilation. A |
||||
runtime-only invalid operation may use a stable PHPX error; its exact Zend |
||||
error level, wording and timing are not part of the compatibility promise. |
||||
|
||||
## Review Decision |
||||
|
||||
Before accepting a compatibility change, answer these questions: |
||||
|
||||
1. Does the issue crash, corrupt memory/data, generate invalid C++, bypass a |
||||
type rule, or break a common construct? |
||||
2. Can the compiler prove the required dispatch or evaluation order? |
||||
3. Can the fix use existing compiler and PHPX abstractions? |
||||
4. If a new API is proposed, is it independently useful and correctly placed? |
||||
5. Will the implementation remain valid across supported PHP versions without |
||||
tracking private ZendVM code? |
||||
|
||||
If the first answer is no and the remaining answers expose disproportionate |
||||
complexity, document the boundary instead of implementing it. |
||||
|
||||
## Dimension-Assignment Example |
||||
|
||||
For `$container[$key] ??= $value`, the important behavior is: |
||||
|
||||
- a real array writes its bucket; |
||||
- an `ArrayAccess` object uses `offsetExists()`, `offsetGet()` and |
||||
`offsetSet()` in the correct branch; |
||||
- ordinary receiver, key and RHS expressions are not evaluated more than |
||||
required; |
||||
- assigning to the temporary returned by `offsetGet()` is never treated as an |
||||
object-dimension write. |
||||
|
||||
These are common correctness requirements. By contrast, exact Zend behavior |
||||
when callbacks rebind the container or key between phases, deprecated |
||||
false-to-array conversion, every string-offset result detail, and exact warning |
||||
ordering are edge compatibility. They must not cause PHPX to duplicate the |
||||
ZendVM dimension executor. |
||||
|
||||
TypePHP intentionally treats a null array key as append. This established |
||||
language difference must be preserved unless a separate design decision changes |
||||
it. |
||||
|
||||
## Documentation and Tests |
||||
|
||||
- Every intentional or partial incompatibility must be recorded in |
||||
`INCOMPATIBLE_PHP_FEATURES.md` and classified in |
||||
`PHP_INCOMPATIBILITY_CLASSIFICATION.md` when appropriate. |
||||
- Regression tests should protect the supported boundary, not require behavior |
||||
that the project deliberately does not promise. |
||||
- Do not make exact diagnostic text part of a test unless the diagnostic is a |
||||
stable TypePHP contract. |
||||
- A test-only gap is normally completed after a sound implementation; it is not |
||||
a reason to accept an unsound architecture. |
||||
@ -0,0 +1,68 @@ |
||||
# 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 决策流程 |
||||
|
||||
接受兼容性修改前,应依次回答: |
||||
|
||||
1. 问题是否会导致崩溃、内存或数据破坏、非法 C++、类型规则穿透,或者破坏 |
||||
常用语法? |
||||
2. 编译器能否证明所需的派发方式和求值顺序? |
||||
3. 是否能够使用现有 TypePHP/PHPX 抽象完成修复? |
||||
4. 如果需要新增 API,它是否具有独立、通用的价值,并位于正确的层次? |
||||
5. 实现是否能够跨受支持的 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 契约,否则测试不应锁死完整报错文本。 |
||||
- 仅缺少测试通常可以在可靠实现合入后补齐,但不能为了满足测试接受错误的架构。 |
||||
Loading…
Reference in new issue