Phasis
Silnik JavaScript napisany w PHP: ES2024+ ze wszystkimi 50 506 zaliczonymi testami test262 (migawka z 2025-05-05), a także URL, kodowanie, structuredClone, fetch i Streams.
composer require phasis/phasisNajnowsze wydanie 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.jsW praktyce
Jeden obiekt silnika uruchamia JavaScript w procesie PHP i przekazuje wartości w obu kierunkach.
Uruchamiaj JavaScript z PHP
Phasis\Engine oblicza ciągi źródłowe i pliki w procesie PHP, który go wywołuje. setGlobal() konwertuje tablice PHP na tablice i obiekty JavaScript oraz opakowuje funkcje domknięcia PHP jako funkcje.
Dane wyjściowe console.log są buforowane w silniku i odczytywane za pomocą getConsoleOutput(). Program formatujący w drugiej karcie nie wymaga silnika.
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"Czysty PHP
Bez exec(), FFI i Node. Wymaga PHP 8.2+ z ext-curl, ext-bcmath i ext-mbstring; CI działa na PHP 8.4 i 8.5.
100% test262
Zalicza wszystkie 50 506 testów zestawu zgodności ECMAScript z migawki 2025-05-05, w każdej kategorii, bez pominięć.
Nowoczesny ECMAScript
Klasy, async/await, generatory, Symbol, BigInt, Proxy, Temporal, TypedArray i Intl z ext-intl. Pełne ES2024+.
Interfejsy API platformy internetowej
URL, TextEncoder i TextDecoder, atob i btoa, structuredClone, performance oraz DOMException są wbudowane.
fetch + strumienie
Prawdziwe HTTP z JS przez ext-curl. Pełne Fetch API z AbortSignal, Blob, FormData i strumieniami WHATWG.
Testy platformy internetowej
Zaimportowane podtesty WPT dla fetch, headers, blob, abort, streams i encoding przechodzą, z wyjątkiem dozwolonej listy przypadków brotli i SharedArrayBuffer.
Interop PHP ↔ JS
setGlobal() udostępnia funkcje domknięcia PHP jako funkcje JS, a call() uruchamia funkcje JS z PHP. Wartości są konwertowane na granicy; tablice są kopiowane.
Możliwość osadzania
Jedna klasa Engine: eval, execFile, setGlobal, call. Można ją dodać do dowolnej aplikacji PHP lub CLI.
Limit stosu wywołań
Stały stos wywołań o głębokości 4 096 ramek zatrzymuje niekontrolowaną rekurencję, zgłaszając przechwytywalny InternalError.
CLI
bin/phasis uruchamia plik lub fragment -e i wyświetla AST za pomocą --ast. W kopii repozytorium bin/test262 uruchamia zestaw testów zgodności.
Oracle Node.js
Scenariusze są sprawdzane względem Node.js (V8). WPT i zestaw testów biblioteki działają w CI przy zmianach obejmujących silnik; pełny przebieg test262 jest uruchamiany na żądanie, ostatnio 2026-06-09.
Rzeczywiste biblioteki
Testowane są pakiety 204 npm, w tym acorn, mustache.js, lodash i marked; ich wynik jest identyczny bajt po bajcie z Node.
Dlaczego Phasis
Proces bez Node.js
Phasis analizuje leksykalnie, parsuje i wykonuje JavaScript w samym PHP, bez wywołań exec() do Node i bez FFI. Instaluje się jako pakiet Composer.
fetch pod kontrolą hosta
fetch() domyślnie używa ext-curl. setFetchTransport() zmienia transport, na przykład na atrapę w testach, a setFetchPolicy() może odrzucić lub zmienić każde żądanie przed jego wysłaniem.
Liczniki czasu wirtualnego
eval() opróżnia pętlę zdarzeń przed powrotem, a zegar przeskakuje do następnego oczekującego licznika zamiast czekać. Hosty działające długotrwale mogą sterować pętlą w czasie rzeczywistym za pomocą tickEventLoop().
Parser i formatter w zestawie
Parser zwraca typowane AST PHP, które można eksportować do ESTree. Formatter ponownie zapisuje JavaScript, JSON i CSS w układzie zgodnym z prettier.
W porównaniu z
Inne sposoby uruchamiania lub przetwarzania JavaScript z aplikacji PHP.
| Narzędzie | Dobre w | Różnica w porównaniu z Phasis |
|---|---|---|
| Node.js | Dobre wNode.js uruchamia JavaScript na silniku V8 i rozwiązuje pakiety z folderów node_modules, dzięki czemu zależności npm można ładować po nazwie. | Różnica w porównaniu z PhasisWywołanie Node z PHP dodaje drugi środowisko uruchomieniowe i granicę podprocesu. Phasis działa wewnątrz PHP, ale jego moduł ładujący nie obsługuje node_modules ani rozwiązywania eksportów package.json, więc pakiety npm trzeba najpierw połączyć, na przykład za pomocą esbuild. |
| V8Js | Dobre wV8Js osadza silnik V8 z Google w PHP i uruchamia JavaScript w piaskownicy, którą można ograniczyć limitem czasu i pamięci. | Różnica w porównaniu z PhasisV8Js to skompilowane rozszerzenie wymagające biblioteki libv8. Phasis to czyste PHP z Composer, a dokumentacja podaje, że jego czas działania na kodzie ograniczonym przez dispatch wynosi około 100 razy więcej niż V8. |
| Peast | Dobre wPeast parsuje JavaScript do AST ESTree w wersji PHP 5.4 i nowszych oraz dodaje przechodzenie po drzewie, zapytania selektorów i renderowanie. | Różnica w porównaniu z PhasisPhasis wymaga PHP 8.2. Jego parser zwraca typowane, tylko do odczytu obiekty węzłów z eksportem ESTree, a ten sam pakiet może również wykonywać kod. |
| Prettier | Dobre wPrettier formatuje JavaScript, TypeScript, JSX, CSS, HTML, Markdown, YAML i inne, a można go zainstalować z npm. | Różnica w porównaniu z PhasisFormatter Phasis obsługuje tylko JavaScript, JSON i CSS. Jego opcje odzwierciedlają 3.x prettier, działa w PHP i nie wymaga Node.js. |
Pytania i odpowiedzi
Krótkie odpowiedzi z dokumentacji Phasis.
Composer wymaga PHP 8.2 lub nowszego z bcmath, curl i mbstring. intl jest zalecane dla interfejsów API Intl i kalendarzy Temporal innych niż ISO, a openssl do szyfrowania AES w crypto.subtle.
Phasis to interpreter przechodzący po drzewie z maszyną wirtualną kodu bajtowego, a nie JIT. Dokumentacja podaje, że jego czas działania na kodzie ograniczonym przez dispatch wynosi około 100 razy więcej niż V8; ten limit, jak twierdzą autorzy, rzadko ma znaczenie przy osadzaniu, na przykład podczas sprawdzania reguł walidacji.
Nie po nazwie. Moduł ładujący rozwiązuje importy względem systemu plików, wymaga jawnych rozszerzeń plików i nie obsługuje node_modules ani eksportów package.json, więc połącz pakiet za pomocą narzędzia takiego jak esbuild i przekaż Phasis gotowy pakiet.
Nie. Okna window, document i drzewo DOM nie są dostarczane, podobnie jak zmienne globalne Node, takie jak process, Buffer i require().
Są buforowane w silniku, a nie wyświetlane. Odczytaj je za pomocą getConsoleOutput() lub getConsoleLines(), a następnie opróżnij bufor przez clearConsole().
Nieprzechwycony rzut wyjątku jest zgłaszany jako Phasis\Exceptions\JsThrowable. Błędy zgłaszane przez sam silnik, takie jak odczyt właściwości wartości null, są zgłaszane jako Phasis\Exceptions\TypeError, ReferenceError lub RangeError; wszystkie dziedziczą po Phasis\Exceptions\RuntimeError. Nieprawidłowy kod źródłowy zgłasza Phasis\Parser\ParseError, czyli Phasis\Exceptions\SyntaxError zawierający wiersz, kolumnę i przesunięcie.
Zacznij od przewodnika dla początkujących, a następnie przeczytaj dokumentację API, aby poznać hooki fetch i pętlę zdarzeń.