diff --git a/docs/pt-br/CONTRIBUTING.md b/docs/pt-br/CONTRIBUTING.md index fb2f74e0..7183424d 100644 --- a/docs/pt-br/CONTRIBUTING.md +++ b/docs/pt-br/CONTRIBUTING.md @@ -33,8 +33,8 @@ Adicione diretórios ao `.dockerignore`. ### Sem Docker (Linux e macOS) -[Siga as instruções para compilar a partir dos fontes](compile.md) e passe a -flag de configuração `--debug`. +[Siga as instruções para compilar a partir do código-fonte](compile.md) e passe +a flag de configuração `--debug`. ## Executando a suite de testes @@ -199,7 +199,7 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push - [PHP embedding in Go (go-php)](https://github.com/deuill/go-php) - [PHP embedding in Go (GoEmPHP)](https://github.com/mikespook/goemphp) - [PHP embedding in C++](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7) -- [Extending and Embedding PHP por Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false) +- [Extending and Embedding PHP, por Sara Golemon](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false) - [What the heck is TSRMLS_CC, anyway?](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html) - [SDL bindings](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main) diff --git a/docs/pt-br/README.md b/docs/pt-br/README.md index b0db439a..bb8533fc 100644 --- a/docs/pt-br/README.md +++ b/docs/pt-br/README.md @@ -1,14 +1,14 @@ -# FrankenPHP: Servidor de aplicações moderno para PHP +# FrankenPHP: um moderno servidor de aplicações para PHP

FrankenPHP

-O FrankenPHP é um servidor de aplicações moderno para PHP, construído sobre o +O FrankenPHP é um moderno servidor de aplicações para PHP, construído sobre o servidor web [Caddy](https://caddyserver.com/). -O FrankenPHP oferece superpoderes às suas aplicações PHP graças aos seus -recursos impressionantes: [_Early Hints_](early-hints.md), -[modo worker](worker.md), [recursos em tempo real](mercure.md), suporte -automático a HTTPS, HTTP/2 e HTTP/3... +O FrankenPHP dá superpoderes às suas aplicações PHP graças aos seus recursos +impressionantes: [_Early Hints_](early-hints.md), [modo worker](worker.md), +[recursos em tempo real](mercure.md), suporte automático a HTTPS, HTTP/2 e +HTTP/3... O FrankenPHP funciona com qualquer aplicação PHP e torna seus projetos Laravel e Symfony mais rápidos do que nunca, graças às suas integrações oficiais com o @@ -56,7 +56,7 @@ frankenphp php-cli /caminho/para/seu/script.php ### Docker -Alternativamente, [imagens do Docker](docker.md) estão disponíveis: +Alternativamente, [imagens Docker](docker.md) estão disponíveis: ```console docker run -v .:/app/public \ @@ -94,11 +94,12 @@ frankenphp php-server ## Documentação - [Modo clássico](classic.md) -- [Modo Worker](worker.md) +- [Modo worker](worker.md) - [Suporte a Early Hints (código de status HTTP 103)](early-hints.md) - [Tempo real](mercure.md) - [Servindo grandes arquivos estáticos com eficiência](x-sendfile.md) - [Configuração](config.md) +- [Escrevendo extensões PHP em Go](extensions.md) - [Imagens Docker](docker.md) - [Implantação em produção](production.md) - [Otimização de desempenho](performance.md) @@ -109,7 +110,7 @@ frankenphp php-server - [Integração com Laravel](laravel.md) - [Problemas conhecidos](known-issues.md) - [Aplicação de demonstração (Symfony) e benchmarks](https://github.com/dunglas/frankenphp-demo) -- [Documentação da biblioteca Go](https://pkg.go.dev/github.com/dunglas/frankenphp) +- [Documentação da biblioteca Go](https://pkg.go.dev/github.com/php/frankenphp) - [Contribuindo e depurando](CONTRIBUTING.md) ## Exemplos e esqueletos diff --git a/docs/pt-br/classic.md b/docs/pt-br/classic.md index bab35f85..e41c27d2 100644 --- a/docs/pt-br/classic.md +++ b/docs/pt-br/classic.md @@ -18,9 +18,9 @@ semelhante ao modo dinâmico do PHP-FPM. As conexões enfileiradas aguardarão indefinidamente até que uma thread PHP esteja disponível para servi-las. Para evitar isso, você pode usar a -[configuração](config.md#configuracao-do-caddyfile) `max_wait_time` para limitar -o tempo que uma requisição pode esperar por uma thread PHP livre antes de ser -rejeitada. +[configuração](config.md#configuracao-do-caddyfile) `max_wait_time` na +configuração global do FrankenPHP para limitar o tempo que uma requisição pode +esperar por uma thread PHP livre antes de ser rejeitada. Além disso, você pode definir um [tempo limite de escrita razoável no Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts). diff --git a/docs/pt-br/compile.md b/docs/pt-br/compile.md index 52bb7c15..194d41d9 100644 --- a/docs/pt-br/compile.md +++ b/docs/pt-br/compile.md @@ -1,4 +1,4 @@ -# Compilar a partir dos fontes +# Compilar a partir do código-fonte Este documento explica como criar um binário FrankenPHP que carregará o PHP como uma biblioteca dinâmica. @@ -29,10 +29,10 @@ brew link --overwrite --force shivammathur/php/php-zts ### Compilando o PHP -Alternativamente, você pode compilar o PHP a partir dos códigos-fonte com as +Alternativamente, você pode compilar o PHP a partir do código-fonte com as opções necessárias para o FrankenPHP seguindo estes passos. -Primeiro, [obtenha os códigos-fonte do PHP](https://www.php.net/downloads.php) e +Primeiro, [obtenha o código-fonte do PHP](https://www.php.net/downloads.php) e extraia-os: ```console @@ -91,10 +91,10 @@ devem ser instaladas. Alternativamente, esses recursos podem ser desabilitados passando as tags de compilação para o compilador Go. -| Recurso | Dependência | Tag de compilação para desabilitá-lo | -|----------------------------------------|-----------------------------------------------------------------------|--------------------------------------| -| Compressão Brotli | [Brotli](https://github.com/google/brotli) | `nobrotli` | -| Reiniciar workers ao alterar o arquivo | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | `nowatcher` | +| Recurso | Dependência | Tag de compilação para desabilitá-lo | +|---------------------------------------|-----------------------------------------------------------------------|--------------------------------------| +| Compressão Brotli | [Brotli](https://github.com/google/brotli) | `nobrotli` | +| Reiniciar workers ao alterar arquivos | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | `nowatcher` | ## Compilando a aplicação Go @@ -115,7 +115,7 @@ CGO_CFLAGS=$(php-config --includes) \ CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ xcaddy build \ --output frankenphp \ - --with github.com/dunglas/frankenphp/caddy \ + --with github.com/php/frankenphp/caddy \ --with github.com/dunglas/mercure/caddy \ --with github.com/dunglas/vulcain/caddy # Adicione módulos Caddy e extensões FrankenPHP extras aqui @@ -141,7 +141,7 @@ Alternativamente, é possível compilar o FrankenPHP sem o `xcaddy` usando o comando `go` diretamente: ```console -curl -L https://github.com/dunglas/frankenphp/archive/refs/heads/main.tar.gz | tar xz +curl -L https://github.com/php/frankenphp/archive/refs/heads/main.tar.gz | tar xz cd frankenphp-main/caddy/frankenphp CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build -tags=nobadger,nomysql,nopgx ``` diff --git a/docs/pt-br/config.md b/docs/pt-br/config.md index c6641b7a..e36a144d 100644 --- a/docs/pt-br/config.md +++ b/docs/pt-br/config.md @@ -4,7 +4,7 @@ FrankenPHP, Caddy, bem como os módulos Mercure e Vulcain, podem ser configurado usando [os formatos suportados pelo Caddy](https://caddyserver.com/docs/getting-started#your-first-config). -Nas [imagens do Docker](docker.md), o `Caddyfile` está localizado em +Nas [imagens Docker](docker.md), o `Caddyfile` está localizado em `/etc/frankenphp/Caddyfile`. O binário estático também procurará pelo `Caddyfile` no diretório onde o comando `frankenphp run` é executado. @@ -19,9 +19,9 @@ arquivos de configuração nos locais descritos acima. ## Docker - `php.ini`: `/usr/local/etc/php/php.ini` (nenhum `php.ini` é fornecido por - padrão) -- Arquivos de configuração adicionais: `/usr/local/etc/php/conf.d/*.ini` -- Extensões PHP: `/usr/local/lib/php/extensions/no-debug-zts-/` + padrão); +- Arquivos de configuração adicionais: `/usr/local/etc/php/conf.d/*.ini`; +- Extensões PHP: `/usr/local/lib/php/extensions/no-debug-zts-/`; - Você deve copiar um template oficial fornecido pelo projeto PHP: ```dockerfile @@ -37,18 +37,18 @@ RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini ## Pacotes RPM e Debian - `php.ini`: `/etc/frankenphp/php.ini` (um arquivo `php.ini` com configurações - de produção é fornecido por padrão) -- Arquivos de configuração adicionais: `/etc/frankenphp/php.d/*.ini` -- Extensões PHP: `/usr/lib/frankenphp/modules/` + de produção é fornecido por padrão); +- Arquivos de configuração adicionais: `/etc/frankenphp/php.d/*.ini`; +- Extensões PHP: `/usr/lib/frankenphp/modules/`. ## Binário estático - `php.ini`: O diretório no qual `frankenphp run` ou `frankenphp php-server` é - executado e, em seguida, `/etc/frankenphp/php.ini` -- Arquivos de configuração adicionais: `/etc/frankenphp/php.d/*.ini` -- Extensões PHP: não podem ser carregadas, empacote-as no próprio binário -- Copie um dos `php.ini-production` ou `php.ini-development` fornecidos - [nos fontes do PHP](https://github.com/php/php-src/). + executado e, em seguida, `/etc/frankenphp/php.ini`; +- Arquivos de configuração adicionais: `/etc/frankenphp/php.d/*.ini`; +- Extensões PHP: não podem ser carregadas, empacote-as no próprio binário; +- Copie um dos arquivos `php.ini-production` ou `php.ini-development` fornecidos + [no código-fonte do PHP](https://github.com/php/php-src/). ## Configuração do Caddyfile @@ -79,11 +79,12 @@ A [opção global](https://caddyserver.com/docs/caddyfile/concepts#global-option max_wait_time # Define o tempo máximo que uma requisição pode esperar por uma thread PHP livre antes de atingir o tempo limite. Padrão: disabled. php_ini # Define uma diretiva php.ini. Pode ser usada várias vezes para definir múltiplas diretivas. worker { - file # Define o caminho para o script do worker. + file # Define o caminho para o worker script. num # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de CPUs disponíveis. - env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificado mais de uma vez para múltiplas variáveis de ambiente. - watch # Define o caminho para monitorar alterações no arquivo. Pode ser especificado mais de uma vez para múltiplos caminhos. + env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. + watch # Define o caminho para monitorar alterações em arquivos. Pode ser especificada mais de uma vez para múltiplos caminhos. name # Define o nome do worker, usado em logs e métricas. Padrão: caminho absoluto do arquivo do worker. + max_consecutive_failures # Define o número máximo de falhas consecutivas antes do worker ser considerado inoperante. -1 significa que o worker sempre reiniciará. Padrão: 6. } } } @@ -109,13 +110,15 @@ servidor: ```caddyfile app.example.com { + root /caminho/para/aplicacao/public php_server { - root /caminho/para/aplicacao/public + root /caminho/para/aplicacao/public # permite melhor armazenamento em cache worker index.php } } outra.example.com { + root /caminho/para/outra/aplicacao/public php_server { root /caminho/para/outra/aplicacao/public worker index.php @@ -162,14 +165,15 @@ php_server [] { root # Define a pasta raiz para o site. Padrão: diretiva `root`. split_path # Define as substrings para dividir o URI em duas partes. A primeira substring correspondente será usada para separar as "informações de caminho" do caminho. A primeira parte é sufixada com a substring correspondente e será assumida como o nome real do recurso (script CGI). A segunda parte será definida como PATH_INFO para o script usar. Padrão: `.php` resolve_root_symlink false # Desabilita a resolução do diretório `root` para seu valor real avaliando um link simbólico, se houver (habilitado por padrão). - env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificado mais de uma vez para múltiplas variáveis de ambiente. + env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. file_server off # Desabilita a diretiva interna file_server. - worker { # Cria um worker específico para este servidor. Pode ser especificado mais de uma vez para múltiplos workers. - file # Define o caminho para o script do worker, pode ser relativo à raiz do php_server - num # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de threads disponíveis + worker { # Cria um worker específico para este servidor. Pode ser especificada mais de uma vez para múltiplos workers. + file # Define o caminho para o worker script, pode ser relativo à raiz do php_server. + num # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de threads disponíveis. name # Define o nome do worker, usado em logs e métricas. Padrão: caminho absoluto do arquivo do worker. Sempre começa com m# quando definido em um bloco php_server. - watch # Define o caminho para monitorar alterações no arquivo. Pode ser especificado mais de uma vez para múltiplos caminhos. - env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificado mais de uma vez para múltiplas variáveis de ambiente. As variáveis de ambiente para este worker também são herdadas do php_server pai, mas podem ser sobrescritas aqui. + watch # Define o caminho para monitorar alterações em arquivos. Pode ser especificada mais de uma vez para múltiplos caminhos. + env # Define uma variável de ambiente extra para o valor fornecido. Pode ser especificada mais de uma vez para múltiplas variáveis de ambiente. As variáveis de ambiente para este worker também são herdadas do pai do php_server, mas podem ser sobrescritas aqui. + match # Corresponde o worker a um padrão de caminho. Substitui try_files e só pode ser usada na diretiva php_server. } worker # Também pode usar a forma abreviada, como no bloco global frankenphp. } @@ -180,7 +184,7 @@ php_server [] { Como os workers inicializam sua aplicação apenas uma vez e a mantêm na memória, quaisquer alterações nos seus arquivos PHP não serão refletidas imediatamente. -Os workers podem ser reiniciados em caso de alterações nos arquivos por meio da +Os workers podem ser reiniciados em caso de alterações em arquivos por meio da diretiva `watch`. Isso é útil para ambientes de desenvolvimento. @@ -224,9 +228,37 @@ Você também pode especificar um ou mais diretórios por meio de um - Tenha cuidado ao monitorar arquivos criados em tempo de execução (como logs), pois eles podem causar reinicializações indesejadas de workers. -O monitor de arquivos é baseado em +O monitor de arquivos é baseado no [e-dant/watcher](https://github.com/e-dant/watcher). +## Correspondendo o worker a um caminho + +Em aplicações PHP tradicionais, os scripts são sempre colocados no diretório +público. +Isso também se aplica aos worker scripts, que são tratados como qualquer outro +script PHP. +Se você quiser colocar o worker script fora do diretório público, pode fazê-lo +por meio da diretiva `match`. + +A diretiva `match` é uma alternativa otimizada ao `try_files`, disponível apenas +dentro do `php_server` e do `php`. +O exemplo a seguir sempre servirá um arquivo no diretório público, se presente, +e, caso contrário, encaminhará a requisição para o worker que corresponde ao +padrão de caminho. + +```caddyfile +{ + frankenphp { + php_server { + worker { + file /caminho/para/worker.php # arquivo pode estar fora do caminho público + match /api/* # todas as requisições que começam com /api/ serão tratadas por este worker + } + } + } +} +``` + ### Full Duplex (HTTP/1) Ao usar HTTP/1.x, pode ser desejável habilitar o modo full-duplex para permitir @@ -267,10 +299,10 @@ no `Caddyfile` sem modificá-lo: - `SERVER_NAME`: altera [os endereços nos quais escutar](https://caddyserver.com/docs/caddyfile/concepts#addresses), - os nomes de host fornecidos também serão usados para o certificado TLS gerado. -- `SERVER_ROOT`: altera o diretório raiz do site, o padrão é `public/`. + os nomes de host fornecidos também serão usados para o certificado TLS gerado; +- `SERVER_ROOT`: altera o diretório raiz do site, o padrão é `public/`; - `CADDY_GLOBAL_OPTIONS`: injeta - [opções globais](https://caddyserver.com/docs/caddyfile/options). + [opções globais](https://caddyserver.com/docs/caddyfile/options); - `FRANKENPHP_CONFIG`: injeta a configuração sob a diretiva `frankenphp`. Quanto às SAPIs FPM e CLI, as variáveis de ambiente são expostas por padrão na @@ -309,7 +341,7 @@ Você também pode alterar a configuração do PHP usando a diretiva `php_ini` n ## Habilitar o modo de depuração -Ao usar a imagem do Docker, defina a variável de ambiente `CADDY_GLOBAL_OPTIONS` +Ao usar a imagem Docker, defina a variável de ambiente `CADDY_GLOBAL_OPTIONS` como `debug` para habilitar o modo de depuração: ```console diff --git a/docs/pt-br/docker.md b/docs/pt-br/docker.md index 5c9d131b..aaf9f3ec 100644 --- a/docs/pt-br/docker.md +++ b/docs/pt-br/docker.md @@ -29,7 +29,7 @@ FROM dunglas/frankenphp COPY . /app/public ``` -Em seguida, execute estes comandos para compilar e executar a imagem do Docker: +Em seguida, execute estes comandos para construir e executar a imagem Docker: ```console docker build -t minha-app-php . @@ -55,13 +55,13 @@ RUN install-php-extensions \ opcache ``` -## Como instalar mais módulos do Caddy +## Como instalar mais módulos Caddy O FrankenPHP é construído sobre o Caddy, e todos os -[módulos do Caddy](https://caddyserver.com/docs/modules/) podem ser usados com o +[módulos Caddy](https://caddyserver.com/docs/modules/) podem ser usados com o FrankenPHP. -A maneira mais fácil de instalar módulos personalizados do Caddy é usar o +A maneira mais fácil de instalar módulos Caddy personalizados é usar o [xcaddy](https://github.com/caddyserver/xcaddy): ```dockerfile @@ -78,8 +78,8 @@ RUN CGO_ENABLED=1 \ CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ xcaddy build \ --output /usr/local/bin/frankenphp \ - --with github.com/dunglas/frankenphp=./ \ - --with github.com/dunglas/frankenphp/caddy=./caddy/ \ + --with github.com/php/frankenphp=./ \ + --with github.com/php/frankenphp/caddy=./caddy/ \ --with github.com/dunglas/caddy-cbrotli \ # Mercure e Vulcain estão incluídos na compilação oficial, mas sinta-se # à vontade para removê-los @@ -93,10 +93,10 @@ FROM dunglas/frankenphp AS runner COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp ``` -A imagem `builder` fornecida pelo FrankenPHP contém uma versão compilada de +A imagem `builder` fornecida pelo FrankenPHP contém uma versão compilada da `libphp`. [Imagens de builder](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) -são fornecidas para todas as versões do FrankenPHP e PHP, tanto para Debian +são fornecidas para todas as versões do FrankenPHP e do PHP, tanto para Debian quanto para Alpine. > [!TIP] @@ -177,8 +177,8 @@ RUN \ useradd ${USER}; \ # Adiciona capacidade adicional para vincular às portas 80 e 443 setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \ - # Concede acesso de escrita a /data/caddy e /config/caddy - chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy + # Concede acesso de escrita a /config/caddy e /data/caddy + chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER} ``` @@ -203,8 +203,8 @@ RUN \ useradd ${USER}; \ # Remove a capacidade padrão setcap -r /usr/local/bin/frankenphp; \ - # Concede acesso de escrita a /data/caddy e /config/caddy - chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy + # Concede acesso de escrita a /config/caddy e /data/caddy + chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER} ``` @@ -215,17 +215,17 @@ Exemplo: `:8000` ## Atualizações -As imagens do Docker são compiladas: +As imagens Docker são construídas: -- quando uma tag de uma nova versão é criada. -- diariamente às 4h UTC, se novas versões das imagens oficiais do PHP estiverem +- Quando uma tag de uma nova versão é criada; +- Diariamente às 4h UTC, se novas versões das imagens oficiais do PHP estiverem disponíveis. ## Versões de desenvolvimento As versões de desenvolvimento estão disponíveis no repositório Docker [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev). -Uma nova compilação é acionada sempre que um commit é enviado para o branch +Uma nova construção é acionada sempre que um commit é enviado para o branch principal do repositório do GitHub. As tags `latest*` apontam para o HEAD do branch `main`. diff --git a/docs/pt-br/embed.md b/docs/pt-br/embed.md index 0c28ebd9..41c34a78 100644 --- a/docs/pt-br/embed.md +++ b/docs/pt-br/embed.md @@ -1,6 +1,6 @@ # Aplicações PHP como binários independentes -O FrankenPHP tem a capacidade de incorporar o código-fonte e os recursos de +O FrankenPHP tem a capacidade de incorporar o código-fonte e os assets de aplicações PHP em um binário estático e independente. Graças a esse recurso, aplicações PHP podem ser distribuídas como binários @@ -11,7 +11,7 @@ Saiba mais sobre esse recurso [na apresentação feita por Kévin na SymfonyCon 2023](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/). Para incorporar aplicações Laravel, -[leia esta entrada específica na documentação](laravel.md#laravel-apps-as-standalone-binaries). +[leia esta entrada específica na documentação](laravel.md#aplicacoes-laravel-como-binarios-independentes). ## Preparando sua aplicação @@ -20,9 +20,9 @@ pronta para ser incorporada. Por exemplo, você provavelmente deseja: -- Instalar as dependências de produção da aplicação. -- Fazer o dump do carregador automático. -- Habilitar o modo de produção da sua aplicação (se houver). +- Instalar as dependências de produção da aplicação; +- Fazer o dump do carregador automático; +- Habilitar o modo de produção da sua aplicação (se houver); - Remover arquivos desnecessários, como `.git` ou testes, para reduzir o tamanho do seu binário final. @@ -83,7 +83,7 @@ Docker que fornecemos. > [`.dockerignore` padrão do Docker do Symfony](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore)) > ignorarão o diretório `vendor/` e os arquivos `.env`. > Certifique-se de ajustar ou remover o arquivo `.dockerignore` antes da - > compilação. + > construção. 2. Construa: @@ -105,7 +105,7 @@ Se você não quiser usar o Docker ou quiser compilar um binário para macOS, us script de shell que fornecemos: ```console -git clone https://github.com/dunglas/frankenphp +git clone https://github.com/php/frankenphp cd frankenphp EMBED=/caminho/para/sua/aplicacao ./build-static.sh ``` @@ -145,8 +145,8 @@ Você também pode executar os scripts PHP CLI incorporados ao seu binário: ## Extensões PHP -Por padrão, o script criará as extensões requeridas pelo arquivo `composer.json` -do seu projeto, se houver. +Por padrão, o script compilará as extensões requeridas pelo arquivo +`composer.json` do seu projeto, se houver. Se o arquivo `composer.json` não existir, as extensões padrão serão compiladas, conforme documentado na [entrada de compilações estáticas](static.md). diff --git a/docs/pt-br/extensions.md b/docs/pt-br/extensions.md new file mode 100644 index 00000000..3d512c1b --- /dev/null +++ b/docs/pt-br/extensions.md @@ -0,0 +1,1001 @@ +# Escrevendo extensões PHP em Go + +Com o FrankenPHP, você pode **escrever extensões PHP em Go**, o que permite +criar **funções nativas de alto desempenho** que podem ser chamadas diretamente +do PHP. +Suas aplicações podem aproveitar qualquer biblioteca Go existente ou nova, bem +como o famoso modelo de concorrência de **goroutines diretamente do seu código +PHP**. + +Escrever extensões PHP normalmente é feito em C, mas também é possível +escrevê-las em outras linguagens com um pouco de trabalho extra. +As extensões PHP permitem que você aproveite o poder das linguagens de baixo +nível para estender as funcionalidades do PHP, por exemplo, adicionando funções +nativas ou otimizando operações específicas. + +Graças aos módulos Caddy, você pode escrever extensões PHP em Go e integrá-las +rapidamente ao FrankenPHP. + +## Duas abordagens + +O FrankenPHP oferece duas maneiras de criar extensões PHP em Go: + +1. **Usando o gerador de extensões** - A abordagem recomendada que gera todo o + código boilerplate necessário para a maioria dos casos de uso, permitindo que + você se concentre em escrever seu código em Go. +2. **Implementação manual** - Controle total sobre a estrutura da extensão para + casos de uso avançados. + +Começaremos com a abordagem do gerador, pois é a maneira mais fácil de começar, +e, em seguida, mostraremos a implementação manual para aqueles que precisam de +controle total. + +## Usando o gerador de extensões + +O FrankenPHP vem com uma ferramenta que permite **criar uma extensão PHP** +usando apenas Go. +**Não é necessário escrever código C** ou usar CGO diretamente: o FrankenPHP +também inclui uma **API de tipos pública** para ajudar você a escrever suas +extensões em Go sem ter que se preocupar com **o malabarismo de tipos entre +PHP/C e Go**. + +> [!TIP] +> Se quiser entender como as extensões podem ser escritas em Go do zero, leia a +> seção de implementação manual abaixo, que demonstra como escrever uma extensão +> PHP em Go sem usar o gerador. + +Lembre-se de que esta ferramenta **não é um gerador de extensões completo**. +Ela foi criada para ajudar você a escrever extensões simples em Go, mas não +oferece os recursos mais avançados das extensões PHP. +Se precisar escrever uma extensão mais **complexa e otimizada**, talvez seja +necessário escrever algum código em C ou usar CGO diretamente. + +### Pré-requisitos + +Conforme abordado na seção de implementação manual abaixo, você precisa +[obter o código-fonte do PHP](https://www.php.net/downloads.php) e criar um novo +módulo Go. + +#### Criando um novo módulo e obtendo o código-fonte do PHP + +O primeiro passo para escrever uma extensão PHP em Go é criar um novo módulo Go. +Você pode usar o seguinte comando para isso: + +```console +go mod init github.com// +``` + +O segundo passo é +[obter o código-fonte do PHP](https://www.php.net/downloads.php) para os +próximos passos. +Depois de obtê-los, descompacte-os no diretório de sua escolha, não dentro do +seu módulo Go: + +```console +tar xf php-* +``` + +### Escrevendo a extensão + +Agora tudo está configurado para escrever sua função nativa em Go. +Crie um novo arquivo chamado `stringext.go`. +Nossa primeira função receberá uma string como argumento, o número de vezes que +ela será repetida, um booleano para indicar se a string deve ser invertida e +retornará a string resultante. +Deve ficar assim: + +```go +import ( + "C" + "github.com/dunglas/frankenphp" + "strings" +) + +//export_php:function repeat_this(string $str, int $count, bool $reverse): string +func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer { + str := frankenphp.GoString(unsafe.Pointer(s)) + + result := strings.Repeat(str, int(count)) + if reverse { + runes := []rune(result) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] + } + result = string(runes) + } + + return frankenphp.PHPString(result, false) +} +``` + +Há duas coisas importantes a serem observadas aqui: + +- Um comentário de diretiva `//export_php:function` define a assinatura da + função no PHP. + É assim que o gerador sabe como gerar a função PHP com os parâmetros e o tipo + de retorno corretos; +- A função deve retornar um `unsafe.Pointer`. + O FrankenPHP fornece uma API para ajudar você com o malabarismo de tipos entre + C e Go. + +Embora o primeiro ponto fale por si, o segundo pode ser mais difícil de +entender. +Vamos nos aprofundar no malabarismo de tipos na próxima seção. + +### Malabarismo de tipos + +Embora alguns tipos de variáveis tenham a mesma representação de memória entre +C/PHP e Go, alguns tipos exigem mais lógica para serem usados diretamente. +Esta talvez seja a parte mais difícil quando se trata de escrever extensões, +pois requer a compreensão dos componentes internos da Zend Engine e de como as +variáveis são armazenadas internamente no PHP. +Esta tabela resume o que você precisa saber: + +| Tipo PHP | Tipo Go | Conversão direta | Auxiliar de C para Go | Auxiliar de Go para C | Suporte a métodos de classe | +|--------------------|-------------------------------|------------------|-----------------------------------|------------------------------------|-----------------------------| +| `int` | `int64` | ✅ | - | - | ✅ | +| `?int` | `*int64` | ✅ | - | - | ✅ | +| `float` | `float64` | ✅ | - | - | ✅ | +| `?float` | `*float64` | ✅ | - | - | ✅ | +| `bool` | `bool` | ✅ | - | - | ✅ | +| `?bool` | `*bool` | ✅ | - | - | ✅ | +| `?bool` | `*bool` | ✅ | - | - | ✅ | +| `string`/`?string` | `*C.zend_string` | ❌ | `frankenphp.GoString()` | `frankenphp.PHPString()` | ✅ | +| `array` | `frankenphp.AssociativeArray` | ❌ | `frankenphp.GoAssociativeArray()` | `frankenphp.PHPAssociativeArray()` | ✅ | +| `array` | `map[string]any` | ❌ | `frankenphp.GoMap()` | `frankenphp.PHPMap()` | ✅ | +| `array` | `[]any` | ❌ | `frankenphp.GoPackedArray()` | `frankenphp.PHPPackedArray()` | ✅ | +| `object` | `struct` | ❌ | _Ainda não implementado_ | _Ainda não implementado_ | ❌ | + +> [!NOTE] +> Esta tabela ainda não é exaustiva e será completada à medida que a API de +> tipos do FrankenPHP se tornar mais completa. +> +> Tipos primitivos e arrays são suportados atualmente, especificamente para +> métodos de classe. +> Objetos ainda não podem ser usados como parâmetros de métodos ou tipos de +> retorno. + +Se você consultar o trecho de código da seção anterior, poderá ver que os +auxiliares são usados para converter o primeiro parâmetro e o valor de retorno. +O segundo e o terceiro parâmetros da nossa função `repeat_this()` não precisam +ser convertidos, pois a representação em memória dos tipos subjacentes é a mesma +para C e Go. + +#### Trabalhando com arrays + +O FrankenPHP oferece suporte nativo para arrays PHP por meio de +`frankenphp.AssociativeArray` ou conversão direta para um mapa ou slice. + +`AssociativeArray` representa um +[hashmap](https://en.wikipedia.org/wiki/Hash_table) composto por um campo +`Map: map[string]any` e um campo opcional `Order: []string` (ao contrário dos +arrays associativos do PHP, os mapas em Go não são ordenados). + +Se a ordem ou a associação não forem necessárias, também é possível converter +diretamente para um slice `[]any` ou um mapa não ordenado `map[string]any`. + +**Criando e manipulando arrays em Go:** + +```go +//export_php:function process_data_ordered(array $input): array +func process_data_ordered_map(arr *C.zval) unsafe.Pointer { + // Converte um array associativo PHP para Go, mantendo a ordem + associativeArray := frankenphp.GoAssociativeArray(unsafe.Pointer(arr)) + + // percorre as entradas em ordem + for _, key := range associativeArray.Order { + value, _ = associativeArray.Map[key] + // faz algo com a chave e o valor + } + + // retorna um array ordenado + // se 'Order' não estiver vazio, apenas os pares chave-valor em 'Order' + // serão respeitados + return frankenphp.PHPAssociativeArray(AssociativeArray{ + Map: map[string]any{ + "chave1": "valor1", + "chave2": "valor2", + }, + Order: []string{"chave1", "chave2"}, + }) +} + +//export_php:function process_data_unordered(array $input): array +func process_data_unordered_map(arr *C.zval) unsafe.Pointer { + // Converte um array associativo PHP em um mapa Go sem manter a ordem + // Ignorar a ordem terá melhor desempenho + goMap := frankenphp.GoMap(unsafe.Pointer(arr)) + + // percorre as entradas sem nenhuma ordem específica + for key, value := range goMap { + // faz algo com a chave e o valor + } + + // retorna um array não ordenado + return frankenphp.PHPMap(map[string]any{ + "chave1": "valor1", + "chave2": "valor2", + }) +} + +//export_php:function process_data_packed(array $input): array +func process_data_packed(arr *C.zval) unsafe.Pointer { + // Converte um array compactado PHP para Go + goSlice := frankenphp.GoPackedArray(unsafe.Pointer(arr), false) + + // percorre o slice em ordem + for index, value := range goSlice { + // faz algo com a chave e o valor + } + + // retorna um array compactado + return frankenphp.PHPackedArray([]any{"valor1", "valor2", "value3"}) +} +``` + +**Principais recursos da conversão de arrays:** + +- **Pares chave-valor ordenados** - Opção para manter a ordem do array + associativo; +- **Otimizado para múltiplos casos** - Opção para ignorar a ordem para melhor + desempenho ou converter diretamente para um slice; +- **Detecção automática de listas** - Ao converter para PHP, detecta + automaticamente se o array deve ser uma lista compactada ou um hashmap; +- **Arrays aninhados** - Os arrays podem ser aninhados e converterão todos os + tipos suportados automaticamente (`int64`, `float64`, `string`, `bool`, `nil`, + `AssociativeArray`, `map[string]any`, `[]any`); +- **Objetos não são suportados** - Atualmente, apenas tipos escalares e arrays + podem ser usados como valores. + Fornecer um objeto resultará em um valor `null` no array PHP. + +##### Métodos disponíveis: empacotado e associativo + +- `frankenphp.PHPAssociativeArray(arr frankenphp.AssociativeArray) unsafe.Pointer` + \- Converte para um array PHP ordenado com pares chave-valor; +- `frankenphp.PHPMap(arr map[string]any) unsafe.Pointer` - Converte um mapa em + um array PHP não ordenado com pares chave-valor; +- `frankenphp.PHPPackedArray(slice []any) unsafe.Pointer` - Converte um slice + em um array PHP compactado apenas com valores indexados; +- `frankenphp.GoAssociativeArray(arr unsafe.Pointer, ordered bool) frankenphp.AssociativeArray` + \- Converte um array PHP em um `AssociativeArray` Go ordenado (mapa com ordem); +- `frankenphp.GoMap(arr unsafe.Pointer) map[string]any` - Converte um array PHP + em um mapa Go não ordenado; +- `frankenphp.GoPackedArray(arr unsafe.Pointer) []any` - Converte um array PHP + em um slice Go. + +### Declarando uma classe PHP nativa + +O gerador suporta a declaração de **classes opacas** como estruturas Go, que +podem ser usadas para criar objetos PHP. +Você pode usar o comentário de diretiva `//export_php:class` para definir uma +classe PHP. +Por exemplo: + +```go +//export_php:class User +type UserStruct struct { + Name string + Age int +} +``` + +#### O que são classes opacas? + +**Classes Opacas** são classes cuja estrutura interna (propriedades) é ocultada +do código PHP. +Isso significa: + +- **Sem acesso direto às propriedades**: Você não pode ler ou escrever + propriedades diretamente do PHP (`$user->name` não funcionará); +- **Interface somente para métodos** - Todas as interações devem passar pelos + métodos que você definir; +- **Melhor encapsulamento** - A estrutura interna de dados é completamente + controlada pelo código Go; +- **Segurança de tipos** - Sem risco do código PHP corromper o estado interno + com tipos incorretos; +- **API mais limpa** - Força o design de uma interface pública adequada. + +Essa abordagem fornece melhor encapsulamento e evita que o código PHP corrompa +acidentalmente o estado interno dos seus objetos Go. +Todas as interações com o objeto devem passar pelos métodos que você definir +explicitamente. + +#### Adicionando métodos às classes + +Como as propriedades não são diretamente acessíveis, você **deve definir +métodos** para interagir com suas classes opacas. +Use a diretiva `//export_php:method` para definir o comportamento: + +```go +//export_php:class User +type UserStruct struct { + Name string + Age int +} + +//export_php:method User::getName(): string +func (us *UserStruct) GetUserName() unsafe.Pointer { + return frankenphp.PHPString(us.Name, false) +} + +//export_php:method User::setAge(int $age): void +func (us *UserStruct) SetUserAge(age int64) { + us.Age = int(age) +} + +//export_php:method User::getAge(): int +func (us *UserStruct) GetUserAge() int64 { + return int64(us.Age) +} + +//export_php:method User::setNamePrefix(string $prefix = "User"): void +func (us *UserStruct) SetNamePrefix(prefix *C.zend_string) { + us.Name = frankenphp.GoString(unsafe.Pointer(prefix)) + ": " + us.Name +} +``` + +#### Parâmetros anuláveis + +O gerador suporta parâmetros anuláveis usando o prefixo `?` em assinaturas PHP. +Quando um parâmetro é anulável, ele se torna um ponteiro na sua função Go, +permitindo que você verifique se o valor era `null` no PHP: + +```go +//export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void +func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) { + // Verifica se o parâmetro name foi fornecido (não nulo) + if name != nil { + us.Name = frankenphp.GoString(unsafe.Pointer(name)) + } + + // Verifica se o parâmetro age foi fornecido (não nulo) + if age != nil { + us.Age = int(*age) + } + + // Verifique se o parâmetro active foi fornecido (não nulo) + if active != nil { + us.Active = *active + } +} +``` + +**Pontos-chave sobre parâmetros anuláveis:** + +- **Tipos primitivos anuláveis** (`?int`, `?float`, `?bool`) tornam-se ponteiros + (`*int64`, `*float64`, `*bool`) em Go; +- **Strings anuláveis** (`?string`) permanecem como `*C.zend_string`, mas podem + ser `*nil`; +- **Verifique `nil`** antes de dereferenciar valores de ponteiro; +- **`null` do PHP torna-se `nil` do Go** - quando o PHP passa `null`, sua função + em Go recebe um ponteiro `nil`. + +> [!WARNING] +> Atualmente, os métodos de classe têm as seguintes limitações. +> **Objetos não são suportados** como tipos de parâmetros ou tipos de retorno. +> **Arrays são totalmente suportados** tanto para parâmetros quanto para tipos +> de retorno. +> Tipos suportados: `string`, `int`, `float`, `bool`, `array` e `void` (para +> tipo de retorno). +> **Tipos de parâmetros anuláveis são totalmente suportados** para todos os +> tipos escalares (`?string`, `?int`, `?float`, `?bool`). + +Após gerar a extensão, você poderá usar a classe e seus métodos no PHP. +Observe que você **não pode acessar propriedades diretamente**: + +```php +setAge(25); +echo $user->getName(); // Saída: (vazio, valor padrão) +echo $user->getAge(); // Saída: 25 +$user->setNamePrefix("Funcionária"); + +// ✅ Isso também funciona - parâmetros anuláveis +$user->updateInfo("João", 30, true); // Todos os parâmetros fornecidos +$user->updateInfo("Joana", null, false); // Age é nulo +$user->updateInfo(null, 25, null); // Name e active são nulos + +// ❌ Isso NÃO funcionará - acesso direto à propriedade +// echo $user->name; // Error: Cannot access private property +// $user->age = 30; // Error: Cannot access private property +``` + +Este design garante que seu código Go tenha controle total sobre como o estado +do objeto é acessado e modificado, proporcionando melhor encapsulamento e +segurança de tipos. + +### Declarando constantes + +O gerador suporta a exportação de constantes Go para PHP usando duas diretivas: +`//export_php:const` para constantes globais e `//export_php:classconstant` para +constantes de classe. +Isso permite que você compartilhe valores de configuração, códigos de status e +outras constantes entre código Go e PHP. + +#### Constantes globais + +Use a diretiva `//export_php:const` para criar constantes PHP globais: + +```go +//export_php:const +const MAX_CONNECTIONS = 100 + +//export_php:const +const API_VERSION = "1.2.3" + +//export_php:const +const STATUS_OK = iota + +//export_php:const +const STATUS_ERROR = iota +``` + +#### Constantes de classe + +Use a diretiva `//export_php:classconstant ClassName` para criar constantes que +pertencem a uma classe PHP específica: + +```go +//export_php:classconstant User +const STATUS_ACTIVE = 1 + +//export_php:classconstant User +const STATUS_INACTIVE = 0 + +//export_php:classconstant User +const ROLE_ADMIN = "admin" + +//export_php:classconstant Order +const STATE_PENDING = iota + +//export_php:classconstant Order +const STATE_PROCESSING = iota + +//export_php:classconstant Order +const STATE_COMPLETED = iota +``` + +Constantes de classe são acessíveis usando o escopo do nome da classe no PHP: + +```php +getName(); // "João Ninguém" + +echo My\Extension\STATUS_ACTIVE; // 1 +``` + +#### Notas importantes + +- Apenas **uma** diretiva de namespace é permitida por arquivo. + Se várias diretivas de namespace forem encontradas, o gerador retornará um + erro; +- O namespace se aplica a **todos** os símbolos exportados no arquivo: funções, + classes, métodos e constantes; +- Os nomes de namespace seguem as convenções de namespace do PHP, usando barras + invertidas (`\`) como separadores; +- Se nenhum namespace for declarado, os símbolos serão exportados para o + namespace global como de costume. + +### Gerando a extensão + +É aqui que a mágica acontece e sua extensão agora pode ser gerada. +Você pode executar o gerador com o seguinte comando: + +```console +GEN_STUB_FILE=php-src/build/gen_stub.php frankenphp extension-init my_extension.go +``` + +> [!NOTE] +> Não se esqueça de definir a variável de ambiente `GEN_STUB_FILE` para o +> caminho do arquivo `gen_stub.php` no código-fonte PHP que você baixou +> anteriormente. +> Este é o mesmo script `gen_stub.php` mencionado na seção de implementação +> manual. + +Se tudo correu bem, um novo diretório chamado `build` deve ter sido criado. +Este diretório contém os arquivos gerados para sua extensão, incluindo o arquivo +`my_extension.go` com os stubs de funções PHP gerados. + +### Integrando a extensão gerada ao FrankenPHP + +Nossa extensão agora está pronta para ser compilada e integrada ao FrankenPHP. +Para fazer isso, consulte a [documentação de compilação](compile.md) do +FrankenPHP para aprender como compilar o FrankenPHP. +Adicione o módulo usando a flag `--with`, apontando para o caminho do seu +módulo: + +```console +CGO_ENABLED=1 \ +XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \ +CGO_CFLAGS=$(php-config --includes) \ +CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ +xcaddy build \ + --output frankenphp \ + --with github.com///build +``` + +Observe que você aponta para o subdiretório `/build` que foi criado durante a +etapa de geração. +Entretanto, isso não é obrigatório: você também pode copiar os arquivos gerados +para o diretório do seu módulo e apontar diretamente para ele. + +### Testando sua extensão gerada + +Você pode criar um arquivo PHP para testar as funções e classes que criou. +Por exemplo, crie um arquivo `index.php` com o seguinte conteúdo: + +```php +process('Olá mundo', StringProcessor::MODE_LOWERCASE); // "olá mundo" +echo $processor->process('Olá mundo', StringProcessor::MODE_UPPERCASE); // "OLÁ MUNDO" +``` + +Depois de integrar sua extensão ao FrankenPHP, como demonstrado na seção +anterior, você pode executar este arquivo de teste usando +`./frankenphp php-server` e deverá ver sua extensão funcionando. + +## Implementação manual + +Se você quiser entender como as extensões funcionam ou precisar de controle +total sobre elas, pode escrevê-las manualmente. +Essa abordagem oferece controle total, mas requer mais código boilerplate. + +### Função básica + +Veremos como escrever uma extensão PHP simples em Go que define uma nova função +nativa. +Essa função será chamada do PHP e disparará uma goroutine que registra uma +mensagem nos logs do Caddy. +Essa função não recebe parâmetros e não retorna nada. + +#### Definindo a função Go + +No seu módulo, você precisa definir uma nova função nativa que será chamada do +PHP. +Para fazer isso, crie um arquivo com o nome desejado, por exemplo, +`extension.go`, e adicione o seguinte código: + +```go +package ext_go + +//#include "extension.h" +import "C" +import ( + "unsafe" + "github.com/caddyserver/caddy/v2" + "github.com/dunglas/frankenphp" +) + +func init() { + frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry)) +} + +//export go_print_something +func go_print_something() { + go func() { + caddy.Log().Info("Olá de uma goroutine!") + }() +} +``` + +A função `frankenphp.RegisterExtension()` simplifica o processo de registro de +extensões, manipulando a lógica interna de registro do PHP. +A função `go_print_something` usa a diretiva `//export` para indicar que estará +acessível no código C que escreveremos, graças ao CGO. + +Neste exemplo, nossa nova função disparará uma goroutine que registra uma +mensagem nos logs do Caddy. + +#### Definindo a função PHP + +Para permitir que o PHP chame nossa função, precisamos definir uma função PHP +correspondente. +Para isso, criaremos um arquivo stub, por exemplo, `extension.stub.php`, que +conterá o seguinte código: + +```php + + +extern zend_module_entry ext_module_entry; + +#endif +``` + +Em seguida, crie um arquivo chamado `extension.c` que executará as seguintes +etapas: + +- Incluir cabeçalhos PHP; +- Declarar nossa nova função nativa PHP `go_print()`; +- Declarar os metadados da extensão. + +Vamos começar incluindo os cabeçalhos necessários: + +```c +#include +#include "extension.h" +#include "extension_arginfo.h" + +// Contém símbolos exportados pelo Go +#include "_cgo_export.h" +``` + +Em seguida, definimos nossa função PHP como uma função nativa da linguagem: + +```c +PHP_FUNCTION(go_print) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + go_print_something(); +} + +zend_module_entry ext_module_entry = { + STANDARD_MODULE_HEADER, + "ext_go", + ext_functions, /* Funções */ + NULL, /* MINIT */ + NULL, /* MSHUTDOWN */ + NULL, /* RINIT */ + NULL, /* RSHUTDOWN */ + NULL, /* MINFO */ + "0.1.1", + STANDARD_MODULE_PROPERTIES +}; +``` + +Neste caso, nossa função não recebe parâmetros e não retorna nada. +Ela simplesmente chama a função Go que definimos anteriormente, exportada usando +a diretiva `//export`. + +Finalmente, definimos os metadados da extensão em uma estrutura +`zend_module_entry`, como seu nome, versão e propriedades. +Essas informações são necessárias para que o PHP reconheça e carregue nossa +extensão. +Observe que `ext_functions` é um array de ponteiros para as funções PHP que +definimos e foi gerado automaticamente pelo script `gen_stub.php` no arquivo +`extension_arginfo.h`. + +O registro da extensão é tratado automaticamente pela função +`RegisterExtension()` do FrankenPHP, que chamamos em nosso código Go. + +### Uso avançado + +Agora que sabemos como criar uma extensão PHP básica em Go, vamos tornar nosso +exemplo mais complexo. +Agora, criaremos uma função PHP que recebe uma string como parâmetro e retorna +sua versão em letras maiúsculas. + +#### Definindo o stub da função PHP + +Para definir a nova função PHP, modificaremos nosso arquivo `extension.stub.php` +para incluir a nova assinatura da função: + +```php + [!TIP] +> Não negligencie a documentação das suas funções! +> Você provavelmente compartilhará os stubs da sua extensão com outras +> pessoas desenvolvedoras para documentar como usar a sua extensão e quais +> recursos estão disponíveis. + +Ao gerar novamente o arquivo stub com o script `gen_stub.php`, o arquivo +`extension_arginfo.h` deverá ficar assim: + +```c +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_go_upper, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_FUNCTION(go_upper); + +static const zend_function_entry ext_functions[] = { + ZEND_FE(go_upper, arginfo_go_upper) + ZEND_FE_END +}; +``` + +Podemos ver que a função `go_upper` é definida com um parâmetro do tipo `string` +e um tipo de retorno `string`. + +#### Malabarismo de tipos entre Go e PHP/C + +Sua função Go não pode aceitar diretamente uma string PHP como parâmetro. +Você precisa convertê-la para uma string Go. +Felizmente, o FrankenPHP fornece funções auxiliares para lidar com a conversão +entre strings PHP e strings Go, semelhante ao que vimos na abordagem do gerador. + +O arquivo de cabeçalho permanece simples: + +```c +#ifndef _EXTENSION_H +#define _EXTENSION_H + +#include + +extern zend_module_entry ext_module_entry; + +#endif +``` + +Agora podemos escrever a ponte entre Go e C no nosso arquivo `extension.c`. +Passaremos a string PHP diretamente para a nossa função Go: + +```c +PHP_FUNCTION(go_upper) +{ + zend_string *str; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + zend_string *result = go_upper(str); + RETVAL_STR(result); +} +``` + +Você pode aprender mais sobre `ZEND_PARSE_PARAMETERS_START` e análise de +parâmetros na página dedicada do +[PHP Internals Book](https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html#parsing-parameters-zend-parse-parameters). +Aqui, informamos ao PHP que nossa função recebe um parâmetro obrigatório do tipo +`string` como uma `zend_string`. +Em seguida, passamos essa string diretamente para nossa função Go e retornamos o +resultado usando `RETVAL_STR`. + +Só resta uma coisa a fazer: implementar a função `go_upper` em Go. + +#### Implementando a função Go + +Nossa função Go receberá uma `*C.zend_string` como parâmetro, a converterá em +uma string Go usando a função auxiliar do FrankenPHP, a processará e retornará o +resultado como uma nova `*C.zend_string`. +As funções auxiliares cuidam de todo o gerenciamento de memória e da +complexidade da conversão para nós. + +```go +import "strings" + +//export go_upper +func go_upper(s *C.zend_string) *C.zend_string { + str := frankenphp.GoString(unsafe.Pointer(s)) + + upper := strings.ToUpper(str) + + return (*C.zend_string)(frankenphp.PHPString(upper, false)) +} +``` + +Essa abordagem é muito mais limpa e segura do que o gerenciamento manual de +memória. +As funções auxiliares do FrankenPHP lidam automaticamente com a conversão entre +o formato `zend_string` do PHP e strings em Go. +O parâmetro `false` em `PHPString()` indica que queremos criar uma nova string +não persistente (liberada ao final da requisição). + +> [!TIP] +> Neste exemplo, não realizamos nenhum tratamento de erro, mas você deve sempre +> verificar se os ponteiros não são `nil` e se os dados são válidos antes de +> usá-los em suas funções em Go. + +### Integrando a extensão ao FrankenPHP + +Nossa extensão agora está pronta para ser compilada e integrada ao FrankenPHP. +Para isso, consulte a [documentação de compilação](compile.md) do FrankenPHP +para aprender como compilar o FrankenPHP. +Adicione o módulo usando a flag `--with`, apontando para o caminho do seu +módulo: + +```console +CGO_ENABLED=1 \ +XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \ +CGO_CFLAGS=$(php-config --includes) \ +CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ +xcaddy build \ + --output frankenphp \ + --with github.com// +``` + +Pronto! +Sua extensão agora está integrada ao FrankenPHP e pode ser usada no seu código +PHP. + +### Testando sua extensão + +Após integrar sua extensão ao FrankenPHP, você pode criar um arquivo `index.php` +com exemplos para as funções que você implementou: + +```php + [!CAUTION] > -> O Docker pode ter uma camada de cache; certifique-se de ter a compilação +> O Docker pode ter uma camada de cache; certifique-se de ter a construção > correta para cada implantação ou reconstrua seu projeto com a opção > `--no-cache` para evitar problemas de cache. diff --git a/docs/pt-br/static.md b/docs/pt-br/static.md index 05613397..3836099f 100644 --- a/docs/pt-br/static.md +++ b/docs/pt-br/static.md @@ -132,7 +132,7 @@ Execute o seguinte script para criar um binário estático para macOS (você precisa ter o [Homebrew](https://brew.sh/) instalado): ```console -git clone https://github.com/dunglas/frankenphp +git clone https://github.com/php/frankenphp cd frankenphp ./build-static.sh ``` @@ -145,34 +145,34 @@ sistemas Unix) e é usado internamente pelas imagens Docker que fornecemos. As seguintes variáveis de ambiente podem ser passadas para `docker build` e para o script `build-static.sh` para personalizar a compilação estática: -- `FRANKENPHP_VERSION`: a versão do FrankenPHP a ser usada. -- `PHP_VERSION`: a versão do PHP a ser usada. +- `FRANKENPHP_VERSION`: a versão do FrankenPHP a ser usada; +- `PHP_VERSION`: a versão do PHP a ser usada; - `PHP_EXTENSIONS`: as extensões PHP a serem compiladas - ([lista de extensões suportadas](https://static-php.dev/en/guide/extensions.html)). + ([lista de extensões suportadas](https://static-php.dev/en/guide/extensions.html)); - `PHP_EXTENSION_LIBS`: bibliotecas extras a serem compiladas que adicionam - recursos às extensões. + recursos às extensões; - `XCADDY_ARGS`: argumentos a passar para o [`xcaddy`](https://github.com/caddyserver/xcaddy), por exemplo, para adicionar - módulos Caddy extras. -- `EMBED`: caminho da aplicação PHP a ser incorporada no binário. + módulos Caddy extras; +- `EMBED`: caminho da aplicação PHP a ser incorporada no binário; - `CLEAN`: quando definida, a `libphp` e todas as suas dependências são - compiladas do zero (sem cache). -- `NO_COMPRESS`: não compacta o binário resultante usando UPX. + compiladas do zero (sem cache); +- `NO_COMPRESS`: não compacta o binário resultante usando UPX; - `DEBUG_SYMBOLS`: quando definida, os símbolos de depuração não serão removidos - e serão adicionados ao binário. + e serão adicionados ao binário; - `MIMALLOC`: (experimental, somente Linux) substitui `mallocng` da `musl` por [`mimalloc`](https://github.com/microsoft/mimalloc) para melhor desempenho. Recomendamos usar isso apenas para compilações direcionadas à `musl`; para `glibc`, prefira desabilitar essa opção e usar [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html) ao - executar seu binário. + executar seu binário; - `RELEASE`: (somente pessoas mantenedoras) quando definida, o binário resultante será enviado para o GitHub. ## Extensões -Com os binários `glibc` ou baseados em macOS, você pode carregar extensões PHP -dinamicamente. +Com os binários baseados na `glibc` ou no macOS, você pode carregar extensões +PHP dinamicamente. No entanto, essas extensões precisarão ser compiladas com suporte a ZTS. Como a maioria dos gerenciadores de pacotes não oferece atualmente versões ZTS de suas extensões, você terá que compilá-las você mesmo. diff --git a/docs/pt-br/worker.md b/docs/pt-br/worker.md index a8867d06..a835be5f 100644 --- a/docs/pt-br/worker.md +++ b/docs/pt-br/worker.md @@ -8,11 +8,11 @@ O FrankenPHP processará as requisições recebidas em poucos milissegundos. ### Docker Defina o valor da variável de ambiente `FRANKENPHP_CONFIG` como -`worker /caminho/para/seu/script/worker.php`: +`worker /caminho/para/seu/worker/script.php`: ```console docker run \ - -e FRANKENPHP_CONFIG="worker /app/caminho/para/seu/script/worker.php" \ + -e FRANKENPHP_CONFIG="worker /app/caminho/para/seu/worker/script.php" \ -v $PWD:/app \ -p 80:80 -p 443:443 -p 443:443/udp \ dunglas/frankenphp @@ -24,7 +24,7 @@ Use a opção `--worker` do comando `php-server` para servir o conteúdo do diretório atual usando um worker: ```console -frankenphp php-server --worker /caminho/para/seu/script/worker.php +frankenphp php-server --worker /caminho/para/seu/worker/script.php ``` Se a sua aplicação PHP estiver [embutida no binário](embed.md), você pode @@ -32,30 +32,29 @@ adicionar um `Caddyfile` personalizado no diretório raiz da aplicação. Ele será usado automaticamente. Também é possível -[reiniciar o worker em caso de alterações no arquivo](config.md#monitorando-alteracoes-em-arquivos) +[reiniciar o worker em caso de alterações em arquivos](config.md#monitorando-alteracoes-em-arquivos) com a opção `--watch`. O comando a seguir acionará uma reinicialização se qualquer arquivo terminado em `.php` no diretório `/caminho/para/sua/aplicacao/` ou subdiretórios for modificado: ```console -frankenphp php-server --worker /caminho/para/seu/script/worker.php --watch="/caminho/para/sua/aplicacao/**/*.php" +frankenphp php-server --worker /caminho/para/seu/worker/script.php --watch="/caminho/para/sua/aplicacao/**/*.php" ``` -## Tempo de execução do Symfony +## Symfony Runtime O modo worker do FrankenPHP é suportado pelo [Componente Symfony Runtime](https://symfony.com/doc/current/components/runtime.html). Para iniciar qualquer aplicação Symfony em um worker, instale o pacote -FrankenPHP do -[tempo de execução do PHP](https://github.com/php-runtime/runtime): +FrankenPHP do [PHP Runtime](https://github.com/php-runtime/runtime): ```console composer require runtime/frankenphp-symfony ``` Inicie seu servidor de aplicações definindo a variável de ambiente `APP_RUNTIME` -para usar o tempo de execução Symfony do FrankenPHP: +para usar o Symfony Runtime do FrankenPHP: ```console docker run \ @@ -83,7 +82,7 @@ uma biblioteca de terceiros: // interrompida ignore_user_abort(true); -// Inicializa sua aplicação +// Inicializa a aplicação require __DIR__.'/vendor/autoload.php'; $myApp = new \App\Kernel(); @@ -125,7 +124,7 @@ docker run \ dunglas/frankenphp ``` -Por padrão, 2 workers por CPU são iniciados. +Por padrão, são iniciados 2 workers por CPU. Você também pode configurar o número de workers a serem iniciados: ```console @@ -151,7 +150,7 @@ requisições a serem processadas, definindo uma variável de ambiente chamada Embora seja possível reiniciar os workers [em alterações de arquivo](config.md#monitorando-alteracoes-em-arquivos), também -é possível reiniciar todos os workers normalmente por meio da +é possível reiniciar todos os workers graciosamente por meio da [API de administração do Caddy](https://caddyserver.com/docs/api). Se o administrador estiver habilitado no seu [Caddyfile](config.md#configuracao-do-caddyfile), você pode executar ping no @@ -166,12 +165,24 @@ curl -X POST http://localhost:2019/frankenphp/workers/restart Se um worker script travar com um código de saída diferente de zero, o FrankenPHP o reiniciará com uma estratégia de backoff exponencial. Se o worker script permanecer ativo por mais tempo do que o último backoff \* 2, -ele não penalizará o worker script e o reiniciará novamente. +ele não irá penalizar o worker script e reiniciá-lo novamente. No entanto, se o worker script continuar a falhar com um código de saída diferente de zero em um curto período de tempo (por exemplo, com um erro de digitação em um script), o FrankenPHP travará com o erro: `too many consecutive failures` (muitas falhas consecutivas). +O número de falhas consecutivas pode ser configurado no seu +[Caddyfile](config.md#caddyfile-config) com a opção `max_consecutive_failures`: + +```caddyfile +frankenphp { + worker { + # ... + max_consecutive_failures 10 + } +} +``` + ## Comportamento das superglobais As diff --git a/docs/pt-br/x-sendfile.md b/docs/pt-br/x-sendfile.md index cb6405d3..6ad94043 100644 --- a/docs/pt-br/x-sendfile.md +++ b/docs/pt-br/x-sendfile.md @@ -9,10 +9,10 @@ comparação com o uso direto do servidor web (sobrecarga de memória, desempenh reduzido...). O FrankenPHP permite delegar o envio de arquivos estáticos ao servidor web -**após** a execução de código PHP personalizado. +**após** a execução do código PHP personalizado. -Para fazer isso, sua aplicação PHP precisa simplesmente definir um cabeçalho -HTTP personalizado contendo o caminho do arquivo a ser servido. +Para fazer isso, sua aplicação PHP só precisa definir um cabeçalho HTTP +personalizado contendo o caminho do arquivo a ser servido. O FrankenPHP cuida do resto. Esse recurso é conhecido como **`X-Sendfile`** para Apache e