Última atualização:
Uma screenshot mostrando o processo de compilação do PHP.
Uma screenshot mostrando o processo de compilação do PHP.

Tutorial: como compilar o PHP

Nawarian
Nawarian php

Saber como compilar o PHP vai abrir uma das poucas portas necessárias para contribuir com a linguagem. Assim que você se familiarizar com este processo vai ser muito mais fácil para você contribuir de diversas maneiras: rodando testes e fazendo upload de reports, escrevendo novos testes e reportando ou corrigindo bugs.

Eu escrevi este tutorial como um esforço inspirado pelo post do Joe sobre o Bus Factor que assombra a linguagem nos dias atuais. Na minha opinião o post é bem alarmista, mas necessário. Já que meu tempo é escasso, faz mais sentido para mim multiplicar o conhecimento em vez de tentar contribuir com o código do PHP sozinho.

A gente vai utilizar o código fonte do PHP, disponível no Github. Você pode clicar aqui para visitar a página.

Navegação rápida

O código é separado entre core (/Zend) e extensões (/ext)

Antes de começar eu preciso te dar uma explicação ultra simplificada sobre a estrutura de pastas do PHP. A gente tem duas pastas:  /Zend/ext.

Pastas /Zend e /ext colocadas em evidência. Screenshot tomada da página do repositório no Github.
Pastas /Zend e /ext colocadas em evidência. Screenshot tomada da página do repositório no Github.

/Zend contém o código da Máquina Virtual, também conhecida como Zend Virtual Machine ou Zend VM. Os códigos desta pasta são responsáveis pela tokenização, interpretação, compilação, gerenciamento da pilha de execução e, em geral, rodar código PHP.

Se você não fazia ideia que o PHP tinha uma Máquina Virtual, considere também ler este post que explica sobre como PHP e seu compilador Just In Time funcionam, aqui você entende como o PHP funciona desde o seu código fonte, passando pela compilação até o tempo de execução. Não é complicado, não. Dê uma olhadinha 😉.

/ext é onde a mágica acontece! Esta é a pasta para as extensões suportadas pelo time de desenvolvimento do PHP. Toda função e classe que existe no PHP está descrita dentro desta pasta. Por exemplo, dentro de /ext/standard você encontra todas as funções da biblioteca padrão (SPL - Standard PHP Library). Você pode ver as funções da SPL para manipulação de strings aqui.

A forma mais fácil de entender a /ext é sempre lembrar que toda extensão PHP é na verdade uma interface para uma biblioteca escrita em C. Coisas que o PHP não poderia fazer sozinho (carregar DLLs, por exemplo) ou que não faria de forma tão performática (leitura de metadados de imagem no formato EXIF) são escritas aqui em C e disponibilizadas à linguagem PHP.

O motivo principal de eu te explicar sobre as pastas  /Zend/ext/ é que você sempre vai precisar compilar  /Zend quando compilar PHP. Mas você pode escolher a dedo quais extensões você quer compilar. Isto é muito útil quando se quer rodar testes ou depurar partes específicas do código.

Como seguir este tutorial

Eu imagino que você queira rodar os códigos aqui na sua máquina local, o que pode te induzir ao erro por diversos motivos: versões de pacote que não batem, pacotes quebrados, dependências que não vão te permitir atualizar...

Já que eu quero MUITO que você consiga compilar o PHP, eu vou rodar tudo dentro de um container docker vazio. Você não precisa disso para desenvolvimento no dia a dia se não quiser, mas eu recomendo demais que você utilize Docker pelo menos nas primeiras vezes que compilar PHP para evitar distrações inúteis.

Rode o comando abaixo. Isto vai subir um container Docker quase vazio, com pouquíssimas bibliotecas e programas instalados. Todos os comandos deste tutorial consideram que você está os executando dentro deste container.

$ docker run --rm -it alpine:3.13
/ #
Uma screenshot mostrando uma sessão bash utilizando o comando "docker run --rm -it alpine:3.13"
Uma screenshot mostrando uma sessão bash utilizando o comando "docker run --rm -it alpine:3.13"

Ao executar o comando acima você terá aberto uma sessão bash dentro do container. E assim que você sair do container (com o comando exit), o container será destruído.

Preparação do ambiente: pacotes necessários para compilar PHP

O código fonte do PHP tem várias dependências: algumas relacionadas à compilação do código em C, outras sobre tokenização e interpretação, outras são opcionais dependendo de quais extensões você quiser instalar.

Os requisitos que eu vou listar aqui são o mínimo necessário para compilar o PHP, sem extensões. Quando você adicionar outras extensões vai precisar adicionar mais dependências a esta lista.

Abaixo eu vou te explicar cada dependência para tornar tudo ao menos um pouquinho mais claro pra ti.

gcc

GCC (GNU C Compiler) é um compilador C livre, utilizado pela maioria dos projetos C mundo afora. Este compilador transforma código C em código de máquina, que seu computador consegue rodar.

libc-dev

A linguagem C é bem crua e não oferece ferramentas para lidar com strings, arquivos, rede e por aí vai. Pra isso a biblioteca libc vem para salvar o dia: ela possui várias funções que tornam o desenvolvimento com C muito mais simples. Você provavelmente já viu stdlib.h, stdio.h ou string.h em algum lugar. Estes arquivos .h (e vários outros) são da biblioteca libc.

autoconf

Autoconf é uma ferramenta para gerar configuração de build. Este programa é necessário para quando você executar o passo buildconf, descrito neste texto.

bison

Bison é um gerador de interpretadores. Sempre que você encontrar um arquivo com a extensão .y, pode ter certeza de que o processo de build do PHP vai utilizar Bison para transformar aquele arquivo .y em um arquivo .c que sabe como interpretar tokens lá definidos.

re2c

Re2C é uma ferramenta responsável por compilar expressões regulares em lexers performáticos em C.

make

Make é uma ferramenta versátil de automação de build. Esta ferramenta lê definições de um arquivo chamado Makefile que explica quais comandos precisam ser executados em cada passo da build.

Existem diferentes passos da compilação, vamos entender o motivo

Quando a gente compila código em C, normalmente a gente escolhe uma máquina alvo: uma arquitetura de CPU específica, um sistema operacional específico... Um binário compilado para Windows não vai rodar no Linux sem algum tipo de emulação. De forma semelhante, um programa compilado para uma CPU 64-bit não vai rodar numa CPU 32-bit.

Isso tudo fica ainda mais complicado quando você leva as CPUs em consideração: cada CPU pode ter sua própria maneira de processar opcodes, ler memória, comunicar com o barramento, otimizar certas operações... O trabalho do gcc é, dado uma CPU e Sistema Operacional alvos, transformar código em C em binários específicos para aquela CPU e sistema operacional.

Com todos estes detalhes fica difícil escrever um único Makefile que consiga capturar todos os requisitos de todas plataformas e CPUs possíveis. O que o projeto PHP faz (e isto é normal para projetos grandes em C) é gerar um Makefile antes da build.

O projeto utiliza várias macros m4 e gera um script chamado configure a partir delas. Este script quando executado, vai gerar um Makefile específico para a sua CPU e Sistema Operacional.

Pode parecer um pouco estranho agora, mas vamos seguir o passo a passo e provavelmente tudo vai ficar mais claro.

Vamos compilar o PHP a partir do código fonte

Instale os pacotes necessários

Para instalar os pacotes mencionados acima no Alpine Linux (no nosso container Docker), basta rodar os comandos a seguir:

/ # apk add gcc autoconf libc-dev bison re2c make
Dependências do PHP instaladas.
Dependências do PHP instaladas.

Eu também vou baixar o programa cURL pra gente poder baixar o código fonte do PHP a partir do Github.

/ # apk add curl
/ # cd /opt
/opt # curl -L https://github.com/php/php-src/archive/refs/heads/master.zip -o php.zip
/opt # unzip php.zip
/opt # cd php-src-master/
/opt # ls -la
Listando arquivos da pasta /opt/php-src-master que acabamos de baixar usando cURL.
Listando arquivos da pasta /opt/php-src-master que acabamos de baixar usando cURL.

Se você rodou os comandos acima, você deverá estar na pasta /opt/php-src-master e no ponto para começar o processo de compilação.

Construa o script de configuração

Toda máquina tem suas especificidades. Seu processador provavelmente tem uma arquitetura diferente do meu, do seu amigo ou de um provedor de Cloud. Já que C precisa de informações muito específica sobre o processador alvo (e outras coisinhas mais) a gente usa um gerador de configurações.

Rode o comando abaixo e você vai gerar um script configure  como resultado:

/opt/php-src-master # ./buildconf
Resultados da execução do script ./buildconf
Resultados da execução do script ./buildconf

Este passo utiliza várias macros .m4 espalhadas pelo projeto todo para compilar o script configure, utilizando a ferramenta autoconf. Como isso tudo funciona está fora do escopo deste tutorial, mas pode entrar em contato comigo se precisar de mais detalhes.

Gere o arquivo Makefile

Agora que a gente tem o script configure é só executar para obter o builder automatizado: o Makefile.

Execute ./configure com o parâmetro --disable-all pra evitar que quaisquer extensões opcionais como a FFI ou SimpleXML sejam instaladas.

/opt/php-src-master # ./configure --disable-all

O que configure faz é verificar a arquitetura, ferramentas e bibliotecas disponíveis na máquina local e seus respectivos diretórios. Toda esta informação é então compilada num arquivo Makefile:

Configure está verificando a arquitetura do sistema, ferramentas e bibliotecas instaladas.
Configure está verificando a arquitetura do sistema, ferramentas e bibliotecas instaladas.

Makefile gerado após este passo pode ser visto aqui:

O passo configure gerou um Makefile no mesmo diretório.
O passo configure gerou um Makefile no mesmo diretório.

Compilando o código fonte do PHP utilizando Make

Agora que o Makefile está disponível, precisamos apenas executar o comando make e pronto! Eu vou adicionar a opção -j8 para aumentar o número de cores utilizados durante a compilação.

/opt/php-src-master # make -j8
Ao rodar "make" vários objetos são compilados.
Ao rodar "make" vários objetos são compilados.

Se tudo correr bem, uma tela de sucesso parecida com a seguinte deverá aparecer e você então poderá rodar os testes.

Compilação completa com a mensagem "Build complete". Podemos rodar os testes agora.
Compilação completa com a mensagem "Build complete". Podemos rodar os testes agora.

Os binários ficam disponíveis na pasta sapi. Lá nós podemos encontrar diferentes binários como o FPM e CLI.

Binários dentro da pasta "sapi/". A imagem mostra a execução do comando "php -v" utilizando o binário CLI.
Binários dentro da pasta "sapi/". A imagem mostra a execução do comando "php -v" utilizando o binário CLI.

Bora rodar os testes

Rodar os testes não vai apenas te mostrar que PHP está funcionando da forma esperada, mas também lhe dá a oportunidade de compartilhar os resultados com a comunidade online. Os testes ficam disponibilizados aqui e podem ser utilizados por outras pessoas para coletar informação o suficiente pra resolver bugs (obrigado pela dica Daniel @geekcom2!).

Vamos rodar os testes utilizando a ferramenta Make:

/opt/php-src-master # make test

Se PHP falhar qualquer teste, você receberá um resultado parecido com o seguinte:

Tela mostrando testes que falharam e a pergunta "Você quer enviar este relatório de testes agora? [Sim/Não]".
Tela mostrando testes que falharam e a pergunta "Você quer enviar este relatório de testes agora? [Sim/Não]".

Ao escolher enviar o relatório você já estará contribuindo com a comunidade PHP. Bacana, né?

(Opcional) Instale o PHP

Se você estiver feliz com a versão que acabou de compilar e gostaria de tornar seu binário PHP disponível para todo o sistema, basta rodar a tarefa install do seu Makefile:

/opt/php-src-master # make install
Resultado da tarefa "make install". Ilustra que "php -v" mostra a versão "8.1.0-dev", mais atual no momento em que escrevo este tutorial.
Resultado da tarefa "make install". Ilustra que "php -v" mostra a versão "8.1.0-dev", mais atual no momento em que escrevo este tutorial.

Eu não recomendo instalar a versão compilada do PHP a menos que você saiba muito bem o que está fazendo. Se você precisar desta versão só para testar algumas coisas, utilize um alias ou adicione à variável PATH temporariamente.

Erros e problemas comuns

Se você sabe programar em C isto provavelmente não vai te afetar muito porque provavelmente já tem o costume de usar algumas convenções. Para quem programa somente em PHP eu resolvi coletar algumas dicas que vão facilitar o processo um pouquinho.

Eu coletei as dicas abaixo perguntando a outros membros da comunidade no twitter e através de pesquisas com usuários deste site. Aqui vai um resumo dos problemas e soluções.

Eu segui este tutorial passo a passo e não consegui compilar o PHP

Eu duvido que este caso aconteça com muita frequência porque estamos usando um ambiente isolado com Docker, as chances de algo dar errado sem que você tenha modificado uma linha sequer são baixíssimas.

Mas caso este seja mesmo o caso, ou se você tiver mudado alguma coisa e a build quebrou, a maior dica que eu posso lhe dar é: leia a mensagem de erro. Sério, leia a mensagem de erro!

A minha intenção não é te fazer sentir inferior! Acontece que a maioria de nós, devs PHP, ignoramos as mensagens de erro de compiladores C porque a gente tem o costume de ver coisas coloridas no terminal, que tornam muito fácil identificar onde está o problema. Isto não é comum em C.

Mas normalmente o problema é descrito na última mensagem impressa na tela, porque as boas práticas em C dizem que assim que um problema acontecer, o código deve abortar o quanto antes.

Se você não entende inglês bem o suficiente, você vai precisar adivinhar baseado em alguns símbolos.

Por exemplo, ao habilitar a extensão simplexml a nnossa compilação já não vai mais funcionar por algum motivo:

/opt/php-src-master # ./configure --disable-all --enable-simplexml
Rodando "configure" com a opção "--enable-simplexml".
Rodando "configure" com a opção "--enable-simplexml".

Ao utilizar a flag "--disable-all" nós desabilitamos todas extensões possíveis, incluindo a extensão libxml que adiciona suporte a XML. Aqui a mensagem de erro que recebemos:

configure: error: SimpleXML extension requires LIBXML extension, add --with-libxml

Rodando "configure" com a flag "--enable-simplexml" gera um erro dizendo que a extensão LIBXML não está disponível.
Rodando "configure" com a flag "--enable-simplexml" gera um erro dizendo que a extensão LIBXML não está disponível.

O próprio programa nos instrui que ao adicionar "--with-libxml" o problema deverá ser resolvido.

Vamos rapidamente visitar o manual da extensão simplexml. Em "Instalação > Requisitos" nós encontramos mais informações sobre quais extensões e bibliotecas são necessárias, e mais outras dicas de como compilar esta extensão.

Tanto a página quanto o comando configure dizem a mesma coisa: é preciso habilitar a extensão libxml. Então teoricamente ao adicionar --with-libxml ao configure, nosso problema deverá ser resolvido.

Mas agora estamos vacinados e sabemos que é possível visitar a página da extensão libxml antes de tentar compilar de novo para que possamos instalar suas dependências. Vamos abrir a página de requisitos da extensão libxml e verificar suas dependências: a extensão depende da biblioteca libxml na versão 2.6.0 ou superior. Vamos instalar esta biblioteca e depois compilar o PHP novamente:

/opt/php-src-master # apk add libxml2-dev
/opt/php-src-master # ./configure --disable-all --with-libxml --enable-simplexml
Adicionamos o pacote libxml2-dev e executamos o comando configure com as extensões libxml ("--with-libxml") e simplexml ("--enable-simplexml").
Adicionamos o pacote libxml2-dev e executamos o comando configure com as extensões libxml ("--with-libxml") e simplexml ("--enable-simplexml").

Agora é só rodar Make novamente para que os binários dentro de /sapi fiquem atualizados e com suporte à extensão SimpleXML.

Como adivinhar quais extensões eu posso adicionar à minha configuração?

Isto é um problema comum em projetos em C. Eu ainda não vi um projeto em C que utilize um gerenciador de dependências parecido com o Composer our NPM.

Com programas em C você normalmente deveria resolver as dependências manualmente e, na verdade, isto é muito benéfico e eficiente. Por exemplo, esta boa pratica reduz o tamanho do binário a ser construído porque várias bibliotecas e dependências são conectadas de forma dinâmica (arquivos .dll ou .so) quando o programa é executado em vez de copiar e compilar todo seu código fonte.

O ideal é que antes de habilitar uma extensão você busque por sua página no manual do PHP. Toda extensão padrão do PHP têm um manual que inclui uma página de Instalação, requisitos e dependências.

Eu ainda estou tendo problemas mesmo seguindo este tutorial passo a passo

Pode ser que o artigo envelheceu: PHP é um projeto muito ativo, as coisas mudam e você precisa conseguir se adaptar. As ideias principais deste tutorial vão te acompanhar na jornada, mas talvez você precise investigar um pouco por si só.

Normalmente quando mudanças bruscas acontecem, elas ficam documentadas no código fonte em dois arquivos: UPGRADING e UPGRADING INTERNALS. Dá uma lida nestes arquivos se você tiver algum problema e tem certeza de que os passos e configurações que você usou já funcionaram algum dia.

Obrigado novamente Daniel @geekcom2 pela dica!

Como saber quais extensões estão disponíveis?

Existem várias flags de compilação disponíveis que a gente pode passar para o script configure como --enable-ffi ou --enable-simplexml. Mas como encontrar todas elas?

O ideal é que você saiba quais extensões quer habilitar, verifique as páginas do manual de cada uma e a partir de lá entenda quais extensões e flags são necessárias.

Se você não quiser ou não puder visitar as páginas do manual, verifique o código offline. Depois de rodar buildconf o arquivo configure será criado no diretório php-src. Basta rodar configure com a flag --help:  

/opt/php-src-master # ./configure --help

O comando acima vai mostrar uma lista de flags de compilação e variáveis de ambiente que você pode mudar antes de gerar seu Makefile.

E se eu quiser utilizar bibliotecas modificadas ou não quiser as instalar no sistema todo?

As vezes você vai ter a necessidade de utilizar uma versão específica de uma biblioteca para satisfazer a instalação do PHP, mas seu sistema já utiliza uma versão diferente. Mudar a versão no sistema todo as vezes não é a melhor opção...

Uma opção para resolver este problema é utilizar um ambiente isolado como uma máquina virtual ou um container Docker por exemplo. Mas isto não resolve todos os casos.

Outra opção é baixar e compilar manualmente a biblioteca em outro diretório. Desta forma você pode compilar a biblioteca sem instalar no sistema, criando objetos dinâmicos (arquivos .dll, .so ou .dylib) que não precisam ser utilizados pelo resto do sistema.

Se você escolher compilar uma biblioteca manualmente, você precisa dizer ao configure em quais pastas procurar determinadas bibliotecas. Você pode fazer isso através de variáveis de ambiente. Tem até um FAQ no site oficial do PHP sobre isso.

/opt/php-src-master # export LDFLAGS=-L/opt/a-biblioteca-que-voce-compilou
/opt/php-src-master # ./configure

Eu consegui compilar o PHP mas não encontro o binário

Todos os binários (executáveis) do PHP ficam dentro da pasta sapi/. Lá você encontra diferentes binários como o FPM, CLI e Embed. Utilize o que for mais adequado para seu caso de uso.

Obrigado, comunidade, por me ajudar a construi este artigo

Desta vez em vez de pesquisar e escrever tudo sozinho eu decidi pedir ajuda da comunidade brasileira de PHP, especialmente na seção de Erros e problemas comuns. Eu vou deixar os nomes e perfis destas pessoas como forma de reconhecimento e agradecimento pela ajuda.

Se você, assim como eu, gosta de fazer o PHP chorar, trate de seguir estas pessoas. Eu garanto conteúdo e sabedorias de alta qualidade:

Algumas das contribuições foram diretas, outras apenas propagaram a minha voz para alcançar mais pessoas. Eu sou muito grato por toda a força que vocês me deram!

Também não esqueça de me seguir no twitter se você gosta do tipo de conteúdo que eu compartilho e gostaria de ver coisas aleatórias sobre programação no seu feed: @nawarian.

E agora?

Agora que você se familiarizou com a compilação do PHP, vá em frente e rode alguns testes, quebra alguns deles brincando com o código C. Vai se divertir!

Eu espero que este tutorial vá te ajudar a tomar mais um passo em direção a contribuir com a comunidade PHP. Nós precisamos de você!

Até a próxima!

Comentários