Limitações conhecidas
Phasis busca se comportar de forma idêntica a V8 em todos os testes observáveis da especificação. Esta página lista os casos em que, na prática, essa identidade termina — geralmente por diferenças fundamentais entre um interpretador PHP que percorre a árvore e um mecanismo C++ compilado por JIT.
Limite de desempenho
O mecanismo percorre a árvore, conta com uma VM de bytecode oportunista e usa um transpilador JsToPhp que converte corpos com muitos valores numéricos ou variáveis locais (incluindo formatos obj.method(), new Ctor() e this.member) em closures nativas PHP que o JIT de rastreamento do PHP 8.5 pode compilar em código de máquina. A maior parte do processamento intensivo ocorre dentro do loop switch da VM ou diretamente em PHP compilado por JIT — não pela travessia da AST —, mas cada opcode da VM ainda paga um custo de dispatch PHP, e qualquer formato que o JsToPhp ainda não trata recorre à VM mais lenta. O limite para cargas de trabalho restritas pelo dispatch é cerca de 100× mais lento que V8.
Em cargas de trabalho incorporadas — execução de lógica fornecida pelo usuário em dados PHP, pipelines de transformação de conteúdo e regras de validação — esse limite raramente importa. V8 é 100 ms; Phasis é 10 s; ambos ficam bem abaixo do tempo limite da solicitação. Para reduzir ainda mais a diferença, seria preciso ensinar mais formatos ao JsToPhp (inferência do tipo de parâmetro, conversão de await / yield*) ou emitir opcodes do opcache diretamente, em vez de fazê-lo por meio de eval(). Consulte Benchmarks § Roteiro.
Concorrência multiagente
O modelo de JavaScript com SharedArrayBuffer + Atomics.wait + postMessage pressupõe que o host pode executar threads reais do sistema operacional. PHP não pode (sem pcntl_fork, que não está disponível em todos os hosts).
Phasis simula um único agente — Atomics.wait retorna "not-equal" ou "timed-out" imediatamente, notify sempre retorna 0. Isso faz com que todos os testes test262 executados com um único agente sejam aprovados (validação de entrada, caminhos de erro, notificação sem operação), o que abrange 376 dos testes Atomics 376.
Testes que realmente exigem dois agentes concorrentes observando um ao outro não são compatíveis. Use uma fila de workers no nível PHP (Redis, Beanstalkd, …) para obter paralelismo real.
Semântica de realm
ShadowRealm está implementado, mas cada realm filho é executado no mesmo processo PHP que o host. Funções host podem vazar referências entre realms se você não tiver cuidado para manter o código PHP protegido. A especificação permite isso; V8 aplica um isolamento mais rigoroso com instâncias separadas de v8::Isolate.
createRealm() (hook de host test262) cria internamente um novo Phasis\Engine e faz a ponte para a biblioteca padrão.
Escolha do mecanismo de expressões regulares
Phasis usa o PCRE2 de PHP como mecanismo principal de expressões regulares, com um comparador personalizado para os casos que o PCRE2 não consegue tratar exatamente (lookbehind com capturas, grupos quantificados com reset e certas semânticas da flag /v). O comparador personalizado é mais lento que o PCRE2 e só é usado quando o dispatch detecta um padrão incompatível.
A consequência notável: o PCRE2 pode ter uma tabela de conversão de maiúsculas e minúsculas diferente da versão do ICU do V8, especialmente em distribuições Linux mais antigas (Ubuntu 22.04 vem com ICU 70; Phasis inclui uma tabela de conversão do ICU 76+ para substituir as diferenças do sistema hospedeiro nos padrões do /iu).
Modelo de memória
O GC de contagem de referências do PHP difere do coletor de rastreamento do V8. Os objetos PHP capturados por closures do hospedeiro permanecem ativos enquanto o mecanismo mantiver essas closures, e os ciclos entre eles não são coletados até que o Engine seja destruído. Use WeakMap / WeakRef para caches que você não quer manter ativos.
As APIs de introspecção específicas de V8, como process.memoryUsage(), não são implementadas.
Carregador de módulos
O carregador de módulos ES resolve importações relativas com base no sistema de arquivos, mas não entende as convenções de empacotadores:
- Sem resolução de
node_modules. - Sem mapa de
package.jsonexports. - Sem inferência de extensão de arquivo (você precisa escrever
import './foo.mjs', nãoimport './foo'). - Sem importações de HTTP.
Isso corresponde à especificação ECMAScript sem extensões, mas não ao comportamento do Node. Não há um gancho para um resolvedor de módulos personalizado. Para usar pacotes npm, empacote-os com uma ferramenta como esbuild em um único script ou módulo antes de passá-los ao Phasis.
Wasm
WebAssembly não está implementado. Não há um ambiente de execução wasm PHP ao qual se vincular.
APIs da Web exclusivas do navegador que não incluímos
O Phasis inclui o Web Platform Pack completo (URL, TextEncoder/TextDecoder, atob/btoa, structuredClone, performance, DOMException), o Fetch Pack completo (fetch, Request, Response, Headers, Body, AbortController/AbortSignal, Blob/File, FormData, EventTarget/Event, WHATWG Streams completo, navigator), além de crypto (getRandomValues, randomUUID, SubtleCrypto completo: digest, HMAC, AES-GCM/CBC/CTR, RSA-OAEP/PSS/PKCS1, ECDSA, ECDH, HKDF, PBKDF2), WebSocket (construtor + codec de quadros RFC 6455, transporte intercambiável), XMLHttpRequest (em camadas sobre o transporte fetch) e um loop de eventos real (setTimeout, setInterval, clearTimeout, clearInterval, queueMicrotask, AsyncContext). Consulte APIs da Web.
O que não está incluído:
window,document, a árvore DOM — Phasis não é um navegador.localStorage,sessionStorage,IndexedDB— questões do lado do hospedeiro; o incorporador é responsável pela persistência.Worker,SharedWorker,MessageChannel— exigem threads reais do sistema operacional, que PHP não oferece.- Service Workers, Push API, Notifications, BroadcastChannel — não são relevantes fora de um navegador.
Globais do Node
process, Buffer, __dirname, __filename, require() — nenhum deles existe. São convenções do ambiente do Node, não recursos da linguagem.
Se estiver portando código do Node, substitua require('foo') por import 'foo', troque process.env.X por um env.X vinculado ao hospedeiro e passe caminhos equivalentes a __dirname de PHP.
Precisão decimal
Os números do ECMAScript são doubles IEEE 754. A aritmética decimal com mais de 15 dígitos significativos perde precisão exatamente como em qualquer outro mecanismo JS. Use BigInt para cálculos inteiros exatos ou uma biblioteca decimal JS se precisar de cálculos fracionários exatos.
O PHP usa internamente o bcmath para BigInt; a única diferença real em relação ao V8 é que operações muito grandes com bigint são mais lentas no PHP do que na implementação em C++ do V8. A precisão numérica é idêntica.