Phasis
Een JavaScript-engine geschreven in PHP: ES2024+ slaagt voor alle 50 506 test262-tests (snapshot van 2025-05-05), plus URL, encoding, structuredClone, fetch en Streams.
composer require phasis/phasisNieuwste release 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 de praktijk
Eén engineobject voert JavaScript uit binnen het PHP-proces en wisselt waarden in beide richtingen uit.
Voer JavaScript uit vanuit PHP
Phasis\Engine evalueert broncodestrings en bestanden in het PHP-proces dat de aanroep doet. setGlobal() zet PHP-arrays om in JavaScript-arrays en objecten, en verpakt PHP-closures als functies.
console.log-uitvoer wordt gebufferd in de engine en met getConsoleOutput() teruggelezen. De formatter in het tweede tabblad heeft helemaal geen engine nodig.
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"Zuiver PHP
Geen exec(), geen FFI, geen Node. Vereist PHP 8.2+ met ext-curl, ext-bcmath en ext-mbstring; CI draait op PHP 8.4 en 8.5.
100% test262
Slaagt voor alle 50 506 tests van de ECMAScript-conformiteitssuite volgens de snapshot van 2025-05-05, in elke categorie en zonder overgeslagen tests.
Modern ECMAScript
Classes, async/await, generators, Symbol, BigInt, Proxy, Temporal, TypedArray en Intl met ext-intl. Volledig ES2024+.
Webplatform-API's
URL, TextEncoder en TextDecoder, atob en btoa, structuredClone, performance en DOMException zijn ingebouwd.
fetch + Streams
Echte HTTP via ext-curl van JS. Volledige Fetch API met AbortSignal, Blob, FormData en WHATWG Streams.
Webplatformtests
Geïmporteerde WPT-subtests voor fetch, headers, blob, abort, streams en encoding slagen, met uitzondering van een allowlist voor brotli- en SharedArrayBuffer-cases.
PHP ↔ JS-interoperabiliteit
setGlobal() stelt PHP-closures beschikbaar als JS-functies en call() voert JS-functies uit vanuit PHP. Waarden worden op de grens omgezet; arrays worden gekopieerd.
Inbedbaar
Eén Engine-class: eval, execFile, setGlobal, call. Integreer deze in elke PHP-app of CLI.
Limiet voor de aanroepstack
Een vaste aanroepstack van 4 096 frames stopt onbeperkte recursie met een opvangbare InternalError.
CLI
bin/phasis voert een bestand of een -e-fragment uit en drukt de AST af met --ast. In een repository-checkout voert bin/test262 de conformiteitssuite uit.
Node.js-oracle
Scenario's worden gecontroleerd met Node.js (V8). WPT en de librarysuite draaien in CI bij pushes die de engine wijzigen; de volledige test262-test wordt op aanvraag uitgevoerd, voor het laatst op 2026-06-09.
Echte libraries
204 npm-packages, waaronder acorn, mustache.js, lodash en marked, worden getest met uitvoer die byte voor byte gelijk is aan die van Node.
Waarom Phasis
Geen Node.js-proces
Phasis lexet, parseert en voert JavaScript uit in PHP zelf, zonder exec()-aanroep naar Node en zonder FFI. Het wordt geïnstalleerd als een Composer-package.
fetch onder controle van de host
fetch() gebruikt standaard ext-curl. setFetchTransport() vervangt het transport, bijvoorbeeld door een mock voor tests, en setFetchPolicy() kan elk verzoek weigeren of aanpassen voordat het wordt verzonden.
Timers op virtuele tijd
eval() verwerkt de eventloop voordat deze terugkeert, en de klok springt naar de eerstvolgende timer in plaats van te wachten. Hosts die lang actief blijven, kunnen de loop in realtime aansturen met tickEventLoop().
Parser en formatter inbegrepen
De parser retourneert een getypeerde PHP-AST die naar ESTree kan worden geëxporteerd. De formatter genereert JavaScript, JSON en CSS opnieuw met een met prettier compatibele indeling.
Vergeleken met
Andere manieren om JavaScript vanuit een PHP-applicatie uit te voeren of te verwerken.
| Tool | Goed in | Verschil met Phasis |
|---|---|---|
| Node.js | Goed inNode.js voert JavaScript uit op de V8-engine en haalt packages op uit node_modules-mappen, zodat npm-dependencies op naam worden geladen. | Verschil met PhasisNode aanroepen vanuit PHP voegt een tweede runtime en een subprocessgrens toe. Phasis draait binnen PHP, maar de moduleloader heeft geen node_modules en ondersteunt geen package.json-exportsresolutie, dus npm-packages moeten eerst worden gebundeld, bijvoorbeeld met esbuild. |
| V8Js | Goed inV8Js integreert de V8-engine van Google in PHP en voert JavaScript uit in een sandbox die met een tijdslimiet en geheugenlimiet kan worden beperkt. | Verschil met PhasisV8Js is een gecompileerde extensie waarvoor de libv8-library nodig is. Phasis is gewone PHP van Composer; de documentatie geeft aan dat de runtime op dispatch-gebonden code ongeveer 100 keer die van V8 bedraagt. |
| Peast | Goed inPeast parseert JavaScript naar een ESTree-AST met PHP 5.4 en hoger, en voegt boomtraversal, selectoraanvragen en rendering toe. | Verschil met PhasisPhasis vereist PHP 8.2. De parser retourneert getypeerde, alleen-lezen nodeobjecten met een ESTree-export, en hetzelfde package kan de code ook uitvoeren. |
| Prettier | Goed inPrettier formatteert JavaScript, TypeScript, JSX, CSS, HTML, Markdown, YAML en meer, en kan worden geïnstalleerd via npm. | Verschil met PhasisDe Phasis-formatter ondersteunt alleen JavaScript, JSON en CSS. De opties komen overeen met die van prettier 3.x en de formatter draait in PHP, zonder Node.js. |
Vragen en antwoorden
Korte antwoorden uit de documentatie van Phasis.
Composer vereist PHP 8.2 of hoger met bcmath, curl en mbstring. intl wordt aanbevolen voor de Intl-API's en niet-ISO-Temporal-kalenders, en openssl voor AES-versleuteling in crypto.subtle.
Phasis is een interpreter die de syntaxboom doorloopt met een bytecode-VM, geen JIT. Volgens de documentatie bedraagt de runtime voor dispatch-gebonden code ongeveer 100 keer die van V8; die bovengrens is volgens de documentatie zelden van belang voor inbedding, bijvoorbeeld bij validatieregels.
Niet op naam. De moduleloader zoekt imports op in het bestandssysteem, vereist expliciete bestandsextensies en ondersteunt geen node_modules of package.json-exports. Bundle een package daarom met een tool zoals esbuild en geef de bundel door aan Phasis.
Nee. window, document en de DOM-boom zijn niet inbegrepen, net zomin als Node-globalen zoals process, Buffer en require().
De uitvoer wordt gebufferd in de engine in plaats van afgedrukt. Lees deze met getConsoleOutput() of getConsoleLines() en maak de buffer leeg met clearConsole().
Een niet-opgevangen throw wordt weergegeven als Phasis\Exceptions\JsThrowable. Fouten die de engine zelf veroorzaakt, zoals het lezen van een eigenschap van null, worden weergegeven als Phasis\Exceptions\TypeError, ReferenceError of RangeError; ze erven allemaal van Phasis\Exceptions\RuntimeError. Ongeldige broncode veroorzaakt Phasis\Parser\ParseError, een Phasis\Exceptions\SyntaxError met de regel, kolom en offset.
Begin met de handleiding om aan de slag te gaan en lees daarna de API-referentie voor de fetch-hooks en de eventloop.