Phasis
Eine JavaScript-Engine in PHP: ES2024+ besteht alle 50,506 test262-Tests (Snapshot vom 2025-05-05) sowie URL, Encoding, structuredClone, fetch und Streams.
composer require phasis/phasisNeueste 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.jsIn der Praxis
Ein Engine-Objekt führt JavaScript im PHP-Prozess aus und übergibt Werte in beide Richtungen.
JavaScript aus PHP ausführen
Phasis\Engine wertet Quelltext-Strings und Dateien im PHP-Prozess aus, der es aufruft. setGlobal() wandelt PHP-Arrays in JavaScript-Arrays und -Objekte um und verpackt PHP-Closures als Funktionen.
Die Ausgabe von console.log wird in der Engine gepuffert und mit getConsoleOutput() ausgelesen. Der Formatter im zweiten Tab benötigt keine Engine.
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"Reines PHP
Kein exec(), kein FFI, kein Node. Erfordert PHP 8.2+ mit ext-curl, ext-bcmath und ext-mbstring; CI läuft mit PHP 8.4 und 8.5.
100% test262
Besteht alle 50.506 Tests der ECMAScript-Konformitätssuite im Snapshot vom 2025-05-05, in jeder Kategorie und ohne ausgelassene Tests.
Modernes ECMAScript
Klassen, async/await, Generatoren, Symbol, BigInt, Proxy, Temporal, TypedArray und Intl mit ext-intl. Vollständiges ES2024+.
Webplattform-APIs
URL, TextEncoder und TextDecoder, atob und btoa, structuredClone, performance und DOMException sind integriert.
fetch + Streams
Echtes HTTP aus JS über ext-curl. Vollständige Fetch-API mit AbortSignal, Blob, FormData und WHATWG Streams.
Web Platform Tests
Importierte WPT-Untertests für fetch, headers, blob, abort, streams und encoding bestehen – abgesehen von einer Allowlist mit brotli- und SharedArrayBuffer-Fällen.
PHP ↔ JS-Interop
setGlobal() stellt PHP-Closures als JS-Funktionen bereit, und call() führt JS-Funktionen aus PHP aus. Werte werden an der Grenze konvertiert; Arrays werden kopiert.
Einbettbar
Eine Engine-Klasse: eval, execFile, setGlobal, call. Integriere sie in jede PHP-App oder jedes CLI.
Begrenzter Call-Stack
Ein fester Call-Stack mit 4.096 Frames stoppt endlose Rekursion mit einem abfangbaren InternalError.
CLI
bin/phasis führt eine Datei oder ein -e-Snippet aus und gibt den AST mit --ast aus. In einem Repository-Checkout führt bin/test262 die Konformitätssuite aus.
Node.js-Referenz
Szenarien werden mit Node.js (V8) abgeglichen. WPT und die Bibliothekssuite laufen in CI bei Pushes, die die Engine betreffen; der vollständige test262-Durchlauf erfolgt bei Bedarf, zuletzt am 2026-06-09.
Echte Bibliotheken
204 npm-Pakete, darunter acorn, mustache.js, lodash und marked, werden getestet; die Ausgabe ist bytegenau wie bei Node.
Warum Phasis
Kein Node.js-Prozess
Phasis lexikalisiert, parst und führt JavaScript direkt in PHP aus – ohne exec()-Aufruf von Node und ohne FFI. Es wird als Composer-Paket installiert.
fetch unter Kontrolle des Hosts
fetch() verwendet standardmäßig ext-curl. setFetchTransport() tauscht den Transport aus, zum Beispiel gegen einen Mock für Tests, und setFetchPolicy() kann jede Anfrage vor dem Senden ablehnen oder umschreiben.
Timer mit virtueller Zeit
eval() leert die Event-Loop, bevor es zurückkehrt; die Uhr springt zum nächsten ausstehenden Timer, statt zu warten. Hosts mit langer Laufzeit können die Loop mit tickEventLoop() in Echtzeit steuern.
Parser und Formatter enthalten
Der Parser gibt einen typisierten PHP-AST zurück, der sich nach ESTree exportieren lässt. Der Formatter gibt JavaScript, JSON und CSS mit einem zu Prettier kompatiblen Layout neu aus.
Im Vergleich zu
Weitere Möglichkeiten, JavaScript aus einer PHP-Anwendung auszuführen oder zu verarbeiten.
| Werkzeug | Gut geeignet für | Unterschiede zu Phasis |
|---|---|---|
| Node.js | Gut geeignet fürNode.js führt JavaScript auf der V8-Engine aus und löst Pakete in node_modules-Ordnern auf, sodass sich npm-Abhängigkeiten über ihren Namen laden lassen. | Unterschiede zu PhasisNode aus PHP aufzurufen, fügt eine zweite Laufzeitumgebung und eine Subprozessgrenze hinzu. Phasis läuft innerhalb von PHP, aber sein Modul-Loader kennt weder node_modules noch die Auflösung von package.json-Exports. Daher müssen npm-Pakete zuerst gebündelt werden, zum Beispiel mit esbuild. |
| V8Js | Gut geeignet fürV8Js bindet Googles V8-Engine in PHP ein und führt JavaScript in einer Sandbox aus, die sich durch ein Zeit- und ein Speicherlimit beschränken lässt. | Unterschiede zu PhasisV8Js ist eine kompilierte Erweiterung, die die libv8-Bibliothek benötigt. Phasis ist reines PHP aus Composer; laut Dokumentation erreicht es bei Code, der durch Funktionsaufrufe begrenzt ist, ungefähr die 100-fache Laufzeit von V8. |
| Peast | Gut geeignet fürPeast parst JavaScript unter PHP 5.4 und höher in einen ESTree-AST und bietet außerdem Baumdurchläufe, Selektorabfragen und Rendering. | Unterschiede zu PhasisPhasis benötigt PHP 8.2. Sein Parser gibt typisierte, schreibgeschützte Knotenobjekte mit einem ESTree-Export zurück; dasselbe Paket kann den Code auch ausführen. |
| Prettier | Gut geeignet fürPrettier formatiert JavaScript, TypeScript, JSX, CSS, HTML, Markdown, YAML und mehr und lässt sich über npm installieren. | Unterschiede zu PhasisDer Phasis-Formatter unterstützt nur JavaScript, JSON und CSS. Seine Optionen entsprechen Prettier 3.x; er läuft in PHP ohne Node.js. |
Fragen und Antworten
Kurze Antworten aus der Phasis-Dokumentation.
Composer benötigt PHP 8.2 oder höher mit bcmath, curl und mbstring. intl wird für die Intl-APIs und Temporal-Kalender außerhalb von ISO empfohlen, openssl für AES-Verschlüsselung in crypto.subtle.
Phasis ist ein Interpreter, der Syntaxbäume durchläuft, mit einer Bytecode-VM, kein JIT. Laut Dokumentation erreicht es bei Code, der durch Funktionsaufrufe begrenzt ist, ungefähr die 100-fache Laufzeit von V8. Diese Obergrenze spielt demnach bei Einbettungsaufgaben wie Validierungsregeln selten eine Rolle.
Nicht über den Namen. Der Modul-Loader löst Imports anhand des Dateisystems auf, benötigt explizite Dateiendungen und unterstützt weder node_modules noch package.json-Exports. Bündle das Paket daher mit einem Tool wie esbuild und übergib Phasis das Bundle.
Nein. window, document und der DOM-Baum sind nicht enthalten, ebenso wenig Node-Globals wie process, Buffer und require().
Sie wird in der Engine gepuffert, statt ausgegeben zu werden. Lies sie mit getConsoleOutput() oder getConsoleLines() aus und leere den Puffer mit clearConsole().
Ein nicht abgefangener Throw wird als Phasis\Exceptions\JsThrowable sichtbar. Fehler, die die Engine selbst auslöst, etwa beim Lesen einer Eigenschaft von null, erscheinen als Phasis\Exceptions\TypeError, ReferenceError oder RangeError und erweitern alle Phasis\Exceptions\RuntimeError. Ungültiger Quelltext löst Phasis\Parser\ParseError aus, eine Unterklasse von Phasis\Exceptions\SyntaxError, mit Zeile, Spalte und Offset.
Beginne mit der Anleitung für die ersten Schritte und lies anschließend die API-Referenz zu den Fetch-Hooks und der Event-Loop.