Compilar PHP em Binário Nativo Sem Interpretador
Há cerca de quinze anos, o Facebook tentou resolver os problemas de performance do seu monólito com o compilador HPHPc, que convertia PHP para C++. Mais tarde, abandonaram essa abordagem em favor da máquina virtual HHVM, e o PHP 8 introduziu um JIT integrado. Parecia que a ideia de um compilador puramente Ahead-Of-Time para PHP havia ficado na história para sempre. A equipe do Swoole decidiu revisitar essa ideia e lançou o projeto TypePHP.
Este é um compilador AOT completo escrito em PHP. Ele traduz código fonte PHP 8.4+ em C++17, depois compila via GCC ou Clang em código de máquina nativo. Sem opcodes em tempo de execução, sem warmup de JIT, e sem interpretador no runtime.
O compilador é totalmente self-hosting. O utilitário tpc constrói a si mesmo a partir de fontes PHP, sem qualquer código C glue dentro do próprio compilador.
Como o TypePHP Funciona
A compilação acontece em dois estágios. Primeiro, o analisador parseia o código do projeto, coleta metadados sobre classes e funções, e constrói uma tabela de símbolos completa. No segundo estágio, os corpos das funções são traduzidos para C++17. Em hot paths, o compilador gera C++ estático rápido, enquanto para construções dinâmicas como reflection ou chamadas de funções do sistema, ele se conecta ao runtime Zend através da camada PHPX.
A saída pode ser um de três formatos:
- Binário executável (
bin). Um arquivo independente para utilitários de console e daemons. Requer um ponto de entradamain(). - Extensão PHP (
ext). Um módulo regular.soou.dllque pode ser carregado nophp.inipadrão. - Biblioteca dinâmica (
lib). Um arquivo binário com arquivos.stub.phpgerados para chamada de outros projetos. - Componente WASI para execução em ambiente WebAssembly.
O Que Muda na Sintaxe
O TypePHP não tenta suportar todos os truques dinâmicos do PHP padrão. Os criadores apostam em tipagem estrita onde máxima velocidade é necessária.
Tipos Escalares Nativos
A diretiva use native_types instrui o compilador a mapear int, float e bool diretamente para tipos C++ (int64_t, double, bool). Uma variável com tal tipo não pode mais mudar de tipo subitamente no meio da execução de uma função. Mas o processador executa instruções de máquina puras sem desempacotar zval.
<?php
use native_types;
function fib(int $n): int
{
if ($n == 1 || $n == 2) {
return 1;
}
return fib($n - 1) + fib($n - 2);
}
function main(int $argc, array $argv): void
{
$n = (int)$argv[1];
$begin = microtime(true);
echo fib($n) . "\n";
echo "Time: " . (microtime(true) - $begin) . "\n";
}
É construído com um único comando:
bin/tpc.php fib.php -O3 -o fib
./fib 35
Contêineres com Tipagem Estrita
Arrays regulares do PHP são universais mas famintos de memória e lentos devido a tabelas hash. O TypePHP adiciona estruturas std::vector, std::array, std::map e std::ordered_map.
<?php
use native_types;
function main(): void
{
$vector = std::vector(Type::Int);
$vector[] = 10;
$vector[] = 20;
$vector[] = 30;
$sum = 0;
foreach ($vector as $val) {
$sum += $val;
}
echo "Sum: " . $sum . "\n";
$map = std::ordered_map(Type::String, Type::Int);
$map["alpha"] = 1;
$map["beta"] = 2;
}
Nos testes dos desenvolvedores, um loop atualizando elementos em std::array levou 6.4 segundos versus 67.6 segundos para um array PHP regular com JIT habilitado. A velocidade essencialmente igualou um vector C++ escrito à mão (6.2 segundos).
Métodos em Primitivos
Em vez de um monte de funções como strlen(), strtoupper() ou in_array(), você pode chamar métodos diretamente em tipos base. O compilador resolve essas chamadas em tempo de build e as converte para chamadas diretas de funções C sem overhead de tabela virtual:
<?php
function main(): void
{
$str = "hello world";
echo $str->upper() . "\n";
echo $str->substr(0, 5) . "\n";
$items = [1, 3, 5, 7];
var_dump($items->contains(3));
}
Geração de Código Template via Atributos
Para evitar escrever dezenas de getters e setters manualmente, o TypePHP processa atributos customizados durante a compilação:
<?php
#[Printer(fields: ['id', 'name'])]
#[Arrayable(fields: ['id', 'name'])]
final class User
{
#[Constructor, Getter, With]
public int $id;
#[Constructor, Getter, Setter]
public string $name = 'guest';
}
function main(): void
{
$user = new User(1);
$user->setName('Ivan');
$copy = $user->withId(2);
echo $user->getId() . "\n"; // 1
echo $copy->getId() . "\n"; // 2
echo $user . "\n"; // User(id=1, name=Ivan)
}
O atributo #[With] gera um método que clona o objeto, altera o campo e retorna uma nova instância. Isso é conveniente para DTOs imutáveis.
Integração Direta com C++
Se algum algoritmo está faltando no PHP, você pode escrevê-lo em C++ e colocá-lo por perto. O linking acontece via um stub file com um corpo vazio:
// math.cpp
#include <phpx.h>
using namespace php;
Int php_fast_sum(Int a, Int b) {
return a + b;
}
<?php
// math.stub.php
function fast_sum(int $a, int $b): int {}
<?php
// main.php
function main(): void
{
echo fast_sum(10, 20) . "\n";
}
Performance nos Benchmarks
De acordo com as medições dos autores em testes sintéticos padrão do repositório php-src (bench.php e micro_bench.php com a flag -O3):
bench.phpcompleta em 0.603 s versus 5.034 s para o interpretador padrão (aproximadamente 8x de melhoria de velocidade);micro_bench.phpexecuta em 2.021 s versus 13.045 s (6.5x de melhoria de velocidade).
Os números são esperados para compilação AOT de matemática e branching. Em aplicações web reais, a maior parte do tempo vai para operações de I/O e banco de dados, então a melhoria será mais modesta lá. Mas para tarefas computacionais e workers em background, a diferença é perceptível.
Limitações e Trade-offs
Você ainda não pode portar um projeto Laravel ou Symfony existente para o TypePHP. Há uma série de regras estritas:
- Código executável em escopo global é proibido — todo código deve viver dentro de funções ou métodos.
- PHP 8.4 ou 8.5 com a biblioteca
libphp.socompilada (embed SAPI) é necessário para construir um binário. - Você precisa de GCC 9+ com suporte a C++17, CMake, e bibliotecas GMP/MPFR para cálculos precisos.
- Algumas features dinâmicas da linguagem, como troca livre de tipos ou referências complexas, não são suportadas intencionalmente.
O arquivo de configuração project.yml ajuda a gerenciar dependências e flags de otimização em projetos grandes:
name: myapp
mode: bin
php-version: "8.5"
optimize: 2
job: 8
build-dir: build
cxx-std: c++17
sources:
- src
- cpp-src
link-libs:
- curl
Quem Se Beneficiará Deste Projeto
O TypePHP está em desenvolvimento ativo. Tem menos de mil estrelas no GitHub até agora, mas o projeto é apoiado pela experiente equipe do Swoole.
Faz sentido experimentar o projeto se você precisa:
- Construir um utilitário CLI leve ou microsserviço como um binário único sem precisar instalar PHP no servidor alvo.
- Ocultar código fonte da aplicação ao entregar para um cliente on-premise, já que binários são significativamente mais difíceis de descompilar que bytecode.
- Escrever uma extensão PHP com computações pesadas sem mergulhar profundamente em C e API Zend.
- Acelerar módulos computacionais isolados, como parsers, empacotadores de dados, ou algoritmos de pontuação.
Para começar, basta clonar o repositório, construir um script de teste via bin/tpc.php app.php, e olhar o C++ gerado no diretório build.
Projetos relacionados