Phasis
Un moteur JavaScript écrit en PHP : ES2024+, les 50 506 tests test262 réussis (instantané du 2025-05-05), avec URL, l’encodage, structuredClone, fetch et Streams.
composer require phasis/phasisDernière version v0.4.2 MIT PHP 8.2+, ext-bcmath, ext-curl, ext-mbstring
./vendor/bin/phasis -e '1 + 2 * 3'
# 7
./vendor/bin/phasis -e '[1, 2, 3].map(x => x * x).join(",")'
# 1,4,9
./vendor/bin/phasis path/to/script.jsEn pratique
Un seul objet moteur exécute JavaScript dans le processus PHP et transmet des valeurs dans les deux sens.
Exécuter JavaScript depuis PHP
Phasis\Engine évalue des chaînes source et des fichiers dans le processus PHP qui l’appelle. setGlobal() convertit les tableaux PHP en tableaux et objets JavaScript, et enveloppe les closures PHP en fonctions.
La sortie console.log est mise en mémoire tampon par le moteur et récupérée avec getConsoleOutput(). Le formateur de l’onglet suivant n’a pas besoin du moteur.
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"use Phasis\Formatter\Formatter;
use Phasis\Formatter\FormatOptions;
$formatted = Formatter::formatSource(
"const a={x:1,y:2};",
'script',
new FormatOptions(useTabs: true, singleQuote: true),
);
// "const a = { x: 1, y: 2 };\n"PHP pur
Pas de exec(), pas de FFI, pas de Node. Nécessite PHP 8.2+ avec ext-curl, ext-bcmath et ext-mbstring ; la CI utilise PHP 8.4 et 8.5.
100% test262
Réussit les 50 506 tests de la suite de conformité ECMAScript à l’instantané du 2025-05-05, dans toutes les catégories, sans omission.
ECMAScript moderne
Classes, async/await, générateurs, Symbol, BigInt, Proxy, Temporal, TypedArray et Intl avec ext-intl. ES2024+ complet.
API de la plateforme Web
URL, TextEncoder et TextDecoder, atob et btoa, structuredClone, performance et DOMException sont intégrés.
fetch + Streams
Requêtes HTTP réelles depuis JS via ext-curl. API Fetch complète avec AbortSignal, Blob, FormData et WHATWG Streams.
Tests de la plateforme Web
Les sous-tests WPT importés pour fetch, headers, blob, abort, streams et encoding réussissent, à l’exception d’une liste d’exclusions pour brotli et certains cas SharedArrayBuffer.
Interopérabilité PHP ↔ JS
setGlobal() expose les closures PHP comme fonctions JS, et call() exécute les fonctions JS depuis PHP. Les valeurs sont converties à la frontière ; les tableaux sont copiés.
Intégrable
Une seule classe Engine : eval, execFile, setGlobal, call. À intégrer dans n’importe quelle application PHP ou CLI.
Limite de la pile d’appels
Une pile d’appels fixe de 4 096 cadres arrête les récursions incontrôlées avec une InternalError interceptable.
CLI
bin/phasis exécute un fichier ou un extrait -e et affiche l’AST avec --ast. Dans une copie du dépôt, bin/test262 exécute la suite de conformité.
Oracle Node.js
Les scénarios sont vérifiés par rapport à Node.js (V8). WPT et la suite de la bibliothèque s’exécutent en CI lors des pushes qui modifient le moteur ; l’exécution complète de test262 se lance à la demande, la dernière fois le 2026-06-09.
Bibliothèques réelles
Les packages 204 npm, dont acorn, mustache.js, lodash et marked, sont testés avec une sortie identique octet pour octet à celle de Node.
Pourquoi Phasis
Pas de processus Node.js
Phasis analyse lexicalement, analyse syntaxiquement et exécute JavaScript dans PHP même, sans appel exec() à Node ni FFI. Il s’installe comme package Composer.
fetch sous le contrôle de l’hôte
fetch() utilise ext-curl par défaut. setFetchTransport() remplace le transport, par exemple par une simulation dans les tests, et setFetchPolicy() peut bloquer ou réécrire chaque requête avant son envoi.
Minuteries en temps virtuel
eval() vide la boucle d’événements avant de rendre la main, et l’horloge avance jusqu’à la prochaine minuterie en attente au lieu de faire une pause. Les hôtes à longue durée de vie peuvent piloter la boucle en temps réel avec tickEventLoop().
Analyseur et formateur intégrés
L’analyseur renvoie un AST typé PHP exportable vers ESTree. Le formateur réimprime JavaScript, JSON et CSS avec une mise en page compatible avec Prettier.
Comparaison avec
Autres moyens d’exécuter ou de traiter JavaScript depuis une application PHP.
| Outil | Idéal pour | Différence avec Phasis |
|---|---|---|
| Node.js | Idéal pourNode.js exécute JavaScript sur le moteur V8 et résout les packages depuis les dossiers node_modules ; les dépendances npm se chargent donc par leur nom. | Différence avec PhasisAppeler Node depuis PHP ajoute un second environnement d’exécution et une frontière de sous-processus. Phasis s’exécute dans PHP, mais son chargeur de modules ne dispose ni de node_modules ni de la résolution des exports package.json ; les packages npm doivent donc d’abord être regroupés, par exemple avec esbuild. |
| V8Js | Idéal pourV8Js intègre le moteur V8 de Google dans PHP et exécute JavaScript dans un bac à sable dont les limites de temps et de mémoire peuvent être définies. | Différence avec PhasisV8Js est une extension compilée qui nécessite la bibliothèque libv8. Phasis est du PHP pur venant de Composer ; sa documentation indique un temps d’exécution environ 100 fois supérieur à celui de V8 pour du code limité par la distribution des appels. |
| Peast | Idéal pourPeast analyse JavaScript en AST ESTree avec PHP 5.4 et versions ultérieures, et ajoute le parcours d’arbres, les requêtes par sélecteur et le rendu. | Différence avec PhasisPhasis nécessite PHP 8.2. Son analyseur renvoie des objets nœuds typés en lecture seule avec un export ESTree ; le même package peut aussi exécuter le code. |
| Prettier | Idéal pourPrettier met en forme JavaScript, TypeScript, JSX, CSS, HTML, Markdown, YAML et bien d’autres, et s’installe depuis npm. | Différence avec PhasisLe formateur Phasis ne prend en charge que JavaScript, JSON et CSS. Ses options reprennent celles de Prettier 3.x et il s’exécute dans PHP, sans Node.js. |
Questions et réponses
Réponses courtes issues de la documentation de Phasis.
Composer nécessite PHP 8.2 ou version ultérieure avec bcmath, curl et mbstring. intl est recommandé pour les API Intl et les calendriers Temporal non-ISO, et openssl pour le chiffrement AES dans crypto.subtle.
Phasis est un interpréteur qui parcourt un arbre avec une VM à bytecode, et non un JIT. Sa documentation indique un temps d’exécution environ 100 fois supérieur à celui de V8 pour le code limité par la distribution des appels ; selon elle, cette limite compte rarement pour des usages intégrés comme les règles de validation.
Pas par leur nom. Le chargeur de modules résout les imports dans le système de fichiers, exige des extensions de fichier explicites et ne prend en charge ni node_modules ni les exports package.json ; regroupez donc le package avec un outil tel qu’esbuild et transmettez le bundle à Phasis.
Non. window, document et l’arbre DOM ne sont pas inclus, pas plus que les variables globales de Node telles que process, Buffer et require().
Elle est mise en mémoire tampon par le moteur au lieu d’être affichée. Récupérez-la avec getConsoleOutput() ou getConsoleLines(), puis videz le tampon avec clearConsole().
Un throw non intercepté remonte sous la forme Phasis\Exceptions\JsThrowable. Les erreurs levées par le moteur lui-même, comme la lecture d’une propriété de null, remontent sous la forme Phasis\Exceptions\TypeError, ReferenceError ou RangeError, et toutes étendent Phasis\Exceptions\RuntimeError. Une source invalide lève Phasis\Parser\ParseError, un Phasis\Exceptions\SyntaxError, avec la ligne, la colonne et le décalage.
Commencez par le guide de démarrage, puis consultez la référence API pour les hooks de récupération et la boucle d’événements.