Web API
Πέρα από την ίδια την ECMAScript, το Phasis παρέχει διάφορα επίπεδα API WHATWG / W3C:
- Πακέτο Web Platform — καθαροί τύποι τιμών, χωρίς I/O:
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 όταν φορτωθεί η επέκταση PECLkjdev/php-ext-brotli),navigatorκαι το καθολικό ψευδώνυμο προδιαγραφής HTMLself+reportError. - Crypto —
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 — constructor, κωδικοποιητής πλαισίων RFC 6455, συμβάντα. Η προεπιλεγμένη μεταφορά χρησιμοποιεί
stream_socket_client· μπορεί να αντικατασταθεί μέσω τουEngine::setWebSocketTransport()για ReactPHP / Ratchet / εικονικές υλοποιήσεις στις δοκιμές. - XMLHttpRequest — βασίζεται στη μεταφορά fetch, οπότε ισχύουν τα ίδια hooks πολιτικής και η δυνατότητα εναλλαγής μεταφοράς.
- Event loop —
setTimeout,setInterval,clearTimeout,clearInterval,queueMicrotask. Επιπλέον, τοAsyncContext(Stage 3) μεταφέρει τιμές μεταξύ ορίων microtask / χρονοδιακόπτη.
Όλα τα πακέτα είναι ενεργά από προεπιλογή. Χωρίς σημαία ή ρύθμιση — το typeof fetch επιστρέφει "function" σε ένα νέο new Engine().
Συμμόρφωση
Το εισαγόμενο σώμα Web Platform Tests (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 — μαζί με χιλιάδες παραλλαγές derive-bits από τα hkdf και pbkdf2), τη μηχανή καταστάσεων WebSocket (constructor URL και επικύρωση subprotocol, σημασιολογία κωδικών κλεισίματος, send() για συμβολοσειρές / ArrayBuffer / προβολές typed-array / Blob, binaryType, bufferedAmount, ασύγχρονο κλείσιμο κατά τη σύνδεση, δυνατότητα null στους χειριστές συμβάντων) και μεγάλο μέρος των Fetch + XMLHttpRequest — μηχανή καταστάσεων responseType, μεταβάσεις abort/send, κεφαλίδες safelist CORS, μεταφορτώσεις multipart FormData, XMLHttpRequestUpload + ProgressEvent, επικύρωση αρχικοποίησης Response/Headers, στατικοί constructors Response.json / Response.error, αφαίρεση Authorization από αλυσίδες ανακατεύθυνσης, URL blob URL.createObjectURL, κατανάλωση body-mixin (text / blob / json / formData / arrayBuffer), Response.clone με ροές tee που βασίζονται στο structuredClone και σφάλμα δικτύου σε σύγχρονο XHR. Περνά κάθε εισαγόμενο υποτεστ.
Για μεγαλύτερη κάλυψη, προσθέστε ένα νέο *.any.js στο tests/Wpt/fixtures/<category>/ — ο runner θα το εντοπίσει αυτόματα. Οι σουίτες XHR/Fetch εκτελούνται από το tests/Wpt/fetch-server.php (εξομοιωτής ενός αρχείου για τους handlers resources/*.py του WPT)· η σουίτα WebSocket χρησιμοποιεί ανενεργή μεταφορά μέσα στον runner, η οποία συνθέτει open/echo/close και επαρκεί για τα εισαγόμενα fixtures. Εκτελέστε το σώμα δοκιμών μόνοι σας:
bin/wpt # everything
bin/wpt --category headers --verbose # one area, per-subtest output
bin/wpt --json # machine-readable
Παράδειγμα fetch()
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. Τα σώματα μεταδίδονται σε ροή μέσω του WHATWG ReadableStream που παρέχει το Phasis, επομένως λειτουργεί και το for await (const chunk of res.body).
Παράδειγμα AbortController
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 αντί να εκπέμπει συμβάν στη μέση του event loop· λειτουργεί με μεταφορές fetch που ελέγχουν το σήμα κατά τη μεταφορά).
Έλεγχοι από το ενσωματώνον περιβάλλον (πλευρά PHP)
Τρία hooks στην κλάση Phasis\Engine ελέγχουν τη συμπεριφορά fetch:
$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 είναι προαιρετική — δεν υπάρχει προεπιλεγμένη αποθήκη και δεν αποθηκεύονται cookie αν δεν ορίσετε μία.
Παράδειγμα Streams
// 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 βρίσκονται στο καθολικό αντικείμενο. Οι microtasks (χειριστές Promise, queueMicrotask) ολοκληρώνονται πριν από κάθε macrotask· οι χρονοδιακόπτες ενεργοποιούνται με σειρά προθεσμίας μέσω ουράς ανά realm.
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
Καθοδηγείται από το Engine::runEventLoop() του Phasis, το οποίο καλούν αυτόματα τα eval() και evalAsModule() όταν ολοκληρωθεί ο σύγχρονος κώδικας. Από προεπιλογή, ο βρόχος λειτουργεί με εικονικό χρόνο — το setTimeout(cb, 5000) δεν περιμένει PHP· ο βρόχος προωθεί το ρολόι του έως την προθεσμία, ώστε το πρόγραμμα να ολοκληρωθεί γρήγορα. Τα ενσωματώνοντα περιβάλλοντα πραγματικού χρόνου καθοδηγούν τα ίδια το tickEventLoop(). Δείτε το API: Event loop για τις λειτουργίες στην πλευρά του host.
Τι δεν περιλαμβάνεται
Αυτά τα API που αφορούν μόνο προγράμματα περιήγησης ή το μοντέλο εκτέλεσης είναι σκόπιμα εκτός πεδίου εφαρμογής — δείτε τους Γνωστούς περιορισμούς για όλες τις λεπτομέρειες:
window,document, το δέντρο DOMlocalStorage,sessionStorage,IndexedDBWorker,SharedWorker,MessageChannel- Service Workers, Push API, Ειδοποιήσεις, BroadcastChannel
Τα περισσότερα από αυτά μπορούν να υλοποιηθούν μέσω polyfill από το ενσωματώνον περιβάλλον με το $engine->setGlobal(), αν χρειάζεται.
Επέκταση του σώματος 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>/. Ο runner θα το εντοπίσει στην επόμενη εκτέλεση· οι αποτυχίες καταγράφονται μέσω του bin/wpt --verbose.