
# واجهات Web API

إلى جانب ECMAScript نفسها، يوفر Phasis عدة حزم من واجهات WHATWG / W3C:

- **حزمة منصة الويب** — أنواع قيم خالصة، بلا إدخال وإخراج: `URL`، `URLSearchParams`، `URLPattern`، `TextEncoder`، `TextDecoder`، `atob`، `btoa`، `structuredClone`، `performance`، `DOMException`.
- **حزمة Fetch** — HTTP فعلي من JavaScript: `fetch`، `Request`، `Response`، `Headers`، `Body`، `AbortController`، `AbortSignal`، `Blob`، `File`، `FormData`، `EventTarget`، `Event`، ومواصفات WHATWG Streams كاملة (`ReadableStream`، `WritableStream`، `TransformStream`، استراتيجيات الاصطفاف، BYOB)، و`CompressionStream` / `DecompressionStream` (gzip / deflate / deflate-raw عبر zlib؛ وbrotli عند تحميل إضافة PECL ‏`kjdev/php-ext-brotli`)، و`navigator`، والاسم البديل العام وفق مواصفات HTML `self` + `reportError`.
- **التشفير** — `crypto.getRandomValues`، و`crypto.randomUUID`، وتنفيذ `SubtleCrypto` كامل: digest ‏(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` (المرحلة 3)، الذي يمرر القيم عبر حدود المهام الدقيقة / المؤقتات.

جميع الحزم **مفعّلة افتراضيًا**. بلا أعلام أو إعداد — يعيد `typeof fetch` القيمة `"function"` على `new Engine()` جديد.

## المطابقة

تغطي مجموعة [اختبارات منصة الويب](https://github.com/web-platform-tests/wpt) المستوردة (`tests/Wpt/fixtures/`) ‏`AbortController` / `AbortSignal`، و`atob` / `btoa`، و`Blob` / `File`، و`TextEncoder` / `TextDecoder`، و`EventTarget` / `Event`، و`FormData`، و`Headers`، و`performance.now`، ومعيار Streams كاملًا (`Readable`/`Writable`/`Transform` + BYOB + الاصطفاف)، و`structuredClone`، و`URL` / `URLSearchParams`، والواجهة الكاملة لـ **`crypto` / `SubtleCrypto`** (digest وHMAC وAES-GCM/CBC/CTR وRSA-OAEP/PSS/PKCS1 وECDSA وECDH وHKDF وPBKDF2 — بما يشمل آلاف التباديل لاشتقاق البتات من `hkdf` و`pbkdf2`)، وآلة حالات **`WebSocket`** (التحقق من المُنشئ URL وبروتوكولات الاتصال الفرعية، ودلالات رموز الإغلاق، و`send()` للسلاسل / `ArrayBuffer` / عروض المصفوفات المكتوبة / `Blob`، و`binaryType`، و`bufferedAmount`، والإغلاق غير المتزامن أثناء الاتصال، وإمكانية أن تكون معالجات الأحداث null)، وشريحة واسعة من **`Fetch`** + **`XMLHttpRequest`** — آلة حالات `responseType`، وانتقالات الإلغاء/الإرسال، والرؤوس المسموح بها في CORS، وتحميلات `FormData` متعددة الأجزاء، و`XMLHttpRequestUpload` + `ProgressEvent`، والتحقق من تهيئة `Response`/`Headers`، والمُنشئان الثابتان `Response.json` / `Response.error`، وإزالة Authorization في سلسلة إعادة التوجيه، وعناوين blob من `URL.createObjectURL`، واستهلاك مزيج المتن (`text` / `blob` / `json` / `formData` / `arrayBuffer`)، وتدفقات tees من `Response.clone` ذات `structuredClone`، وإطلاق خطأ شبكة عند استخدام XHR المتزامن. اجتاز كل اختبار فرعي مستورد.

لزيادة التغطية، أضف ملف `*.any.js` جديدًا إلى `tests/Wpt/fixtures/<category>/` — وسيلتقطه المشغّل تلقائيًا. تُدار مجموعتا اختبارات XHR/Fetch بواسطة `tests/Wpt/fetch-server.php` (محاكي في ملف واحد لمعالجات WPT ‏`resources/*.py`)؛ وتستخدم مجموعة WebSocket نقلًا خاملًا داخل المشغّل يحاكي الفتح/الصدى/الإغلاق، وهو كافٍ للملفات الاختبارية المستوردة. شغّل المجموعة بنفسك:

```bash
bin/wpt                # everything
bin/wpt --category headers --verbose   # one area, per-subtest output
bin/wpt --json         # machine-readable
```

## مثال fetch()

```js
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

```js
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)

تتحكم ثلاثة خطافات في سلوك fetch ضمن الصنف `Phasis\Engine`:

```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` الافتراضي. وعاء ملفات تعريف الارتباط **اختياري صراحةً** — لا يوجد وعاء افتراضي ولا تُخزّن ملفات تعريف الارتباط ما لم توفر واحدًا.

## مثال Streams

```js
// 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`) بالكامل بين كل مهمة كبرى؛ وتُطلق المؤقتات حسب ترتيب المواعيد النهائية عبر قائمة انتظار خاصة بكل نطاق.

```js
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: حلقة الأحداث](https://phasis.inline0.dev/ar/docs/api#event-loop) للتعرف على الواجهة المتاحة للمضيف.

## ما لا يتم شحنه

واجهات API المخصصة للمتصفح أو لنموذج بيئة التشغيل هذه خارج النطاق عمدًا — راجع [القيود المعروفة](https://phasis.inline0.dev/ar/docs/compatibility/limitations) للاطلاع على التفاصيل كاملةً:

- `window`، و`document`، وشجرة DOM
- `localStorage`، و`sessionStorage`، و`IndexedDB`
- `Worker`، و`SharedWorker`، و`MessageChannel`
- Service Workers، وPush API، والإشعارات، وBroadcastChannel

يمكن للمُضمّن توفير بدائل برمجية لمعظم هذه الواجهات عبر `$engine->setGlobal()` عند الحاجة.

## توسيع مجموعة WPT

إن `tests/Wpt/upstream/` عبارة عن سحب جزئي من [web-platform-tests/wpt](https://github.com/web-platform-tests/wpt) يغطي `fetch/`، و`streams/`، و`FileAPI/`، و`xhr/formdata/`، و`dom/abort/`، و`dom/events/`. لتوسيع التغطية: انسخ أي ملف `*.any.js` من `upstream/<area>/` إلى `tests/Wpt/fixtures/<category>/`. سيلتقطه المشغّل في التشغيل التالي؛ وتُسجل حالات الإخفاق عبر `bin/wpt --verbose`.
