Saltar al contenido

Phasis

Un motor JavaScript escrito en PHP: supera ES2024+ y todas las pruebas test262 50,506 (instantánea del 2025-05-05), además de URL, codificación, structuredClone, fetch y Streams.

Terminal
composer require phasis/phasis

Última versión v0.4.2 MIT PHP 8.2+, ext-bcmath, ext-curl, ext-mbstring

En la práctica

Un único objeto de motor ejecuta JavaScript dentro del proceso PHP y transfiere valores en ambas direcciones.

Ejecutar JavaScript desde PHP

Phasis\Engine evalúa cadenas de código fuente y archivos en el proceso PHP que lo invoca. setGlobal() convierte los arrays de PHP en arrays y objetos de JavaScript, y envuelve los cierres de PHP como funciones.

La salida de console.log se almacena en el motor y se recupera con getConsoleOutput(). El formateador de la segunda pestaña no necesita ningún motor.

Motor
use Phasis\Engine;

$engine = new Engine();

// Evaluate an expression
$engine->eval('1 + 2 * 3');                  // 7

// Run a file
$engine->execFile('/path/to/script.js');

// Bridge a PHP value into JS (arrays are copied)
$engine->setGlobal('config', ['debug' => true]);
$engine->eval('config.debug');               // true

// console.log is buffered, not printed
$engine->eval('console.log("hi")');
echo $engine->getConsoleOutput();            // hi

// Expose a PHP closure as a JS function
$engine->setGlobal('greet', fn(string $name) => "Hello, $name");
$engine->eval('greet("world")');             // "Hello, world"

PHP puro

Sin exec(), sin FFI ni Node. Requiere PHP 8.2+ con ext-curl, ext-bcmath y ext-mbstring; la CI ejecuta PHP 8.4 y 8.5.

100% test262

Supera todas las pruebas 50.506 de la suite de conformidad de ECMAScript en su instantánea del 2025-05-05, todas las categorías y sin omisiones.

ECMAScript moderno

Clases, async/await, generadores, Symbol, BigInt, Proxy, Temporal, TypedArray e Intl con ext-intl. ES2024+ completo.

API de plataforma web

URL, TextEncoder y TextDecoder, atob y btoa, structuredClone, performance y DOMException vienen integrados.

fetch + Streams

HTTP reales desde JS mediante ext-curl. Fetch API completo con AbortSignal, Blob, FormData y WHATWG Streams.

Pruebas de la plataforma web

Las subpruebas importadas de WPT para fetch, headers, blob, abort, streams y encoding pasan, salvo una lista permitida de casos de brotli y SharedArrayBuffer.

Interoperabilidad PHP ↔ JS

setGlobal() expone los cierres de PHP como funciones de JS, y call() ejecuta funciones de JS desde PHP. Los valores se convierten en el límite; los arrays se copian.

Integrable

Una sola clase Engine: eval, execFile, setGlobal, call. Incorpórela a cualquier aplicación PHP o CLI.

Límite de pila de llamadas

Una pila de llamadas fija de 4096 marcos detiene la recursión descontrolada con un InternalError capturable.

CLI

bin/phasis ejecuta un archivo o fragmento -e e imprime el AST con --ast. En un clon del repositorio, bin/test262 ejecuta la suite de conformidad.

Oráculo Node.js

Los escenarios se comprueban con Node.js (V8). WPT y la suite de la biblioteca se ejecutan en CI con las inserciones que afectan al motor; la ejecución completa de test262 se realiza bajo demanda, la última vez el 2026-06-09.

Bibliotecas reales

Se prueban 204 paquetes npm, entre ellos acorn, mustache.js, lodash y marked, con resultados idénticos byte por byte a Node.

Por qué Phasis

Sin proceso Node.js

Phasis analiza léxica y sintácticamente, y ejecuta JavaScript dentro de PHP, sin llamadas exec() a Node ni FFI. Se instala como paquete Composer.

fetch bajo control del host

fetch() usa ext-curl de forma predeterminada. setFetchTransport() sustituye el transporte, por ejemplo por una simulación en las pruebas, y setFetchPolicy() puede denegar o reescribir cada solicitud antes de enviarla.

Temporizadores con tiempo virtual

eval() vacía la cola de eventos antes de devolver el control, y el reloj avanza hasta el siguiente temporizador pendiente en vez de esperar. Los procesos anfitriones de larga duración pueden controlar el bucle en tiempo real con tickEventLoop().

Analizador y formateador incluidos

El analizador devuelve un AST tipado de PHP que se exporta a ESTree. El formateador vuelve a imprimir JavaScript, JSON y CSS con una disposición compatible con prettier.

Comparación con

Otras formas de ejecutar o procesar JavaScript desde una aplicación PHP.

Phasis en comparación con otras herramientas
Node.js Ideal paraNode.js ejecuta JavaScript en el motor V8 y resuelve paquetes desde carpetas node_modules, así que las dependencias de npm se cargan por nombre. Diferencia con PhasisInvocar Node desde PHP añade un segundo entorno de ejecución y un límite de subproceso. Phasis se ejecuta dentro de PHP, pero su cargador de módulos no dispone de node_modules ni resuelve exportaciones de package.json, así que primero hay que empaquetar los paquetes npm, por ejemplo con esbuild.
V8Js Ideal paraV8Js integra el motor de Google V8 en PHP y ejecuta JavaScript en un entorno aislado que se puede limitar por tiempo y memoria. Diferencia con PhasisV8Js es una extensión compilada que necesita la biblioteca libv8. Phasis es PHP puro de Composer, y su documentación lo sitúa aproximadamente a 100 veces el tiempo de ejecución de V8 en código limitado por el despacho.
Peast Ideal paraPeast analiza JavaScript en un AST ESTree con PHP 5.4 y versiones posteriores, y añade recorrido del árbol, consultas de selectores y renderizado. Diferencia con PhasisPhasis requiere PHP 8.2. Su analizador devuelve objetos de nodo tipados y de solo lectura con exportación a ESTree, y el mismo paquete también puede ejecutar el código.
Prettier Ideal paraPrettier formatea JavaScript, TypeScript, JSX, CSS, HTML, Markdown, YAML y más, y se instala desde npm. Diferencia con PhasisEl formateador de Phasis solo admite JavaScript, JSON y CSS. Sus opciones siguen las de prettier 3.x y se ejecuta en PHP, sin Node.js.

Preguntas y respuestas

Respuestas breves de la documentación de Phasis.

Empieza con la guía de inicio y luego consulta la referencia de API para los hooks de fetch y el bucle de eventos.