Limitaciones conocidas
Phasis busca comportarse de forma idéntica a V8 en todas las pruebas observables de la especificación. Esta página enumera los casos en los que esa identidad no se da en la práctica, normalmente por diferencias fundamentales entre un intérprete PHP que recorre el árbol y un motor C++ compilado con JIT.
Límite de rendimiento
El motor recorre el árbol, cuenta con una VM de bytecode oportunista y un transpilador JsToPhp que convierte cuerpos con muchos valores numéricos o variables locales (incluidas las formas obj.method(), new Ctor() y this.member) en cierres nativos de PHP, que el JIT de trazado de PHP 8.5 puede compilar a código máquina. La mayor parte del despacho en las rutas críticas ocurre dentro del bucle switch de la VM o directamente en PHP compilado con JIT, no al recorrer el AST; aun así, cada código de operación de la VM paga el coste de despacho de PHP y cualquier forma que JsToPhp aún no admita vuelve a la VM más lenta. En cargas limitadas por el despacho, el límite es aproximadamente 100× más lento que V8.
En cargas de integración —ejecutar lógica proporcionada por el usuario sobre datos PHP, canalizaciones de transformación de contenido y reglas de validación—, el límite rara vez importa. V8 tarda 100 ms; Phasis tarda 10 s; ambos quedan muy por debajo del tiempo de espera de la solicitud. Para reducir más la diferencia habría que ampliar JsToPhp a más formas (inferencia de tipos de parámetros y conversión de await / yield*) o emitir códigos de operación opcache directamente en lugar de hacerlo mediante eval(). Consulta Benchmarks § Roadmap.
Concurrencia multiagente
El modelo de JavaScript, basado en SharedArrayBuffer + Atomics.wait + postMessage, presupone que el entorno anfitrión puede ejecutar hilos reales del sistema operativo. PHP no puede hacerlo sin pcntl_fork, que no está disponible en todos los entornos anfitriones.
Phasis simula un único agente: Atomics.wait devuelve "not-equal" o "timed-out" inmediatamente; notify siempre devuelve 0. Esto supera todas las pruebas de test262 que se ejecutan con un solo agente (validación de entradas, rutas de error y notificación sin efecto), que abarcan 376 de las pruebas Atomics de 376.
No se admiten las pruebas que requieren que dos agentes concurrentes se observen entre sí. Para obtener paralelismo real, usa una cola de workers en el nivel de PHP (Redis, Beanstalkd, …).
Semántica de los realms
ShadowRealm está implementado, pero cada realm secundario se ejecuta en el mismo proceso PHP que el anfitrión. Las funciones del anfitrión pueden filtrar referencias entre realms si no aíslas con cuidado el código PHP. La especificación lo permite; V8 impone un aislamiento más estricto mediante instancias independientes de v8::Isolate.
createRealm() (el hook anfitrión de test262) crea internamente un Phasis\Engine nuevo y conecta la biblioteca estándar.
Elección del motor de expresiones regulares
Phasis usa PCRE2 de PHP como motor principal de expresiones regulares, con un comparador personalizado para los casos que PCRE2 no puede gestionar con exactitud (lookbehind con capturas, grupos cuantificados con reinicio y ciertos comportamientos de las opciones /v). El comparador personalizado es más lento que PCRE2 y solo se usa cuando el despacho detecta un patrón incompatible.
La consecuencia destacable: PCRE2 puede usar una tabla de plegado de mayúsculas y minúsculas distinta de la versión de ICU de V8, especialmente en distribuciones antiguas de Linux (Ubuntu 22.04 incluye ICU 70; Phasis incorpora una tabla de plegado ICU 76+ para corregir las diferencias del entorno anfitrión en patrones /iu).
Modelo de memoria
El recolector de basura por conteo de referencias de PHP difiere del recolector de trazado de V8. Los objetos PHP capturados por cierres del anfitrión permanecen activos mientras el motor conserve esos cierres, y los ciclos entre ellos no se recolectan hasta destruir Engine. Usa WeakMap / WeakRef para las cachés que no quieras mantener activas.
No se han implementado process.memoryUsage() ni API de introspección similares específicas de V8.
Cargador de módulos
El cargador de módulos ES resuelve las importaciones relativas según el sistema de archivos, pero no entiende las convenciones de los empaquetadores:
- Sin resolución de
node_modules. - Sin mapa de
package.jsonexports. - Sin inferencia de extensiones de archivo (debes escribir
import './foo.mjs', noimport './foo'). - Sin importaciones de HTTP.
Esto coincide con la especificación básica de ECMAScript, pero no con el comportamiento de Node. No hay ningún hook para un resolver de módulos personalizado. Para usar paquetes npm, agrúpalos con una herramienta como esbuild en un único script o módulo antes de pasarlo a Phasis.
Wasm
WebAssembly no está implementado. No hay un entorno de ejecución wasm PHP al que enlazar.
API web exclusivas del navegador que omitimos
Phasis incluye el Web Platform Pack completo (URL, TextEncoder/TextDecoder, atob/btoa, structuredClone, performance, DOMException), el Fetch Pack completo (fetch, Request, Response, Headers, Body, AbortController/AbortSignal, Blob/File, FormData, EventTarget/Event, WHATWG Streams completo, navigator), además de crypto (getRandomValues, randomUUID, SubtleCrypto completo: digest, HMAC, AES-GCM/CBC/CTR, RSA-OAEP/PSS/PKCS1, ECDSA, ECDH, HKDF, PBKDF2), WebSocket (constructor + códec de tramas RFC 6455, transporte intercambiable), XMLHttpRequest (superpuesto al transporte fetch) y un bucle de eventos real (setTimeout, setInterval, clearTimeout, clearInterval, queueMicrotask, AsyncContext). Consulta Web APIs.
Lo que no se incluye:
window,documenty el árbol DOM: Phasis no es un navegador.localStorage,sessionStorageyIndexedDB: son responsabilidades del entorno anfitrión; quien integra el motor gestiona la persistencia.Worker,SharedWorkeryMessageChannel: requieren hilos reales del sistema operativo, que PHP no proporciona.- Service Workers, Push API, Notifications y BroadcastChannel: no tienen sentido fuera de un navegador.
Variables globales de Node
process, Buffer, __dirname, __filename, require(): ninguna de estas existe. Son convenciones del entorno anfitrión de Node, no características del lenguaje.
Si estás adaptando código de Node, sustituye require('foo') por import 'foo', cambia process.env.X por un env.X vinculado al anfitrión y pasa rutas equivalentes a __dirname desde PHP.
Precisión decimal
Los números de ECMAScript son valores de doble precisión IEEE 754. La aritmética decimal con más de 15 dígitos significativos pierde precisión, igual que en cualquier otro motor JS. Usa BigInt para cálculos exactos con enteros o una biblioteca decimal de JS si necesitas exactitud con fracciones.
El bcmath de PHP se usa internamente para BigInt; la única diferencia real frente a V8 es que las operaciones bigint muy grandes son más lentas en PHP que en la implementación C++ de V8. La precisión numérica es idéntica.