Phasis
En JavaScript-motor skriven i PHP: ES2024+ klarar alla 50 506 test262-tester (ögonblicksbild 2025-05-05), samt URL, kodning, structuredClone, fetch och Streams.
composer require phasis/phasisSenaste versionen 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.jsI praktiken
Ett motorobjekt kör JavaScript i PHP-processen och skickar värden åt båda hållen.
Kör JavaScript från PHP
Phasis\Engine utvärderar källsträngar och filer i PHP-processen som anropar den. setGlobal() konverterar arrayer i PHP till arrayer och objekt i JavaScript, och omsluter closures i PHP som funktioner.
Utdata från console.log buffras på motorn och läses tillbaka med getConsoleOutput(). Formatteraren på den andra fliken behöver ingen motor alls.
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"Ren PHP
Ingen exec(), ingen FFI, ingen Node. Kräver PHP 8.2+ med ext-curl, ext-bcmath och ext-mbstring; CI kör PHP 8.4 och 8.5.
100% test262
Klarar alla 50 506-tester i ECMAScript-kompatibilitetssviten enligt ögonblicksbilden från 2025-05-05, samtliga kategorier, utan överhoppade tester.
Modern ECMAScript
Klasser, async/await, generatorer, Symbol, BigInt, Proxy, Temporal, TypedArray och Intl med ext-intl. Fullständigt ES2024+.
Webbplattformens API:er
URL, TextEncoder och TextDecoder, atob och btoa, structuredClone, performance och DOMException ingår.
fetch + Streams
Äkta HTTP från JS via ext-curl. Fullständigt Fetch-API med AbortSignal, Blob, FormData och WHATWG Streams.
Tester för webbplattformen
Importerade WPT-deltester för fetch, headers, blob, abort, streams och kodning klarar sig, förutom en tillåtelselista med fall för brotli och SharedArrayBuffer.
PHP ↔ JS-kompatibilitet
setGlobal() exponerar PHP-stängningar som JS-funktioner och call() kör JS-funktioner från PHP. Värden konverteras vid gränsen; arrayer kopieras.
Inbäddningsbar
En enda klass, Engine: eval, execFile, setGlobal, call. Lägg in den i valfri PHP-app eller CLI.
Anropsstackstak
En fast anropsstack på 4 096 ramar stoppar skenande rekursion med ett fångningsbart InternalError.
CLI
bin/phasis kör en fil eller ett -e-utdrag och skriver ut AST:n med --ast. I en utcheckad kodbas kör bin/test262 kompatibilitetssviten.
Node.js-orakel
Scenarier kontrolleras mot Node.js (V8). WPT och bibliotekssviten körs i CI vid pushar som berör motorn; hela testkörningen test262 körs vid behov, senast 2026-06-09.
Verkliga bibliotek
204 npm-paket, däribland acorn, mustache.js, lodash och marked, testas med utdata som är byteidentisk med Node.
Varför Phasis
Ingen Node.js-process
Phasis lexar, parsar och kör JavaScript i PHP självt, utan något exec()-anrop till Node och utan FFI. Det installeras som ett Composer-paket.
fetch under värdkontroll
fetch() använder ext-curl som standard. setFetchTransport() byter transport, till exempel till en mock i tester, och setFetchPolicy() kan neka eller skriva om varje begäran innan den skickas.
Timers med virtuell tid
eval() tömmer händelseloopen innan den returnerar, och klockan hoppar till nästa väntande timer i stället för att vila. Värdar med lång livslängd kan driva loopen i realtid med tickEventLoop().
Parser och formatterare ingår
Parsern returnerar en typad PHP-AST som exporteras till ESTree. Formatteraren skriver om JavaScript, JSON och CSS med en layout som är kompatibel med prettier.
Jämfört med
Andra sätt att köra eller bearbeta JavaScript från en applikation i PHP.
| Verktyg | Bra på | Skillnaden jämfört med Phasis |
|---|---|---|
| Node.js | Bra påNode.js kör JavaScript på motorn V8 och löser paket från mapparna node_modules, så npm-beroenden laddas med namn. | Skillnaden jämfört med PhasisAtt anropa Node från PHP lägger till en andra körtidsmiljö och en subprocessgräns. Phasis körs inuti PHP, men dess modulladdare saknar node_modules och upplösning av package.json-exporter, så paket i npm måste paketeras först, till exempel med esbuild. |
| V8Js | Bra påV8Js bäddar in V8-motorn från Google i PHP och kör JavaScript i en sandlåda som kan begränsas med en tidsgräns och en minnesgräns. | Skillnaden jämfört med PhasisV8Js är ett kompilerat tillägg som behöver libv8-biblioteket. Phasis är ren PHP från Composer, och dokumentationen anger att körningen tar ungefär 100 gånger så lång tid som V8 för kod som är bunden av dispatch. |
| Peast | Bra påPeast parsar JavaScript till en ESTree-AST med PHP 5.4 och senare, och lägger till trädtraversering, väljarsökningar och rendering. | Skillnaden jämfört med PhasisPhasis kräver PHP 8.2. Parsern returnerar typade, skrivskyddade nodobjekt med en ESTree-export, och samma paket kan även köra koden. |
| Prettier | Bra påPrettier formaterar JavaScript, TypeScript, JSX, CSS, HTML, Markdown, YAML med mera och installeras från npm. | Skillnaden jämfört med PhasisFormatteraren i Phasis hanterar endast JavaScript, JSON och CSS. Alternativen speglar prettier 3.x, och den körs i PHP utan Node.js. |
Frågor och svar
Korta svar från dokumentationen för Phasis.
Composer kräver PHP 8.2 eller senare med bcmath, curl och mbstring. intl rekommenderas för Intl-API:erna och kalendrar som inte är ISO Temporal, och openssl för AES-kryptering i crypto.subtle.
Phasis är en trädvandrande tolk med en bytekod-VM, inte en JIT. Dokumentationen anger att körningen tar ungefär 100 gånger så lång tid som V8 för kod som är bunden av dispatch; enligt dokumentationen är det ett tak som sällan spelar roll för inbäddningsarbete som valideringsregler.
Inte med namn. Modulladdaren löser importer mot filsystemet, kräver explicita filtillägg och saknar stöd för node_modules och package.json-exporter. Paketera därför ett paket med ett verktyg som esbuild och skicka paketet till Phasis.
Nej. window, document och DOM-trädet ingår inte, och inte heller globala Node-variabler som process, Buffer och require().
Den buffras på motorn i stället för att skrivas ut. Läs den med getConsoleOutput() eller getConsoleLines() och töm bufferten med clearConsole().
Ett ohanterat kast visas som Phasis\Exceptions\JsThrowable. Fel som motorn själv utlöser, till exempel när en egenskap läses från null, visas som Phasis\Exceptions\TypeError, ReferenceError eller RangeError, och alla ärver från Phasis\Exceptions\RuntimeError. Ogiltig källkod utlöser Phasis\Parser\ParseError, en Phasis\Exceptions\SyntaxError, med rad, kolumn och offset.
Börja med kom igång-guiden och läs sedan referensen för API för hämtningskrokar och händelseslingan.