Известни ограничения
Phasis цели да се държи идентично на V8 при всеки тест на спецификацията, който може да се наблюдава. Тази страница описва къде тази идентичност на практика приключва — обикновено заради фундаментални разлики между интерпретатор с обхождане на дървото PHP и машина на C++, компилирана с JIT.
Ограничение в производителността
Машината обхожда синтактичното дърво и включва опортюнистична виртуална машина с байткод, както и транспилатор JsToPhp, който преобразува тела с много числови операции или локални променливи (включително форми obj.method(), new Ctor() и this.member) в нативни затваряния PHP, които проследяващият JIT компилатор на PHP 8.5 може да превърне в машинен код. Повечето натоварени операции по изпълнението се случват в цикъла за превключване на виртуалната машина или директно в JIT-компилиран PHP, а не чрез обхождане на AST — но всяка инструкция от виртуалната машина все пак плаща цената на PHP превключване, а всяка форма, която JsToPhp още не обработва, преминава към по-бавната виртуална машина. При натоварвания, ограничени от скоростта на изпълнение, таванът е около ~100× по-бавно от V8.
При натоварвания с вграждане — изпълнение на предоставена от потребителя логика върху данни PHP, конвейери за преобразуване на съдържание и правила за валидация — това ограничение рядко има значение. V8 е 100 ms; Phasis е 10 s; и двете стойности са много под времевия лимит на заявката. За да се намали разликата, JsToPhp трябва да поддържа още форми (извеждане на типовете на параметрите, преобразуване на await / yield*) или да генерира директно инструкции за opcache, вместо да го прави чрез eval(). Вижте Бенчмаркове § Пътна карта.
Едновременна работа на множество агенти
Моделът SharedArrayBuffer + Atomics.wait + postMessage на JavaScript предполага, че хостът може да стартира реални нишки на операционната система. PHP не може (без pcntl_fork, която не е налична във всички хостове).
Phasis симулира един агент — Atomics.wait незабавно връща "not-equal" или "timed-out", а notify винаги връща 0. Така преминават всички тестове на test262 за един агент (проверка на входни данни, пътища за грешки и празни извиквания на notify), които обхващат 376 от 376 теста за Atomics.
Тестовете, за които са нужни два едновременни агента, наблюдаващи се един друг, не се поддържат. За истинска паралелна работа използвайте опашка от работници на ниво PHP (Redis, Beanstalkd и др.).
Семантика на областите
ShadowRealm е реализиран, но всяка дъщерна среда работи в същия процес PHP като хоста. Функциите на хоста могат да пренасят препратки между средите, ако не се погрижите кодът на PHP да е изолиран. Спецификацията го позволява; V8 налага по-строга изолация чрез отделни екземпляри на v8::Isolate.
createRealm() (host hook за test262) създава вътрешно нов Phasis\Engine и предоставя стандартната библиотека чрез мост.
Избор на regex енджин
Phasis използва PCRE2 на PHP като основен regex бекенд и собствен съпоставител за случаите, с които PCRE2 не може да се справи точно (lookbehind с captures, квантифицирани групи с reset, определени семантики на флага /v). Собственият съпоставител е по-бавен от PCRE2 и се използва само когато диспечерът засече несъвместим шаблон.
Забележима последица: таблицата за преобразуване на регистъра в PCRE2 може да се различава от тази във версията на ICU на V8, особено в по-старите дистрибуции на Linux (Ubuntu 22.04 включва ICU 70; Phasis включва таблица за преобразуване на регистъра от ICU 76+, за да компенсира разминаванията в хоста при шаблони /iu).
Модел на паметта
GC с броене на препратките в PHP се различава от проследяващия събирач на отпадъци на V8. Обектите на PHP, уловени от затварянията на хоста, остават живи, докато енджинът пази тези затваряния, а циклите между тях не се събират, докато Engine не бъде унищожен. Използвайте WeakMap / WeakRef за кешове, които не искате да остават в паметта.
process.memoryUsage() и подобни API за интроспекция, специфични за V8, не са реализирани.
Зареждане на модули
Зареждащият модулите ES модул разрешава относителните импорти спрямо файловата система, но не разбира конвенциите на пакетиращите инструменти:
- Няма резолюция на
node_modules. - Няма карта на
package.jsonexports. - Няма извеждане на файловото разширение (трябва да напишете
import './foo.mjs', а неimport './foo'). - Няма импорти на HTTP.
Това съответства на чистата спецификация на ECMAScript, но не и на поведението на Node. Няма hook за персонализиран резолвер на модули. За да използвате пакетите на npm, пакетирайте ги с инструмент като esbuild в един скрипт или модул, преди да го подадете на Phasis.
Wasm
WebAssembly не е реализиран. Няма wasm среда за изпълнение на PHP, с която да се свърже.
Пропускани API на уеб платформата, достъпни само в браузъра
Phasis включва пълния пакет за уеб платформата (URL, TextEncoder/TextDecoder, atob/btoa, structuredClone, performance, DOMException), пълния пакет за Fetch (fetch, Request, Response, Headers, Body, AbortController/AbortSignal, Blob/File, FormData, EventTarget/Event, пълния WHATWG Streams, navigator), плюс crypto (getRandomValues, randomUUID, пълния SubtleCrypto: digest, HMAC, AES-GCM/CBC/CTR, RSA-OAEP/PSS/PKCS1, ECDSA, ECDH, HKDF, PBKDF2), WebSocket (конструктор + кодек за кадри по RFC 6455, сменяем транспорт), XMLHttpRequest (надграждащ fetch транспорта) и реален цикъл на събитията (setTimeout, setInterval, clearTimeout, clearInterval, queueMicrotask, AsyncContext). Вижте Web APIs.
Какво не е включено:
window,document, DOM дървото — Phasis не е браузър.localStorage,sessionStorage,IndexedDB— отговорности на хоста; вграждащият компонент управлява постоянството на данните.Worker,SharedWorker,MessageChannel— изискват реални нишки на ОС, каквито PHP не предоставя.- Service Workers, Push API, Notifications, BroadcastChannel — нямат приложение извън браузър.
Глобални променливи на Node
process, Buffer, __dirname, __filename, require() — нито една от тях не съществува. Това са конвенции на хост средата на Node, а не езикови функции.
Ако пренасяте Node код, заменете require('foo') с import 'foo', сменете process.env.X с env.X, обвързан с хоста, и подайте пътища, еквивалентни на __dirname, от PHP.
Десетична точност
Числата в ECMAScript са двойни числа с плаваща запетая по IEEE 754. Десетичната аритметика с повече от 15 значещи цифри губи точност точно както във всеки друг енджин на JS. Използвайте BigInt за точна аритметика с цели числа или библиотека за десетични числа на JS, ако ви е нужна точност при дробните числа.
PHP използва вътрешно bcmath за BigInt; единствената реална разлика спрямо V8 е, че много големите операции с bigint са по-бавни в PHP, отколкото в реализацията на C++ на V8. Числената точност е еднаква.