Phasis
Движок JavaScript, написанный на PHP: ES2024+; проходит все 50 506 тестов test262 (снимок от 2025-05-05), а также поддерживает URL, кодирование, structuredClone, fetch и Streams.
composer require phasis/phasisПоследний выпуск 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.jsНа практике
Один объект движка запускает JavaScript в процессе PHP и передаёт значения в обе стороны.
Запускайте JavaScript из PHP
Phasis\Engine вычисляет строки исходного кода и файлы в процессе PHP, который его вызывает. setGlobal() преобразует массивы PHP в массивы и объекты JavaScript, а замыкания PHP оборачивает в функции.
Вывод console.log буферизуется в движке и считывается с помощью getConsoleOutput(). Для форматирования во второй вкладке движок не нужен.
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
Без exec(), FFI и Node. Требуется PHP 8.2+ с ext-curl, ext-bcmath и ext-mbstring; CI работает на PHP 8.4 и 8.5.
100% test262
Проходит все 50 506 тестов набора на соответствие ECMAScript по снимку от 2025-05-05: все категории, без пропусков.
Современный ECMAScript
Классы, async/await, генераторы, Symbol, BigInt, Proxy, Temporal, TypedArray и Intl с ext-intl. Полная поддержка ES2024+.
API веб-платформы
Встроены URL, TextEncoder и TextDecoder, atob и btoa, structuredClone, performance и DOMException.
fetch + потоки
Настоящие HTTP через JS с помощью ext-curl. Полная реализация Fetch API с AbortSignal, Blob, FormData и WHATWG Streams.
Тесты веб-платформы
Импортированные подтесты WPT для fetch, headers, blob, abort, streams и encoding проходят, кроме разрешённого списка случаев с brotli и SharedArrayBuffer.
Совместимость PHP ↔ JS
setGlobal() предоставляет замыкания PHP как функции JS, а call() запускает функции JS из PHP. Значения преобразуются на границе; массивы копируются.
Встраиваемый
Один класс Engine: eval, execFile, setGlobal, call. Подходит для любого приложения PHP или CLI.
Ограничение стека вызовов
Фиксированный стек вызовов на 4 096 кадров останавливает бесконечную рекурсию и выдаёт перехватываемую InternalError.
CLI
bin/phasis запускает файл или фрагмент кода с флагом -e и выводит AST с флагом --ast. В клоне репозитория команда bin/test262 запускает набор тестов на соответствие.
Эталон Node.js
Сценарии проверяются по Node.js (V8). WPT и набор тестов библиотеки запускаются в CI при отправке изменений, затрагивающих движок; полный прогон test262 запускается по запросу, последний раз — 2026-06-09.
Настоящие библиотеки
204 пакета npm, включая acorn, mustache.js, lodash и marked, протестированы: вывод побайтно совпадает с Node.
Почему Phasis
Без процесса Node.js
Phasis разбирает и выполняет JavaScript непосредственно в PHP без вызова Node через exec() и без FFI. Устанавливается как пакет Composer.
fetch под контролем хоста
По умолчанию fetch() использует ext-curl. setFetchTransport() позволяет заменить транспорт, например, на имитацию в тестах, а setFetchPolicy() может отклонить или изменить каждый запрос перед отправкой.
Таймеры по виртуальному времени
eval() обрабатывает очередь событий до возврата, а часы перескакивают к следующему ожидающему таймеру вместо ожидания. Хосты с длительным временем работы могут запускать цикл в реальном времени с помощью tickEventLoop().
Парсер и форматтер в комплекте
Парсер возвращает типизированное AST PHP, которое экспортируется в ESTree. Форматтер повторно выводит JavaScript, JSON и CSS с форматированием, совместимым с prettier.
Сравнение с
Другие способы запускать или обрабатывать JavaScript из приложения PHP.
| Инструмент | Подходит для | Отличия от Phasis |
|---|---|---|
| Node.js | Подходит дляNode.js запускает JavaScript на движке V8 и разрешает пакеты из каталогов node_modules, поэтому зависимости npm загружаются по имени. | Отличия от PhasisВызов Node из PHP добавляет вторую среду выполнения и границу подпроцесса. Phasis работает внутри PHP, но его загрузчик модулей не поддерживает node_modules и разрешение экспортов package.json, поэтому пакеты npm сначала нужно собрать, например, с помощью esbuild. |
| V8Js | Подходит дляV8Js встраивает движок V8 из Google в PHP и запускает JavaScript в песочнице, для которой можно задать ограничения по времени и памяти. | Отличия от PhasisV8Js — скомпилированное расширение, которому нужна библиотека libv8. Phasis — это обычный PHP из Composer; в документации указано, что на коде с ограничением по диспетчеризации он работает примерно в 100 раз медленнее V8. |
| Peast | Подходит дляPeast разбирает JavaScript в AST формата ESTree на PHP 5.4 и новее, а также поддерживает обход дерева, запросы с селекторами и рендеринг. | Отличия от PhasisPhasis требуется PHP 8.2. Его парсер возвращает типизированные объекты узлов только для чтения с экспортом в ESTree; этот же пакет также может выполнять код. |
| Prettier | Подходит дляPrettier форматирует JavaScript, TypeScript, JSX, CSS, HTML, Markdown, YAML и многое другое; устанавливается из npm. | Отличия от PhasisФорматтер Phasis поддерживает только JavaScript, JSON и CSS. Его параметры соответствуют 3.x prettier; он работает в PHP без Node.js. |
Вопросы и ответы
Краткие ответы из документации Phasis.
Для Composer требуется PHP 8.2 или новее с bcmath, curl и mbstring. Для API Intl и календарей, отличных от ISO Temporal, рекомендуется intl; для шифрования AES в crypto.subtle — openssl.
Phasis — интерпретатор с обходом дерева и виртуальной машиной байт-кода, а не JIT. В документации указано, что на коде с ограничением по диспетчеризации он работает примерно в 100 раз медленнее V8; по их словам, для встраивания, например проверки правил, этот предел редко имеет значение.
По имени — нет. Загрузчик модулей ищет импорты в файловой системе, требует явных расширений файлов и не поддерживает node_modules или экспорты package.json, поэтому соберите пакет с помощью такого инструмента, как esbuild, и передайте Phasis собранный файл.
Нет. window, document и дерево DOM не входят в комплект; глобальные объекты Node, такие как process, Buffer и require(), тоже.
Вывод буферизуется в движке, а не печатается. Прочитайте его с помощью getConsoleOutput() или getConsoleLines(), а буфер очистите с помощью clearConsole().
Неперехваченный выброс проявляется как Phasis\Exceptions\JsThrowable. Ошибки, возникающие непосредственно в движке, например при чтении свойства null, проявляются как Phasis\Exceptions\TypeError, ReferenceError или RangeError; все они наследуются от Phasis\Exceptions\RuntimeError. Некорректный исходный код вызывает Phasis\Parser\ParseError — объект Phasis\Exceptions\SyntaxError с указанием строки, столбца и смещения.
Начните с руководства для начала работы, затем изучите справочник API, посвящённый перехватчикам fetch и циклу событий.