
# 已知限制

Phasis 致力于在所有可观测的规范测试中与 V8 表现一致。本页列出了这种一致性在实践中的局限，通常源于树遍历 PHP 解释器与 JIT 编译的 C++ 引擎之间存在根本差异。

## 性能上限

该引擎采用树遍历方式，搭配机会式字节码 VM 和 JsToPhp 转译器；后者会将数值密集型 / 局部变量密集型主体（包括 `obj.method()`、`new Ctor()` 和 `this.member` 形态）降级转换为原生 PHP 闭包，供 PHP 8.5 的追踪式 JIT 编译为机器码。大多数热点分派都在 VM 的 switch 循环中或直接在 JIT 编译的 PHP 内完成，而非通过遍历 AST 完成；但每条 VM 操作码仍要承担 PHP 分派开销，而 JsToPhp 尚未处理的任何形态都会回退到较慢的 VM。受分派开销限制的工作负载，其性能上限大约比 V8 慢 **~100×**。

对于嵌入式工作负载，例如在 PHP 数据上运行用户提供的逻辑、内容转换流水线和验证规则，性能上限很少会造成影响。V8 耗时 100 毫秒；Phasis 耗时 10 秒；两者都远低于请求超时。要进一步缩小差距，需要让 JsToPhp 支持更多形态（参数类型推断、`await` / `yield*` 降级转换），或直接生成 opcache 操作码，而不是通过 `eval()` 生成。参阅[基准测试 § 路线图](https://phasis.inline0.dev/zh/docs/advanced/benchmarks#roadmap)。

## 多代理并发

JavaScript 的 `SharedArrayBuffer` + `Atomics.wait` + `postMessage` 模型假设宿主可以运行真正的 OS 线程。PHP 无法做到这一点（除非使用 `pcntl_fork`，但并非所有宿主都提供该功能）。

Phasis 会模拟单个代理 — `Atomics.wait` 会立即返回 "not-equal" 或 "timed-out"，`notify` 始终返回 0。这能通过所有以单个代理运行的 test262 测试（输入验证、错误路径、空操作 notify），涵盖 376 Atomics 测试中的 376。

不支持实际需要两个并发代理相互观察的测试。要实现真正的并行，请在 PHP 层级使用工作线程队列（Redis、Beanstalkd、…）。

## Realm 语义

`ShadowRealm` 已实现，但每个子 realm 都与宿主运行在同一个 PHP 进程中。宿主函数可能会让引用跨 realm 泄漏，因此请谨慎隔离 PHP 代码。规范允许这种情况；V8 会通过单独的 v8::Isolate 实例实施更严格的隔离。

`createRealm()`（test262 宿主钩子）会在内部创建新的 `Phasis\Engine`，并桥接标准库。

## 正则表达式引擎选择

Phasis 使用 PHP 的 PCRE2 作为主要正则表达式后端，并为 PCRE2 无法精确处理的情况提供自定义匹配器（带捕获的后行断言、带重置的量词组、某些 `/v` 标志语义）。自定义匹配器比 PCRE2 慢，仅在分派逻辑检测到不兼容模式时使用。

值得注意的后果是：PCRE2 的大小写折叠表可能与 V8 的 ICU 版本不同，尤其是在较旧的 Linux 发行版上（Ubuntu 22.04 自带 ICU 70；Phasis 捆绑了 ICU 76+ 折叠表，以覆盖宿主版本差异对 `/iu` 模式的影响）。

## 内存模型

PHP 的引用计数 GC 与 V8 的追踪式垃圾回收器不同。由宿主闭包捕获的 PHP 对象只要仍被引擎持有，这些闭包就会一直存活；它们之间的循环引用要等到销毁 `Engine` 后才会被回收。对于不希望一直保留的缓存，请使用 `WeakMap` / `WeakRef`。

`process.memoryUsage()` 以及类似的 V8 专属内省 API 尚未实现。

## 模块加载器

ES 模块加载器会根据文件系统解析相对导入，但不支持打包器约定：

- 不支持 `node_modules` 解析。
- 不支持 `package.json` `exports` 映射。
- 不推断文件扩展名（必须写 `import './foo.mjs'`，而不是 `import './foo'`）。
- 不支持 HTTP 导入。

这符合纯 ECMAScript 规范，但不符合 Node 的行为。没有用于自定义模块解析器的钩子。要使用 npm 包，请先用 esbuild 等工具将其打包成单个脚本或模块，再传给 Phasis。

## Wasm

`WebAssembly` 尚未实现。没有可供绑定的 PHP wasm 运行时。

## 跳过的仅限浏览器 Web API

Phasis 提供完整的 **Web Platform Pack**（`URL`、`TextEncoder`/`TextDecoder`、`atob`/`btoa`、`structuredClone`、`performance`、`DOMException`）、完整的 **Fetch Pack**（`fetch`、`Request`、`Response`、`Headers`、`Body`、`AbortController`/`AbortSignal`、`Blob`/`File`、`FormData`、`EventTarget`/`Event`、完整 WHATWG Streams、`navigator`），以及 `crypto`（`getRandomValues`、`randomUUID`、完整 `SubtleCrypto`：digest、HMAC、AES-GCM/CBC/CTR、RSA-OAEP/PSS/PKCS1、ECDSA、ECDH、HKDF、PBKDF2）、`WebSocket`（构造函数 + RFC 6455 帧编解码器，可替换传输方式）、`XMLHttpRequest`（基于 fetch 传输）、以及真正的事件循环（`setTimeout`、`setInterval`、`clearTimeout`、`clearInterval`、`queueMicrotask`、`AsyncContext`）。参阅[Web API](https://phasis.inline0.dev/zh/docs/compatibility/web-apis)。

未提供的功能：

- `window`、`document` 和 DOM 树 — Phasis 并非浏览器。
- `localStorage`、`sessionStorage`、`IndexedDB` — 这些属于宿主端职责；持久化由嵌入方负责。
- `Worker`、`SharedWorker`、`MessageChannel` — 需要真正的 OS 线程，而 PHP 不提供此功能。
- Service Workers、Push API、Notifications、BroadcastChannel — 在浏览器之外毫无用处。

## Node 全局对象

`process`、`Buffer`、`__dirname`、`__filename`、`require()` — 这些也都不存在。它们是 Node 的宿主约定，并非语言功能。

如果要移植 Node 代码，请将 `require('foo')` 替换为 `import 'foo'`，将 `process.env.X` 换成绑定到宿主的 `env.X`，并从 PHP 传入相当于 `__dirname` 的路径。

## 十进制精度

ECMAScript 数值是 IEEE 754 双精度浮点数。与所有其他 JS 引擎一样，进行有效位数超过 15 位的十进制运算会丢失精度。需要精确整数运算时请使用 `BigInt`；需要精确的小数运算时，请使用 JS 十进制库。

PHP 的 `bcmath` 在内部用于 `BigInt`；与 V8 的唯一实际差异是，大数值的 bigint 运算在 PHP 中比在 V8 的 C++ 实现中更慢。数值结果完全一致。
