跳转到正文

Web API

除 ECMAScript 本身外,Phasis还提供多层 WHATWG / W3C API 套件:

  • Web 平台套件 — 纯值类型,无 I/O:URL, URLSearchParams, URLPattern, TextEncoder, TextDecoder, atob, btoa, structuredClone, performance, DOMException。
  • Fetch 套件 — 来自 JavaScript 的真实 HTTP:fetch, Request, Response, Headers, Body, AbortController, AbortSignal, Blob, File, FormData, EventTarget, Event, 完整 WHATWG Streams(ReadableStream, WritableStream, TransformStream, 排队策略, BYOB),CompressionStream / DecompressionStream(通过 zlib 支持 gzip / deflate / deflate-raw;加载 kjdev/php-ext-brotli PECL 扩展后支持 brotli),navigator,以及 HTML 规范的 self 全局别名 + reportError。
  • Crypto — crypto.getRandomValues、crypto.randomUUID 和完整 SubtleCrypto:摘要(SHA-1/256/384/512)、HMAC、AES-GCM/CBC/CTR、RSA-OAEP/PSS/PKCS1、ECDSA(P-256/384/521)、ECDH、HKDF、PBKDF2,以及 JWK 导入/导出。由 ext-openssl 提供支持。
  • WebSocket — 构造函数、RFC 6455 帧编解码器、事件。默认传输使用 stream_socket_client;可通过 Engine::setWebSocketTransport() 替换,以用于 ReactPHP / Ratchet / 测试模拟对象。
  • XMLHttpRequest — 基于 fetch 传输层,因此适用相同的策略钩子和可替换传输层。
  • 事件循环 — setTimeout, setInterval, clearTimeout, clearInterval, queueMicrotask。此外,AsyncContext(Stage 3)可在微任务/计时器边界之间传递值。

默认启用所有套件。无需标志或设置 — 在全新的 new Engine() 上,typeof fetch会返回 "function"。

一致性

导入的Web Platform Tests测试集(tests/Wpt/fixtures/)涵盖 AbortController / AbortSignal、atob / btoa、Blob / File、TextEncoder / TextDecoder、EventTarget / Event、FormData、Headers、performance.now、完整 Streams Standard(Readable/Writable/Transform + BYOB + 排队)、structuredClone、URL / URLSearchParams、完整的 crypto / SubtleCrypto 接口(摘要、HMAC、AES-GCM/CBC/CTR、RSA-OAEP/PSS/PKCS1、ECDSA、ECDH、HKDF、PBKDF2 — 包括来自 hkdf 和 pbkdf2 的数千种 derive-bits 组合)、WebSocket 状态机(构造函数 URL 和子协议验证、关闭代码语义、字符串的 send() / ArrayBuffer / 类型化数组视图 / Blob、binaryType、bufferedAmount、连接时异步关闭、事件处理器可空性),以及 Fetch + XMLHttpRequest 的广泛测试 — responseType 状态机、中止/发送转换、CORS 安全列表标头、多部分 FormData 上传、XMLHttpRequestUpload + ProgressEvent、Response/Headers 初始化验证、静态 Response.json / Response.error 构造函数、重定向链上的 Authorization 剥离、URL.createObjectURL blob URL、请求体混入消耗(text / blob / json / formData / arrayBuffer)、使用 structuredClone-d 分流流的 Response.clone,以及同步 XHR 网络错误抛出。所有导入的子测试均通过。

如需进一步扩展,请将新的 *.any.js 放入 tests/Wpt/fixtures/<category>/ — 运行器会自动检测。XHR/Fetch 测试套件由 tests/Wpt/fetch-server.php 驱动(一个单文件模拟器,用于模拟 WPT resources/*.py 处理程序);WebSocket 测试套件使用运行器内的惰性传输层来模拟 open/echo/close,足以支持已导入的测试样本。亲自运行测试集:

命令行
bin/wpt                # everything
bin/wpt --category headers --verbose   # one area, per-subtest output
bin/wpt --json         # machine-readable

fetch() 示例

JavaScript
const res = await fetch("https://api.example.com/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "alice" }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const user = await res.json();

开箱即用。默认传输层是 PHP ext-curl。请求体会通过 ReadableStream Phasis 提供的 WHATWG 流传输,因此 for await (const chunk of res.body) 也能正常工作。

AbortController 示例

JavaScript
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 5000);
try {
  const res = await fetch(slowUrl, { signal: ctrl.signal });
} catch (e) {
  if (e.name === "AbortError") {
    // request was cancelled
  }
}

AbortSignal.timeout(ms) 也已实现(惰性截止时间 — 在 aborted 读取时检查,而不是在事件循环中途触发事件;适用于在传输过程中轮询该信号的 fetch 传输层)。

嵌入器控制(PHP 端)

Phasis\Engine 类上的三个钩子可控制 fetch 行为:

PHP
$engine = new Phasis\Engine();

// 1. Swap the HTTP transport. The callable receives a request descriptor
//    and an optional AbortSignal; returns a response descriptor.
$engine->setFetchTransport(function (array $request, $signal) {
    // ... use Guzzle, Symfony HttpClient, your stub, etc.
    return [
        'status' => 200,
        'statusText' => 'OK',
        'headers' => [['content-type', 'application/json']],
        'body' => '{"data":"value"}',
    ];
});

// 2. Pre-flight policy hook — rewrite, deny, or allow.
$engine->setFetchPolicy(function ($request) {
    $url = $request->get('url')->value;
    if (!str_starts_with($url, 'https://')) {
        throw new \RuntimeException('HTTPS only');
    }
    return null;  // allow as-is; return modified Request to rewrite
});

// 3. Cookie jar. Opt-in. Accepts any object with get(url):string and
//    set(url, header):void methods. JsObject or PHP object both work.
$engine->setCookieJar($myJar);

如果未调用 setFetchTransport(),则会运行默认的 CurlTransport。Cookie jar 默认不启用 — 默认没有 Cookie jar,除非你提供,否则不会存储任何 Cookie。

Streams 示例

JavaScript
// Producer: ReadableStream that yields numbers 1..5
const rs = new ReadableStream({
  start(controller) {
    for (let i = 1; i <= 5; i++) controller.enqueue(i);
    controller.close();
  },
});

// Consumer: async iteration
for await (const value of rs) {
  console.log(value);  // 1, 2, 3, 4, 5
}

// Pipe through a transform
const ts = new TransformStream({
  transform(chunk, controller) { controller.enqueue(chunk * 2); },
});
const doubled = new ReadableStream({
  start(c) { [1, 2, 3].forEach(n => c.enqueue(n)); c.close(); }
}).pipeThrough(ts);
for await (const v of doubled) console.log(v);  // 2, 4, 6

完整 Streams 规范:ReadableStream, WritableStream, TransformStream, ReadableStreamDefaultController, ReadableByteStreamController, ReadableStreamDefaultReader, ReadableStreamBYOBReader, ReadableStreamBYOBRequest, WritableStreamDefaultController, WritableStreamDefaultWriter, TransformStreamDefaultController, ByteLengthQueuingStrategy, CountQueuingStrategy,以及 pipeTo / pipeThrough / tee。

事件循环

setTimeout、setInterval、clearTimeout、clearInterval 和 queueMicrotask 位于全局对象上。微任务(Promise 处理程序、queueMicrotask)会在每个宏任务之间执行完毕;计时器通过每个 realm 的队列按截止时间顺序触发。

JavaScript
console.log('sync');
queueMicrotask(() => console.log('microtask'));
setTimeout(() => console.log('timer'), 0);
// → sync, microtask, timer

const id = setInterval(() => console.log('tick'), 1000);
// ...later
clearInterval(id);

await new Promise(r => setTimeout(r, 50));   // common "delay" pattern works

由 Phasis 的 Engine::runEventLoop() 驱动,它会在同步代码执行完毕后自动调用 eval() 和 evalAsModule()。循环默认采用虚拟时间——setTimeout(cb, 5000) 不会休眠 PHP;循环会将时钟推进到截止时间,使程序迅速完成。使用实时环境的嵌入方自行驱动 tickEventLoop()。宿主侧接口请参见 事件循环:宿主接口API。

未发布的内容

这些仅限浏览器或运行时模型的 API 刻意不在范围内 — 完整说明请参阅已知限制:

  • window、document、DOM 树
  • localStorage、sessionStorage、IndexedDB
  • Worker、SharedWorker、MessageChannel
  • Service Worker、Push API、通知、BroadcastChannel

如有需要,大多数功能都可由嵌入器通过 $engine->setGlobal() 进行 polyfill。

扩充 WPT 测试集

tests/Wpt/upstream/ 是 web-platform-tests/wpt 的稀疏检出版本,涵盖 fetch/、streams/、FileAPI/、xhr/formdata/、dom/abort/、dom/events/。如需扩展覆盖范围,请将任意 *.any.js 文件从 upstream/<area>/ 复制到 tests/Wpt/fixtures/<category>/。运行器下次运行时会自动检测;失败会通过 bin/wpt --verbose 记录。