Limitazioni note
Phasis mira a comportarsi in modo identico a V8 in ogni test osservabile delle specifiche. Questa pagina descrive dove, nella pratica, l'identità viene meno, di solito per differenze fondamentali tra un interprete PHP tree-walking e un motore C++ compilato con JIT.
Limiti di prestazioni
Il motore è un interprete tree-walking con una VM a bytecode usata quando possibile e un transpiler JsToPhp che converte i corpi ricchi di valori numerici e variabili locali (incluse le forme obj.method(), new Ctor() e this.member) in closure native PHP che il JIT di tracciamento di PHP 8.5 può compilare in codice macchina. La maggior parte delle operazioni di dispatch intense viene eseguita nel ciclo switch della VM o direttamente nel codice PHP compilato dal JIT, non scorrendo l'AST; tuttavia, ogni opcode della VM comporta ancora un costo di dispatch PHP, e ogni forma non ancora gestita da JsToPhp ricade nella VM più lenta. Nei carichi di lavoro limitati dal dispatch, il limite è circa 100× più lento di V8.
Per i carichi di lavoro di embedding — eseguire logica fornita dagli utenti su dati PHP, pipeline di trasformazione dei contenuti, regole di convalida — il limite massimo raramente conta. V8 è 100 ms; Phasis è 10 s; entrambi sono ben al di sotto del timeout della richiesta. Ridurre ulteriormente il divario richiederebbe insegnare a JsToPhp a gestire più forme (inferenza dei tipi dei parametri, lowering di await / yield*) oppure generare direttamente opcodes opcache invece che tramite eval(). Consultate Benchmark § Roadmap.
Concorrenza multi-agente
Il modello JavaScript SharedArrayBuffer + Atomics.wait + postMessage presuppone che l'host possa eseguire thread reali del sistema operativo. PHP non può (senza pcntl_fork, che non è disponibile su tutti gli host).
Phasis simula un singolo agente: Atomics.wait restituisce subito "not-equal" o "timed-out", mentre notify restituisce sempre 0. Questo supera tutti i test test262 eseguibili con un singolo agente (convalida dell'input, percorsi di errore, notify senza operazioni), che coprono 376 di 376 test Atomics.
I test che richiedono davvero due agenti concorrenti che si osservino a vicenda non sono supportati. Per il vero parallelismo, usate una coda di worker a livello PHP (Redis, Beanstalkd, …).
Semantica dei realm
ShadowRealm è implementato, ma ogni realm figlio viene eseguito nello stesso processo PHP dell'host. Le funzioni host possono far trapelare riferimenti tra realm, a meno che non si faccia attenzione a isolare il codice PHP. La specifica lo consente; V8 impone un isolamento più rigoroso tramite istanze separate di v8::Isolate.
createRealm() (hook host test262) crea internamente un nuovo Phasis\Engine e rende disponibile la libreria standard tramite un bridge.
Scelta del motore regex
Phasis usa PCRE2 di PHP come backend regex principale e un matcher personalizzato per i casi che PCRE2 non può gestire esattamente (lookbehind con catture, gruppi quantificati con reset, determinate semantiche dei flag /v). Il matcher personalizzato è più lento di PCRE2 e viene usato solo quando il dispatcher rileva un pattern incompatibile.
Conseguenza degna di nota: PCRE2 potrebbe avere una tabella di conversione tra maiuscole e minuscole diversa dalla versione ICU di V8, soprattutto nelle distribuzioni Linux più vecchie (Ubuntu 22.04 include ICU 70; Phasis integra una tabella di conversione ICU 76+ per correggere le differenze tra host nei pattern /iu).
Modello di memoria
Il garbage collector con conteggio dei riferimenti di PHP è diverso dal collector con tracciamento di V8. Gli oggetti PHP catturati da closure host restano attivi finché il motore mantiene quelle closure; i cicli tra loro non vengono raccolti finché non viene distrutto Engine. Per le cache che non volete mantenere attive, usate WeakMap / WeakRef.
Le API di introspezione specifiche di process.memoryUsage() e simili V8 non sono implementate.
Caricatore dei moduli
Il caricatore di moduli ES risolve gli import relativi rispetto al file system, ma non riconosce le convenzioni dei bundler:
- Nessuna risoluzione
node_modules. - Nessuna mappa
package.jsonexports. - Nessuna inferenza dell'estensione del file (dovete scrivere
import './foo.mjs', nonimport './foo'). - Nessun import HTTP.
Questo corrisponde alla specifica ECMAScript essenziale, ma non al comportamento di Node. Non esiste un hook per un resolver di moduli personalizzato. Per usare pacchetti npm, assemblateli con uno strumento come esbuild in un singolo script o modulo prima di passarli a Phasis.
Wasm
WebAssembly non è implementato. Non c'è un runtime wasm PHP a cui collegarsi.
API Web solo per browser che non includiamo
Phasis include il pacchetto Web Platform Pack completo (URL, TextEncoder/TextDecoder, atob/btoa, structuredClone, performance, DOMException), il pacchetto Fetch Pack completo (fetch, Request, Response, Headers, Body, AbortController/AbortSignal, Blob/File, FormData, EventTarget/Event, WHATWG Streams completo, navigator), oltre a crypto (getRandomValues, randomUUID, SubtleCrypto completo: digest, HMAC, AES-GCM/CBC/CTR, RSA-OAEP/PSS/PKCS1, ECDSA, ECDH, HKDF, PBKDF2), WebSocket (costruttore + codec di frame RFC 6455, trasporto sostituibile), XMLHttpRequest (basato sul trasporto fetch) e un vero event loop (setTimeout, setInterval, clearTimeout, clearInterval, queueMicrotask, AsyncContext). Consultate API Web.
Cosa non è incluso:
window,document, l'albero DOM — Phasis non è un browser.localStorage,sessionStorage,IndexedDB— aspetti gestiti dall'host; la persistenza è responsabilità dell'embedder.Worker,SharedWorker,MessageChannel— richiedono veri thread del sistema operativo, che PHP non offre.- Service Worker, Push API, notifiche, BroadcastChannel — non pertinenti al di fuori di un browser.
Globali di Node
process, Buffer, __dirname, __filename, require() — non esiste nemmeno nessuno di questi. Sono convenzioni dell'host Node, non funzionalità del linguaggio.
Se state trasferendo codice Node, sostituite require('foo') con import 'foo', scambiate process.env.X con un env.X legato all'host e passate percorsi equivalenti a __dirname da PHP.
Precisione decimale
I numeri ECMAScript sono valori double IEEE 754. L'aritmetica decimale con più di 15 cifre significative perde precisione, proprio come in qualsiasi altro motore JS. Usate BigInt per calcoli esatti con numeri interi oppure una libreria decimale JS se vi servono calcoli frazionari esatti.
PHP usa internamente bcmath per BigInt; l'unica vera differenza rispetto a V8 è che le operazioni bigint molto grandi sono più lente in PHP che nell'implementazione C++ di V8. La correttezza numerica è identica.