feat: add Brazilian Portuguese translation (#1645)

* feat: add Brazilian Portuguese

* Translate file README.md

* Update file README.md

* Translate file docs/classic.md

* Translate file docs/worker.md

* Translate file docs/early-hints.md

* Translate file docs/mercure.md

* Translate file docs/x-sendfile.md

* Translate file docs/config.md

* Translate file docs/docker.md

* Minor fixes

* Translate file docs/production.md

* Translate file CONTRIBUTING.md

* Minor fixes

* Translate file docs/performance.md

* Minor fixes

* Translate file docs/embed.md

* Minor fixes

* Minor fixes

* Translate file docs/static.md

* Translate file docs/compile.md

* Minor fixes

* Translate file docs/metrics.md

* Translate file docs/laravel.md

* Translate file docs/known-issues.md

* Minor fixes

* Translate file docs/github-actions.md

* Fix build

* Fix build

* fix: remove text already translated

* feat: update translation

* fix: format comments based on other translations
This commit is contained in:
Adiel Cristo
2025-08-25 11:13:04 -03:00
committed by GitHub
parent 34005da9f8
commit 1eb16f1434
18 changed files with 2645 additions and 0 deletions

238
docs/pt-br/CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,238 @@
# Contribuindo
## Compilando o PHP
### Com Docker (Linux)
Crie a imagem Docker de desenvolvimento:
```console
docker build -t frankenphp-dev -f dev.Dockerfile .
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
```
A imagem contém as ferramentas de desenvolvimento usuais (Go, GDB, Valgrind,
Neovim...) e usa os seguintes locais de configuração do PHP:
- php.ini: `/etc/frankenphp/php.ini`.
Um arquivo `php.ini` com configurações de desenvolvimento é fornecido por
padrão.
- Arquivos de configuração adicionais: `/etc/frankenphp/php.d/*.ini`.
- Extensões PHP: `/usr/lib/frankenphp/modules/`.
Se a sua versão do Docker for anterior à 23.0, a compilação falhará devido ao
[problema de padrão do `.dockerignore`](https://github.com/moby/moby/pull/42676).
Adicione diretórios ao `.dockerignore`.
```patch
!testdata/*.php
!testdata/*.txt
+!caddy
+!internal
```
### 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`.
## Executando a suite de testes
```console
go test -tags watcher -race -v ./...
```
## Módulo Caddy
Construa o Caddy com o módulo Caddy FrankenPHP:
```console
cd caddy/frankenphp/
go build -tags watcher,brotli,nobadger,nomysql,nopgx
cd ../../
```
Execute o Caddy com o módulo Caddy FrankenPHP:
```console
cd testdata/
../caddy/frankenphp/frankenphp run
```
O servidor está escutando em `127.0.0.1:80`:
> [!NOTE]
> Se você estiver usando o Docker, terá que vincular a porta 80 do contêiner ou
> executar de dentro do contêiner.
```console
curl -vk http://127.0.0.1/phpinfo.php
```
## Servidor de teste mínimo
Construa o servidor de teste mínimo:
```console
cd internal/testserver/
go build
cd ../../
```
Execute o servidor de teste:
```console
cd testdata/
../internal/testserver/testserver
```
O servidor está escutando em `127.0.0.1:8080`:
```console
curl -v http://127.0.0.1:8080/phpinfo.php
```
## Construindo imagens Docker localmente
Imprima o plano do bake:
```console
docker buildx bake -f docker-bake.hcl --print
```
Construa imagens FrankenPHP para amd64 localmente:
```console
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/amd64"
```
Construa imagens FrankenPHP para arm64 localmente:
```console
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/arm64"
```
Construa imagens FrankenPHP do zero para arm64 e amd64 e envie para o Docker
Hub:
```console
docker buildx bake -f docker-bake.hcl --pull --no-cache --push
```
## Depurando falhas de segmentação com compilações estáticas
1. Baixe a versão de depuração do binário do FrankenPHP do GitHub ou crie sua
própria compilação estática personalizada, incluindo símbolos de depuração:
```console
docker buildx bake \
--load \
--set static-builder.args.DEBUG_SYMBOLS=1 \
--set "static-builder.platform=linux/amd64" \
static-builder
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
```
2. Substitua sua versão atual do `frankenphp` pelo executável de depuração do
FrankenPHP.
3. Inicie o FrankenPHP normalmente (alternativamente, você pode iniciar o
FrankenPHP diretamente com o GDB: `gdb --args frankenphp run`).
4. Anexe ao processo com o GDB:
```console
gdb -p `pidof frankenphp`
```
5. Se necessário, digite `continue` no shell do GDB.
6. Faça o FrankenPHP travar.
7. Digite `bt` no shell do GDB.
8. Copie a saída.
## Depurando falhas de segmentação no GitHub Actions
1. Abra o arquivo `.github/workflows/tests.yml`.
2. Habilite os símbolos de depuração do PHP:
```patch
- uses: shivammathur/setup-php@v2
# ...
env:
phpts: ts
+ debug: true
```
3. Habilite o `tmate` para se conectar ao contêiner:
```patch
- name: Set CGO flags
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
+ - run: |
+ sudo apt install gdb
+ mkdir -p /home/runner/.config/gdb/
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
+ - uses: mxschmitt/action-tmate@v3
```
4. Conecte-se ao contêiner.
5. Abra o `frankenphp.go`.
6. Habilite o `cgosymbolizer`:
```patch
- //_ "github.com/ianlancetaylor/cgosymbolizer"
+ _ "github.com/ianlancetaylor/cgosymbolizer"
```
7. Baixe o módulo: `go get`.
8. No contêiner, você pode usar o GDB e similares:
```console
go test -tags watcher -c -ldflags=-w
gdb --args frankenphp.test -test.run ^MyTest$
```
9. Quando a falha for corrigida, reverta todas essas alterações.
## Recursos diversos de desenvolvimento
- [PHP embedding in uWSGI](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
- [PHP embedding in NGINX Unit](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
- [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)
- [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)
## Recursos relacionados ao Docker
- [Definição do arquivo Bake](https://docs.docker.com/build/customize/bake/file-definition/)
- [`docker buildx build`](https://docs.docker.com/engine/reference/commandline/buildx_build/)
## Comando útil
```console
apk add strace util-linux gdb
strace -e 'trace=!futex,epoll_ctl,epoll_pwait,tgkill,rt_sigreturn' -p 1
```
## Traduzindo a documentação
Para traduzir a documentação e o site para um novo idioma, siga estes passos:
1. Crie um novo diretório com o código ISO de 2 caracteres do idioma no
diretório `docs/` deste repositório.
2. Copie todos os arquivos `.md` da raiz do diretório `docs/` para o novo
diretório (sempre use a versão em inglês como fonte para tradução, pois está
sempre atualizada).
3. Copie os arquivos `README.md` e `CONTRIBUTING.md` do diretório raiz para o
novo diretório.
4. Traduza o conteúdo dos arquivos, mas não altere os nomes dos arquivos, nem
traduza strings que comecem com `> [!` (é uma marcação especial para o
GitHub).
5. Crie um pull request com as traduções.
6. No
[repositório do site](https://github.com/dunglas/frankenphp-website/tree/main),
copie e traduza os arquivos de tradução nos diretórios `content/`, `data/` e
`i18n/`.
7. Traduza os valores no arquivo YAML criado.
8. Abra um pull request no repositório do site.

125
docs/pt-br/README.md Normal file
View File

@@ -0,0 +1,125 @@
# FrankenPHP: Servidor de aplicações moderno para PHP
<h1 align="center"><a href="https://frankenphp.dev/pt-br"><img src="frankenphp.png" alt="FrankenPHP" width="600"></a></h1>
O FrankenPHP é um servidor de aplicações moderno 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 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
modo worker.
O FrankenPHP também pode ser usado como uma biblioteca Go independente para
incorporar PHP em qualquer aplicação usando `net/http`.
[**Saiba mais** em _frankenphp.dev_](https://frankenphp.dev/pt-br) e neste
conjunto de slides:
<a href="https://dunglas.dev/2022/10/frankenphp-the-modern-php-app-server-written-in-go/"><img src="https://dunglas.dev/wp-content/uploads/2022/10/frankenphp.png" alt="Slides" width="600"></a>
## Começando
### Binário independente
Fornecemos binários estáticos do FrankenPHP para Linux e macOS contendo o
[PHP 8.4](https://www.php.net/releases/8.4/pt_BR.php) e as extensões PHP mais
populares.
No Windows, use [WSL](https://learn.microsoft.com/pt-br/windows/wsl/) para
executar o FrankenPHP.
[Baixe o FrankenPHP](https://github.com/php/frankenphp/releases) ou copie esta
linha no seu terminal para instalar automaticamente a versão apropriada para sua
plataforma:
```console
curl https://frankenphp.dev/install.sh | sh
mv frankenphp /usr/local/bin/
```
Para servir o conteúdo do diretório atual, execute:
```console
frankenphp php-server
```
Você também pode executar scripts de linha de comando com:
```console
frankenphp php-cli /caminho/para/seu/script.php
```
### Docker
Alternativamente, [imagens do Docker](docker.md) estão disponíveis:
```console
docker run -v .:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
Acesse `https://localhost` e divirta-se!
> [!TIP]
>
> Não tente usar `https://127.0.0.1`.
> Use `https://localhost` e aceite o certificado autoassinado.
> Use a
> [variável de ambiente `SERVER_NAME`](config.md#variaveis-de-ambiente)
> para alterar o domínio a ser usado.
### Homebrew
O FrankenPHP também está disponível como um pacote [Homebrew](https://brew.sh)
para macOS e Linux.
Para instalá-lo:
```console
brew install dunglas/frankenphp/frankenphp
```
Para servir o conteúdo do diretório atual, execute:
```console
frankenphp php-server
```
## Documentação
- [Modo clássico](classic.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)
- [Imagens Docker](docker.md)
- [Implantação em produção](production.md)
- [Otimização de desempenho](performance.md)
- [Crie aplicações PHP **independentes** e autoexecutáveis](embed.md)
- [Crie binários estáticos](static.md)
- [Compile a partir do código-fonte](compile.md)
- [Monitorando o FrankenPHP](metrics.md)
- [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)
- [Contribuindo e depurando](CONTRIBUTING.md)
## Exemplos e esqueletos
- [Symfony](https://github.com/dunglas/symfony-docker)
- [API Platform](https://api-platform.com/docs/symfony)
- [Laravel](laravel.md)
- [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
- [WordPress](https://github.com/StephenMiracle/frankenwp)
- [Drupal](https://github.com/dunglas/frankenphp-drupal)
- [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
- [TYPO3](https://github.com/ochorocho/franken-typo3)
- [Magento2](https://github.com/ekino/frankenphp-magento2)

28
docs/pt-br/classic.md Normal file
View File

@@ -0,0 +1,28 @@
# Usando o modo clássico
Sem nenhuma configuração adicional, o FrankenPHP opera no modo clássico.
Neste modo, o FrankenPHP funciona como um servidor PHP tradicional, servindo
diretamente arquivos PHP.
Isso o torna um substituto perfeito para PHP-FPM ou Apache com mod_php.
Semelhante ao Caddy, o FrankenPHP aceita um número ilimitado de conexões e usa
um [número fixo de threads](config.md#configuracao-do-caddyfile) para servi-las.
O número de conexões aceitas e enfileiradas é limitado apenas pelos recursos
disponíveis no sistema.
O pool de threads do PHP opera com um número fixo de threads inicializadas na
inicialização, comparável ao modo estático do PHP-FPM.
Também é possível permitir que as threads
[escalem automaticamente em tempo de execução](performance.md#max_threads),
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.
Além disso, você pode definir um
[tempo limite de escrita razoável no Caddy](https://caddyserver.com/docs/caddyfile/options#timeouts).
Cada instância do Caddy ativará apenas um pool de threads do FrankenPHP, que
será compartilhado entre todos os blocos `php_server`.

147
docs/pt-br/compile.md Normal file
View File

@@ -0,0 +1,147 @@
# Compilar a partir dos fontes
Este documento explica como criar um binário FrankenPHP que carregará o PHP como
uma biblioteca dinâmica.
Este é o método recomendado.
Como alternativa, [compilações totalmente e principalmente estáticas](static.md)
também podem ser criadas.
## Instalar o PHP
O FrankenPHP é compatível com PHP 8.2 e versões superiores.
### Com o Homebrew (Linux e Mac)
A maneira mais fácil de instalar uma versão da `libphp` compatível com o
FrankenPHP é usar os pacotes ZTS fornecidos pelo
[Homebrew PHP](https://github.com/shivammathur/homebrew-php).
Primeiro, se ainda não o fez, instale o [Homebrew](https://brew.sh).
Em seguida, instale a variante ZTS do PHP, o Brotli (opcional, para suporte à
compressão) e o watcher (opcional, para detecção de alterações em arquivos):
```console
brew install shivammathur/php/php-zts brotli watcher
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
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
extraia-os:
```console
tar xf php-*
cd php-*/
```
Em seguida, execute o script `configure` com as opções necessárias para sua
plataforma.
As seguintes flags `./configure` são obrigatórias, mas você pode adicionar
outras, por exemplo, para compilar extensões ou recursos adicionais.
#### Linux
```console
./configure \
--enable-embed \
--enable-zts \
--disable-zend-signals \
--enable-zend-max-execution-timers
```
#### Mac
Use o gerenciador de pacotes [Homebrew](https://brew.sh/) para instalar as
dependências necessárias e opcionais:
```console
brew install libiconv bison brotli re2c pkg-config watcher
echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc
```
Em seguida, execute o script `configure`:
```console
./configure \
--enable-embed \
--enable-zts \
--disable-zend-signals \
--with-iconv=/opt/homebrew/opt/libiconv/
```
#### Compilar o PHP
Finalmente, compile e instale o PHP:
```console
make -j"$(getconf _NPROCESSORS_ONLN)"
sudo make install
```
## Instalar dependências opcionais
Alguns recursos do FrankenPHP dependem de dependências opcionais do sistema que
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` |
## Compilando a aplicação Go
Agora você pode compilar o binário final.
### Usando o `xcaddy`
A maneira recomendada é usar o [`xcaddy`](https://github.com/caddyserver/xcaddy)
para compilar o FrankenPHP.
O `xcaddy` também permite adicionar facilmente
[módulos Caddy personalizados](https://caddyserver.com/docs/modules/) e
extensões FrankenPHP:
```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/dunglas/frankenphp/caddy \
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy
# Adicione módulos Caddy e extensões FrankenPHP extras aqui
```
> [!TIP]
>
> Se você estiver usando a `libc` `musl` (o padrão no Alpine Linux) e Symfony,
> pode ser necessário aumentar o tamanho da pilha padrão.
> Caso contrário, você poderá receber erros como `PHP Fatal error: Maximum call
> stack size of 83360 bytes reached during compilation.
> Try splitting expression`.
>
> Para fazer isso, altere a variável de ambiente `XCADDY_GO_BUILD_FLAGS` para
> algo como
> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`
> (altere o valor do tamanho da pilha de acordo com as necessidades da sua
> aplicação).
### Sem o `xcaddy`
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
cd frankenphp-main/caddy/frankenphp
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build -tags=nobadger,nomysql,nopgx
```

320
docs/pt-br/config.md Normal file
View File

@@ -0,0 +1,320 @@
# Configuração
FrankenPHP, Caddy, bem como os módulos Mercure e Vulcain, podem ser configurados
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
`/etc/frankenphp/Caddyfile`.
O binário estático também procurará pelo `Caddyfile` no diretório onde o comando
`frankenphp run` é executado.
Você pode especificar um caminho personalizado com a opção `-c` ou `--config`.
O próprio PHP pode ser configurado
[usando um arquivo `php.ini`](https://www.php.net/manual/pt_BR/configuration.file.php).
Dependendo do seu método de instalação, o interpretador PHP procurará por
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-<YYYYMMDD>/`
- Você deve copiar um template oficial fornecido pelo projeto PHP:
```dockerfile
FROM dunglas/frankenphp
# Produção:
RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
# Ou desenvolvimento:
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/`
## 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/).
## Configuração do Caddyfile
As [diretivas HTTP](https://caddyserver.com/docs/caddyfile/concepts#directives)
`php_server` ou `php` podem ser usadas dentro dos blocos de site para servir sua
aplicação PHP.
Exemplo mínimo:
```caddyfile
localhost {
# Habilita compressão (opcional)
encode zstd br gzip
# Executa arquivos PHP no diretório atual e serve assets
php_server
}
```
Você também pode configurar explicitamente o FrankenPHP usando a opção global:
A [opção global](https://caddyserver.com/docs/caddyfile/concepts#global-options)
`frankenphp` pode ser usada para configurar o FrankenPHP.
```caddyfile
{
frankenphp {
num_threads <num_threads> # Define o número de threads PHP a serem iniciadas. Padrão: 2x o número de CPUs disponíveis.
max_threads <num_threads> # Limita o número de threads PHP adicionais que podem ser iniciadas em tempo de execução. Padrão: num_threads. Pode ser definido como 'auto'.
max_wait_time <duracao> # 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 <chave> <valor> # Define uma diretiva php.ini. Pode ser usada várias vezes para definir múltiplas diretivas.
worker {
file <caminho> # Define o caminho para o script do worker.
num <num> # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de CPUs disponíveis.
env <chave> <valor> # 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 <caminho> # Define o caminho para monitorar alterações no arquivo. Pode ser especificado mais de uma vez para múltiplos caminhos.
name <nome> # Define o nome do worker, usado em logs e métricas. Padrão: caminho absoluto do arquivo do worker.
}
}
}
# ...
```
Alternativamente, você pode usar a forma abreviada de uma linha da opção
`worker`:
```caddyfile
{
frankenphp {
worker <arquivo> <num>
}
}
# ...
```
Você também pode definir vários workers se servir várias aplicações no mesmo
servidor:
```caddyfile
app.example.com {
php_server {
root /caminho/para/aplicacao/public
worker index.php <num>
}
}
outra.example.com {
php_server {
root /caminho/para/outra/aplicacao/public
worker index.php <num>
}
}
# ...
```
Usar a diretiva `php_server` geralmente é o que você precisa, mas se precisar de
controle total, você pode usar a diretiva `php` de mais baixo nível.
A diretiva `php` passa toda a entrada para o PHP, em vez de primeiro verificar
se é um arquivo PHP ou não.
Leia mais sobre isso na [página de desempenho](performance.md#try_files).
Usar a diretiva `php_server` é equivalente a esta configuração:
```caddyfile
route {
# Adiciona barra final para requisições de diretório
@canonicalPath {
file {path}/index.php
not path */
}
redir @canonicalPath {path}/ 308
# Se o arquivo requisitado não existir, tenta os arquivos index
@indexFiles file {
try_files {path} {path}/index.php index.php
split_path .php
}
rewrite @indexFiles {http.matchers.file.relative}
# FrankenPHP!
@phpFiles path *.php
php @phpFiles
file_server
}
```
As diretivas `php_server` e `php` têm as seguintes opções:
```caddyfile
php_server [<matcher>] {
root <directory> # Define a pasta raiz para o site. Padrão: diretiva `root`.
split_path <delim...> # 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 <chave> <valor> # 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.
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 <caminho> # Define o caminho para o script do worker, pode ser relativo à raiz do php_server
num <num> # Define o número de threads PHP a serem iniciadas, o padrão é 2x o número de threads disponíveis
name <nome> # 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 <caminho> # Define o caminho para monitorar alterações no arquivo. Pode ser especificado mais de uma vez para múltiplos caminhos.
env <chave> <valor> # 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.
}
worker <outro_arquivo> <num> # Também pode usar a forma abreviada, como no bloco global frankenphp.
}
```
### Monitorando alterações em arquivos
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
diretiva `watch`.
Isso é útil para ambientes de desenvolvimento.
```caddyfile
{
frankenphp {
worker {
file /caminho/para/aplicacao/public/worker.php
watch
}
}
}
```
Se o diretório `watch` não for especificado, ele usará o valor padrão
`./**/*.{php,yaml,yml,twig,env}`,
que monitora todos os arquivos `.php`, `.yaml`, `.yml`, `.twig` e `.env` no
diretório e subdiretórios onde o processo FrankenPHP foi iniciado.
Você também pode especificar um ou mais diretórios por meio de um
[padrão de nome de arquivo shell](https://pkg.go.dev/path/filepath#Match):
```caddyfile
{
frankenphp {
worker {
file /caminho/para/aplicacao/public/worker.php
watch /caminho/para/aplicacao # monitora todos os arquivos em todos os subdiretórios de /caminho/para/aplicacao
watch /caminho/para/aplicacao/*.php # monitora arquivos terminados em .php em /caminho/para/aplicacao
watch /caminho/para/aplicacao/**/*.php # monitora arquivos PHP em /caminho/para/aplicacao e subdiretórios
watch /caminho/para/aplicacao/**/*.{php,twig} # monitora arquivos PHP e Twig em /caminho/para/aplicacao e subdiretórios
}
}
}
```
- O padrão `**` significa monitoramento recursivo
- Diretórios também podem ser relativos (ao local de início do processo
FrankenPHP)
- Se você tiver vários workers definidos, todos eles serão reiniciados quando um
arquivo for alterado
- 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
[e-dant/watcher](https://github.com/e-dant/watcher).
### Full Duplex (HTTP/1)
Ao usar HTTP/1.x, pode ser desejável habilitar o modo full-duplex para permitir
a gravação de uma resposta antes que todo o corpo tenha sido lido.
(por exemplo: WebSocket, Server-Sent Events, etc.)
Esta é uma configuração opcional que precisa ser adicionada às opções globais no
`Caddyfile`:
```caddyfile
{
servers {
enable_full_duplex
}
}
```
> [!CAUTION]
>
> Habilitar esta opção pode causar deadlock em clientes HTTP/1.x antigos que não
> suportam full-duplex.
> Isso também pode ser configurado usando a configuração de ambiente
> `CADDY_GLOBAL_OPTIONS`:
```sh
CADDY_GLOBAL_OPTIONS="servers {
enable_full_duplex
}"
```
Você pode encontrar mais informações sobre esta configuração na
[documentação do Caddy](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex).
## Variáveis de ambiente
As seguintes variáveis de ambiente podem ser usadas para injetar diretivas Caddy
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/`.
- `CADDY_GLOBAL_OPTIONS`: injeta
[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
superglobal `$_SERVER`.
O valor `S` da
[diretiva `variables_order` do PHP](https://www.php.net/manual/pt_BR/ini.core.php#ini.variables-order)
é sempre equivalente a `ES`, independentemente da colocação de `E` em outra
parte desta diretiva.
## Configuração do PHP
Para carregar
[arquivos de configuração adicionais do PHP](https://www.php.net/manual/pt_BR/configuration.file.php#configuration.file.scan),
a variável de ambiente `PHP_INI_SCAN_DIR` pode ser usada.
Quando definida, o PHP carregará todos os arquivos com a extensão `.ini`
presentes nos diretórios fornecidos.
Você também pode alterar a configuração do PHP usando a diretiva `php_ini` no
`Caddyfile`:
```caddyfile
{
frankenphp {
php_ini memory_limit 256M
# ou
php_ini {
memory_limit 256M
max_execution_time 15
}
}
}
```
## Habilitar o modo de depuração
Ao usar a imagem do Docker, defina a variável de ambiente `CADDY_GLOBAL_OPTIONS`
como `debug` para habilitar o modo de depuração:
```console
docker run -v $PWD:/app/public \
-e CADDY_GLOBAL_OPTIONS=debug \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```

232
docs/pt-br/docker.md Normal file
View File

@@ -0,0 +1,232 @@
# Construindo uma imagem Docker personalizada
[As imagens Docker do FrankenPHP](https://hub.docker.com/r/dunglas/frankenphp)
são baseadas em [imagens oficiais do PHP](https://hub.docker.com/_/php/).
Variantes do Debian e do Alpine Linux são fornecidas para arquiteturas
populares.
Variantes do Debian são recomendadas.
Variantes para PHP 8.2, 8.3 e 8.4 são fornecidas.
As tags seguem este padrão:
`dunglas/frankenphp:<versao-do-frankenphp>-php<versao-do-php>-<so>`.
- `<versao-do-frankenphp>` e `<versao-do-php>` são números de versão do
FrankenPHP e do PHP, respectivamente, variando de maior (ex.: `1`), menor
(ex.: `1.2`) a versões de patch (ex.: `1.2.3`).
- `<so>` é `bookworm` (para Debian Bookworm) ou `alpine` (para a versão estável
mais recente do Alpine).
[Navegue pelas tags](https://hub.docker.com/r/dunglas/frankenphp/tags).
## Como usar as imagens
Crie um `Dockerfile` no seu projeto:
```dockerfile
FROM dunglas/frankenphp
COPY . /app/public
```
Em seguida, execute estes comandos para compilar e executar a imagem do Docker:
```console
docker build -t minha-app-php .
docker run -it --rm --name minha-app-rodando minha-app-php
```
## Como instalar mais extensões PHP
O script
[`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer)
é fornecido na imagem base.
Adicionar extensões PHP adicionais é simples:
```dockerfile
FROM dunglas/frankenphp
# adicione extensões adicionais aqui:
RUN install-php-extensions \
pdo_mysql \
gd \
intl \
zip \
opcache
```
## Como instalar mais módulos do 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
FrankenPHP.
A maneira mais fácil de instalar módulos personalizados do Caddy é usar o
[xcaddy](https://github.com/caddyserver/xcaddy):
```dockerfile
FROM dunglas/frankenphp:builder AS builder
# Copia o xcaddy para a imagem do builder
COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy
# O CGO precisa estar habilitado para compilar o FrankenPHP
RUN CGO_ENABLED=1 \
XCADDY_SETCAP=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 /usr/local/bin/frankenphp \
--with github.com/dunglas/frankenphp=./ \
--with github.com/dunglas/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
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy
# Adicione módulos Caddy extras aqui
FROM dunglas/frankenphp AS runner
# Substitui o binário oficial pelo que contém seus módulos personalizados
COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
```
A imagem `builder` fornecida pelo FrankenPHP contém uma versão compilada de
`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
quanto para Alpine.
> [!TIP]
>
> Se você estiver usando Alpine Linux e Symfony, pode ser necessário
> [aumentar o tamanho padrão da pilha](compile.md#using-xcaddy).
## Habilitando o modo worker por padrão
Defina a variável de ambiente `FRANKENPHP_CONFIG` para iniciar o FrankenPHP com
um worker script:
```dockerfile
FROM dunglas/frankenphp
# ...
ENV FRANKENPHP_CONFIG="worker ./public/index.php"
```
## Usando um volume em desenvolvimento
Para desenvolver facilmente com o FrankenPHP, monte o diretório do seu host que
contém o código-fonte da aplicação como um volume no contêiner Docker:
```console
docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty minha-app-php
```
> [!TIP]
>
> A opção `--tty` permite ter logs legíveis por humanos em vez de logs JSON.
Com o Docker Compose:
```yaml
# compose.yaml
services:
php:
image: dunglas/frankenphp
# Descomente a linha a seguir se quiser usar um Dockerfile personalizado
#build: .
# Descomente a linha a seguir se quiser executar isso em um ambiente de
# produção
# restart: always
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "443:443/udp" # HTTP/3
volumes:
- ./:/app/public
- caddy_data:/data
- caddy_config:/config
# Comente a linha a seguir em produção, isso permite ter logs legíveis em
# desenvolvimento
tty: true
# Volumes necessários para certificados e configuração do Caddy
volumes:
caddy_data:
caddy_config:
```
## Executando como um usuário não root
O FrankenPHP pode ser executado como um usuário não root no Docker.
Aqui está um exemplo de `Dockerfile` fazendo isso:
```dockerfile
FROM dunglas/frankenphp
ARG USER=appuser
RUN \
# Use "adduser -D ${USER}" para distribuições baseadas em Alpine
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
USER ${USER}
```
### Executando sem capacidades adicionais
Mesmo executando sem root, o FrankenPHP precisa do recurso
`CAP_NET_BIND_SERVICE` para vincular o servidor web em portas privilegiadas (80
e 443).
Se você expor o FrankenPHP em uma porta não privilegiada (1024 e acima), é
possível executar o servidor web como um usuário não root e sem a necessidade de
nenhuma capacidade adicional:
```dockerfile
FROM dunglas/frankenphp
ARG USER=appuser
RUN \
# Use "adduser -D ${USER}" para distribuições baseadas em Alpine
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
USER ${USER}
```
Em seguida, defina a variável de ambiente `SERVER_NAME` para usar uma porta sem
privilégios.
Exemplo: `:8000`
## Atualizações
As imagens do Docker são compiladas:
- 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
principal do repositório do GitHub.
As tags `latest*` apontam para o HEAD do branch `main`.
Tags no formato `sha-<git-commit-hash>` também estão disponíveis.

24
docs/pt-br/early-hints.md Normal file
View File

@@ -0,0 +1,24 @@
# Early Hints
O FrankenPHP suporta nativamente o
[código de status 103 Early Hints](https://developer.chrome.com/blog/early-hints/).
Usar Early Hints pode melhorar o tempo de carregamento das suas páginas web em
30%.
```php
<?php
header('Link: </style.css>; rel=preload; as=style');
headers_send(103);
// seus algoritmos e consultas SQL lentos 🤪
echo <<<'HTML'
<!DOCTYPE html>
<title>Olá FrankenPHP</title>
<link rel="stylesheet" href="style.css">
HTML;
```
As Early Hints são suportadas tanto pelo modo normal quanto pelo modo
[worker](worker.md).

166
docs/pt-br/embed.md Normal file
View File

@@ -0,0 +1,166 @@
# Aplicações PHP como binários independentes
O FrankenPHP tem a capacidade de incorporar o código-fonte e os recursos 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
independentes que incluem a própria aplicação, o interpretador PHP e o Caddy, um
servidor web de nível de produção.
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).
## Preparando sua aplicação
Antes de criar o binário independente, certifique-se de que sua aplicação esteja
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).
- Remover arquivos desnecessários, como `.git` ou testes, para reduzir o tamanho
do seu binário final.
Por exemplo, para uma aplicação Symfony, você pode usar os seguintes comandos:
```console
# Exporta o projeto para se livrar de .git/, etc.
mkdir $TMPDIR/minha-aplicacao-preparada
git archive HEAD | tar -x -C $TMPDIR/minha-aplicacao-preparada
cd $TMPDIR/minha-aplicacao-preparada
# Define as variáveis de ambiente adequadas
echo APP_ENV=prod > .env.local
echo APP_DEBUG=0 >> .env.local
# Remove os testes e outros arquivos desnecessários para economizar espaço.
# Como alternativa, adicione esses arquivos com o atributo export-ignore no seu
# arquivo .gitattributes.
rm -Rf tests/
# Instala as dependências
composer install --ignore-platform-reqs --no-dev -a
# Otimiza o arquivo .env
composer dump-env prod
```
### Personalizando a configuração
Para personalizar
[a configuração](config.md), você pode colocar um arquivo `Caddyfile` e um
arquivo `php.ini` no diretório principal da aplicação a ser incorporada
(`$TMPDIR/minha-aplicacao-preparada` no exemplo anterior).
## Criando um binário do Linux
A maneira mais fácil de criar um binário do Linux é usar o builder baseado em
Docker que fornecemos.
1. Crie um arquivo chamado `static-build.Dockerfile` no repositório da sua
aplicação:
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# Copia sua aplicação
WORKDIR /go/src/app/dist/app
COPY . .
# Compila o binário estático
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
```
> [!CAUTION]
>
> Alguns arquivos `.dockerignore` (por exemplo, o
> [`.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.
2. Construa:
```console
docker build -t aplicacao-estatica -f static-build.Dockerfile .
```
3. Extraia o binário:
```console
docker cp $(docker create --name aplicacao-estatica-tmp aplicacao-estatica):/go/src/app/dist/frankenphp-linux-x86_64 minha-aplicacao ; docker rm aplicacao-estatica-tmp
```
O binário resultante é o arquivo `minha-aplicacao` no diretório atual.
## Criando um binário para outros sistemas operacionais
Se você não quiser usar o Docker ou quiser compilar um binário para macOS, use o
script de shell que fornecemos:
```console
git clone https://github.com/dunglas/frankenphp
cd frankenphp
EMBED=/caminho/para/sua/aplicacao ./build-static.sh
```
O binário resultante é o arquivo `frankenphp-<os>-<arch>` no diretório `dist/`.
## Usando o binário
É isso! O arquivo `minha-aplicacao` (ou `dist/frankenphp-<os>-<arch>` em outros
sistemas operacionais) contém sua aplicação independente!
Para iniciar a aplicação web, execute:
```console
./minha-aplicacao php-server
```
Se a sua aplicação contiver um [worker script](worker.md), inicie o worker com
algo como:
```console
./minha-aplicacao php-server --worker public/index.php
```
Para habilitar HTTPS (um certificado Let's Encrypt é criado automaticamente),
HTTP/2 e HTTP/3, especifique o nome de domínio a ser usado:
```console
./minha-aplicacao php-server --domain localhost
```
Você também pode executar os scripts PHP CLI incorporados ao seu binário:
```console
./minha-aplicacao php-cli bin/console
```
## Extensões PHP
Por padrão, o script criará 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).
Para personalizar as extensões, use a variável de ambiente `PHP_EXTENSIONS`.
## Personalizando a compilação
[Leia a documentação da compilação estática](static.md) para ver como
personalizar o binário (extensões, versão do PHP...).
## Distribuindo o binário
No Linux, o binário criado é compactado usando [UPX](https://upx.github.io).
No Mac, para reduzir o tamanho do arquivo antes de enviá-lo, você pode
compactá-lo.
Recomendamos usar `xz`.

View File

@@ -0,0 +1,39 @@
# Usando GitHub Actions
Este repositório constrói e implementa a imagem do Docker no
[Docker Hub](https://hub.docker.com/r/dunglas/frankenphp) em cada pull request
aprovado ou em seu próprio fork após a configuração.
## Configurando GitHub Actions
Nas configurações do repositório, em "Secrets", adicione os seguintes segredos:
- `REGISTRY_LOGIN_SERVER`: O registro do Docker a ser usado (por exemplo,
`docker.io`).
- `REGISTRY_USERNAME`: O nome de usuário a ser usado para fazer login no
registro (por exemplo, `dunglas`).
- `REGISTRY_PASSWORD`: A senha a ser usada para fazer login no registro (por
exemplo, uma chave de acesso).
- `IMAGE_NAME`: O nome da imagem (por exemplo, `dunglas/frankenphp`).
## Construindo e enviando a imagem
1. Crie um pull request ou faça o push para o seu fork.
2. O GitHub Actions construirá a imagem e executará os testes.
3. Se a construção for bem-sucedida, a imagem será enviada para o registro
usando a tag `pr-x`, onde `x` é o número do PR.
## Implantando a imagem
1. Após o merge do pull request, o GitHub Actions executará os testes novamente
e criará uma nova imagem.
2. Se a construção for bem-sucedida, a tag `main` será atualizada no registro do
Docker.
## Versões
1. Crie uma nova tag no repositório.
2. O GitHub Actions construirá a imagem e executará os testes.
3. Se a construção for bem-sucedida, a imagem será enviada para o registro
usando o nome da tag como tag (por exemplo, `v1.2.3` e `v1.2` serão criadas).
4. A tag `latest` também será atualizada.

185
docs/pt-br/known-issues.md Normal file
View File

@@ -0,0 +1,185 @@
# Problemas conhecidos
## Extensões PHP não suportadas
As seguintes extensões são conhecidas por não serem compatíveis com o
FrankenPHP:
| Nome | Motivo | Alternativas |
|-------------------------------------------------------------------------------------------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------|
| [imap](https://www.php.net/manual/pt_BR/imap.installation.php) | Não é thread-safe | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) |
| [newrelic](https://docs.newrelic.com/docs/apm/agents/php-agent/getting-started/introduction-new-relic-php/) | Não é thread-safe | - |
## Extensões PHP com falhas
As seguintes extensões apresentam falhas conhecidas e comportamentos inesperados
quando usadas com o FrankenPHP:
| Nome | Problema |
|------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [ext-openssl](https://www.php.net/manual/pt_BR/book.openssl.php) | Ao usar uma versão estática do FrankenPHP (compilada com a `libc` `musl`), a extensão OpenSSL pode quebrar sob cargas pesadas. Uma solução alternativa é usar uma versão vinculada dinamicamente (como a usada em imagens Docker). Esta falha está [sendo monitorada pelo PHP](https://github.com/php/php-src/issues/13648) |
## `get_browser`
A função
[`get_browser()`](https://www.php.net/manual/pt_BR/function.get-browser.php)
parece apresentar mau desempenho após algum tempo.
Uma solução alternativa é armazenar em cache (por exemplo, com
[APCu](https://www.php.net/manual/pt_BR/book.apcu.php)) os resultados por Agente
de Usuário, pois são estáticos.
## Imagens Docker binárias independentes e baseadas em Alpine
As imagens Docker binárias independentes e baseadas em Alpine
(`dunglas/frankenphp:*-alpine`) usam a [`libc` `musl`](https://musl.libc.org/)
em vez de [`glibc` e similares](https://www.etalabs.net/compare_libcs.html) para
manter um tamanho binário menor.
Isso pode levar a alguns problemas de compatibilidade.
Em particular, o sinalizador glob `GLOB_BRACE`
[não está disponível](https://www.php.net/manual/pt_BR/function.glob.php)
## Usando `https://127.0.0.1` com o Docker
Por padrão, o FrankenPHP gera um certificado TLS para `localhost`.
É a opção mais fácil e recomendada para desenvolvimento local.
Se você realmente deseja usar `127.0.0.1` como host, é possível configurá-lo
para gerar um certificado definindo o nome do servidor como `127.0.0.1`.
Infelizmente, isso não é suficiente ao usar o Docker devido ao
[seu sistema de rede](https://docs.docker.com/network/).
Você receberá um erro TLS semelhante a
`curl: (35) LibreSSL/3.3.6: erro:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`.
Se você estiver usando Linux, uma solução é usar
[o driver de rede do host](https://docs.docker.com/network/network-tutorial-host/):
```console
docker run \
-e SERVER_NAME="127.0.0.1" \
-v $PWD:/app/public \
--network host \
dunglas/frankenphp
```
O driver de rede do host não é compatível com Mac e Windows.
Nessas plataformas, você terá que descobrir o endereço IP do contêiner e
incluí-lo nos nomes dos servidores.
Execute o comando `docker network inspect bridge` e verifique a chave
`Containers` para identificar o último endereço IP atribuído atualmente sob a
chave `IPv4Address` e incremente-o em um.
Se nenhum contêiner estiver em execução, o primeiro endereço IP atribuído
geralmente é `172.17.0.2`.
Em seguida, inclua isso na variável de ambiente `SERVER_NAME`:
```console
docker run \
-e SERVER_NAME="127.0.0.1, 172.17.0.3" \
-v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
> [!CAUTION]
>
> Certifique-se de substituir `172.17.0.3` pelo IP que será atribuído ao seu
> contêiner.
Agora você deve conseguir acessar `https://127.0.0.1` a partir da máquina host.
Se este não for o caso, inicie o FrankenPHP em modo de depuração para tentar
descobrir o problema:
```console
docker run \
-e CADDY_GLOBAL_OPTIONS="debug" \
-e SERVER_NAME="127.0.0.1" \
-v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
## Scripts do Composer que referenciam `@php`
[Scripts do Composer](https://getcomposer.org/doc/articles/scripts.md) podem
querer executar um binário PHP para algumas tarefas, por exemplo, em
[um projeto Laravel](laravel.md) para executar
`@php artisan package:discover --ansi`.
Isso
[atualmente falha](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915)
por dois motivos:
- O Composer não sabe como chamar o binário do FrankenPHP;
- O Composer pode adicionar configurações do PHP usando a flag `-d` no comando,
que o FrankenPHP ainda não suporta.
Como solução alternativa, podemos criar um script de shell em
`/usr/local/bin/php` que remove os parâmetros não suportados e, em seguida,
chama o FrankenPHP:
```bash
#!/usr/bin/env bash
args=("$@")
index=0
for i in "$@"
do
if [ "$i" == "-d" ]; then
unset 'args[$index]'
unset 'args[$index+1]'
fi
index=$((index+1))
done
/usr/local/bin/frankenphp php-cli ${args[@]}
```
Em seguida, defina a variável de ambiente `PHP_BINARY` para o caminho do nosso
script `php` e execute o Composer:
```console
export PHP_BINARY=/usr/local/bin/php
composer install
```
## Solução de problemas de TLS/SSL com binários estáticos
Ao usar binários estáticos, você pode encontrar os seguintes erros relacionados
a TLS, por exemplo, ao enviar emails usando STARTTLS:
```text
Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages:
error:80000002:system library::No such file or directory
error:80000002:system library::No such file or directory
error:80000002:system library::No such file or directory
error:0A000086:SSL routines::certificate verify failed
```
Como o binário estático não empacota certificados TLS, você precisa apontar o
OpenSSL para a instalação local de certificados de CA.
Inspecione a saída de
[`openssl_get_cert_locations()`](https://www.php.net/manual/pt_BR/function.openssl-get-cert-locations.php),
para descobrir onde os certificados de CA devem ser instalados e armazene-os
neste local.
> [!WARNING]
>
> Contextos web e CLI podem ter configurações diferentes.
> Certifique-se de executar `openssl_get_cert_locations()` no contexto
> apropriado.
[Certificados CA extraídos do Mozilla podem ser baixados no site do curl](https://curl.se/docs/caextract.html).
Como alternativa, muitas distribuições, incluindo Debian, Ubuntu e Alpine,
fornecem pacotes chamados `ca-certificates` que contêm esses certificados.
Também é possível usar `SSL_CERT_FILE` e `SSL_CERT_DIR` para indicar ao OpenSSL
onde procurar certificados CA:
```console
# Define variáveis de ambiente para certificados TLS
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
export SSL_CERT_DIR=/etc/ssl/certs
```

216
docs/pt-br/laravel.md Normal file
View File

@@ -0,0 +1,216 @@
# Laravel
## Docker
Servir uma aplicação web [Laravel](https://laravel.com) com FrankenPHP é tão
fácil quanto montar o projeto no diretório `/app` da imagem oficial do Docker.
Execute este comando a partir do diretório principal da sua aplicação Laravel:
```console
docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp
```
E divirta-se!
## Instalação local
Alternativamente, você pode executar seus projetos Laravel com FrankenPHP a
partir da sua máquina local:
1. [Baixe o binário correspondente ao seu sistema](../../#getting-started).
2. Adicione a seguinte configuração a um arquivo chamado `Caddyfile` no
diretório raiz do seu projeto Laravel:
```caddyfile
{
frankenphp
}
# O nome de domínio do seu servidor
localhost {
# Define o diretório webroot/ como root public/
root public/
# Habilita a compressão (opcional)
encode zstd br gzip
# Executa os arquivos PHP a partir do diretório public/ e serve os assets
php_server {
try_files {path} index.php
}
}
```
3. Inicie o FrankenPHP a partir do diretório raiz do seu projeto Laravel:
`frankenphp run`.
## Laravel Octane
O Octane pode ser instalado através do gerenciador de pacotes Composer:
```console
composer require laravel/octane
```
Após instalar o Octane, você pode executar o comando `octane:install` do
Artisan, que instalará o arquivo de configuração do Octane em sua aplicação:
```console
php artisan octane:install --server=frankenphp
```
O servidor Octane pode ser iniciado por meio do comando `octane:frankenphp` do
Artisan.
```console
php artisan octane:frankenphp
```
O comando `octane:frankenphp` pode receber as seguintes opções:
- `--host`: O endereço IP ao qual o servidor deve se vincular (padrão:
`127.0.0.1`).
- `--port`: A porta na qual o servidor deve estar disponível (padrão: `8000`).
- `--admin-port`: A porta na qual o servidor de administração deve estar
disponível (padrão: `2019`).
- `--workers`: O número de workers que devem estar disponíveis para processar
requisições (padrão: `auto`).
- `--max-requests`: O número de requisições a serem processadas antes de
recarregar o servidor (padrão: `500`).
- `--caddyfile`: O caminho para o arquivo `Caddyfile` do FrankenPHP (padrão:
[stub de `Caddyfile` no Laravel Octane](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)).
- `--https`: Habilita HTTPS, HTTP/2 e HTTP/3 e gera e renova certificados
automaticamente.
- `--http-redirect`: Habilita o redirecionamento de HTTP para HTTPS (somente
- habilitado se `--https` for passada).
- `--watch`: Recarrega o servidor automaticamente quando a aplicação é
modificada.
- `--poll`: Usa o polling do sistema de arquivos durante a verificação para
monitorar arquivos em uma rede.
- `--log-level`: Registra mensagens de log no nível de log especificado ou acima
dele, usando o logger nativo do Caddy.
> [!TIP]
> Para obter logs JSON estruturados (útil ao usar soluções de análise de logs),
> passe explicitamente a opção `--log-level`.
Saiba mais sobre o
[Laravel Octane em sua documentação oficial](https://laravel.com/docs/octane).
## Aplicações Laravel como binários independentes
Usando o [recurso de incorporação de aplicativos do FrankenPHP](embed.md), é
possível distribuir aplicativos Laravel como binários independentes.
Siga estes passos para empacotar sua aplicação Laravel como um binário
independente para Linux:
1. Crie um arquivo chamado `static-build.Dockerfile` no repositório da sua
aplicação:
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# Copia sua aplicação
WORKDIR /go/src/app/dist/app
COPY . .
# Remove os testes e outros arquivos desnecessários para economizar espaço
# Como alternativa, adicione esses arquivos a um arquivo .dockerignore
RUN rm -Rf tests/
# Copia o arquivo .env
RUN cp .env.example .env
# Altera APP_ENV e APP_DEBUG para que estejam prontas para produção
RUN sed -i'' -e 's/^APP_ENV=.*/APP_ENV=production/' -e 's/^APP_DEBUG=.*/APP_DEBUG=false/' .env
# Faça outras alterações no seu arquivo .env, se necessário
# Instala as dependências
RUN composer install --ignore-platform-reqs --no-dev -a
# Compila o binário estático
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
```
> [!CAUTION]
>
> Alguns arquivos `.dockerignore` ignorarão o diretório `vendor/` e os
> arquivos `.env`.
> Certifique-se de ajustar ou remover o arquivo `.dockerignore` antes da
> compilação.
2. Construa:
```console
docker build -t static-laravel-app -f static-build.Dockerfile .
```
3. Extraia o binário:
```console
docker cp $(docker create --name static-laravel-app-tmp static-laravel-app):/go/src/app/dist/frankenphp-linux-x86_64 frankenphp ; docker rm static-laravel-app-tmp
```
4. Popule os caches:
```console
frankenphp php-cli artisan optimize
```
5. Execute as migrações de banco de dados (se houver):
```console
frankenphp php-cli artisan migrate
```
6. Gere a chave secreta da aplicação:
```console
frankenphp php-cli artisan key:generate
```
7. Inicie o servidor:
```console
frankenphp php-server
```
Agora sua aplicação está pronta!
Saiba mais sobre as opções disponíveis e como compilar binários para outros
sistemas operacionais na documentação de
[incorporação de aplicações](embed.md).
### Alterando o caminho do armazenamento
Por padrão, o Laravel armazena arquivos enviados, caches, logs, etc., no
diretório `storage/` da aplicação.
Isso não é adequado para aplicações embarcadas, pois cada nova versão será
extraída para um diretório temporário diferente.
Defina a variável de ambiente `LARAVEL_STORAGE_PATH` (por exemplo, no seu
arquivo `.env`) ou chame o método
`Illuminate\Foundation\Application::useStoragePath()` para usar um diretório
fora do diretório temporário.
### Executando o Octane com binários independentes
É possível até empacotar aplicações Octane do Laravel como binários
independentes!
Para fazer isso, [instale o Octane corretamente](#laravel-octane) e siga os
passos descritos na
[seção anterior](#aplicações-laravel-como-binários-independentes).
Em seguida, para iniciar o FrankenPHP em modo worker através do Octane, execute:
```console
PATH="$PWD:$PATH" frankenphp php-cli artisan octane:frankenphp
```
> [!CAUTION]
>
> Para que o comando funcione, o binário independente **deve** ser nomeado
> `frankenphp` porque o Octane precisa de um programa chamado `frankenphp`
> disponível no caminho.

21
docs/pt-br/mercure.md Normal file
View File

@@ -0,0 +1,21 @@
# Tempo real
O FrankenPHP vem com um hub [Mercure](https://mercure.rocks) integrado!
O Mercure permite que você envie eventos em tempo real para todos os
dispositivos conectados: eles receberão um evento JavaScript instantaneamente.
Não é necessária nenhuma biblioteca JS ou SDK!
![Mercure](mercure-hub.png)
Para habilitar o hub Mercure, atualize o `Caddyfile` conforme descrito
[no site do Mercure](https://mercure.rocks/docs/hub/config).
O caminho do hub Mercure é `/.well-known/mercure`.
Ao executar o FrankenPHP dentro do Docker, a URL de envio completa seria
`http://php/.well-known/mercure` (com `php` sendo o nome do contêiner que
executa o FrankenPHP).
Para enviar atualizações do Mercure do seu código, recomendamos o
[Componente Symfony Mercure](https://symfony.com/components/Mercure) (você não
precisa do framework full-stack do Symfony para usá-lo).

29
docs/pt-br/metrics.md Normal file
View File

@@ -0,0 +1,29 @@
# Métricas
Quando as [métricas do Caddy](https://caddyserver.com/docs/metrics) estão
habilitadas, o FrankenPHP expõe as seguintes métricas:
- `frankenphp_total_threads`: O número total de threads PHP.
- `frankenphp_busy_threads`: O número de threads PHP processando uma requisição
no momento (workers em execução sempre consomem uma thread).
- `frankenphp_queue_depth`: O número de requisições regulares na fila.
- `frankenphp_total_workers{worker="[nome_do_worker]"}`: O número total de
workers.
- `frankenphp_busy_workers{worker="[nome_do_worker]"}`: O número de workers
processando uma requisição no momento.
- `frankenphp_worker_request_time{worker="[nome_do_worker]"}`: O tempo gasto no
processamento de requisições por todos os workers.
- `frankenphp_worker_request_count{worker="[nome_do_worker]"}`: O número de
requisições processadas por todos os workers.
- `frankenphp_ready_workers{worker="[nome_do_worker]"}`: O número de workers que
chamaram `frankenphp_handle_request` pelo menos uma vez.
- `frankenphp_worker_crashes{worker="[nome_do_worker]"}`: O número de vezes que
um worker foi encerrado inesperadamente.
- `frankenphp_worker_restarts{worker="[nome_do_worker]"}`: O número de vezes que
um worker foi reiniciado deliberadamente.
- `frankenphp_worker_queue_depth{worker="[nome_do_worker]"}`: O número de
requisições na fila.
Para métricas de worker, o placeholder `[nome_do_worker]` é substituído pelo
nome do worker no Caddyfile; caso contrário, o caminho absoluto do arquivo do
worker será usado.

214
docs/pt-br/performance.md Normal file
View File

@@ -0,0 +1,214 @@
# Desempenho
Por padrão, o FrankenPHP tenta oferecer um bom equilíbrio entre desempenho e
facilidade de uso.
No entanto, é possível melhorar substancialmente o desempenho usando uma
configuração apropriada.
## Número de threads e workers
Por padrão, o FrankenPHP inicia 2 vezes mais threads e workers (no modo worker)
do que a quantidade de CPU disponível.
Os valores apropriados dependem muito de como sua aplicação foi escrita, do que
ela faz e do seu hardware.
Recomendamos fortemente alterar esses valores.
Para melhor estabilidade do sistema, recomenda-se ter `num_threads` x
`memory_limit` < `available_memory`.
Para encontrar os valores corretos, é melhor executar testes de carga simulando
tráfego real.
[k6](https://k6.io) e [Gatling](https://gatling.io) são boas ferramentas para
isso.
Para configurar o número de threads, use a opção `num_threads` das diretivas
`php_server` e `php`.
Para alterar o número de workers, use a opção `num` da seção `worker` da
diretiva `frankenphp`.
### `max_threads`
Embora seja sempre melhor saber exatamente como será o seu tráfego, aplicações
reais tendem a ser mais imprevisíveis.
A [configuração](config.md#configuracao-do-caddyfile) `max_threads` permite que
o FrankenPHP gere threads adicionais automaticamente em tempo de execução até o
limite especificado.
`max_threads` pode ajudar você a descobrir quantas threads são necessárias para
lidar com seu tráfego e pode tornar o servidor mais resiliente a picos de
latência.
Se definido como `auto`, o limite será estimado com base no `memory_limit` em
seu `php.ini`.
Caso contrário, `auto` assumirá como padrão o valor 2x `num_threads`.
Lembre-se de que `auto` pode subestimar bastante o número de threads
necessárias.
`max_threads` é semelhante ao
[pm.max_children](https://www.php.net/manual/pt_BR/install.fpm.configuration.php#pm.max-children)
do PHP FPM.
A principal diferença é que o FrankenPHP usa threads em vez de processos e os
delega automaticamente entre diferentes worker scripts e o modo clássico,
conforme necessário.
## Modo worker
Habilitar [o modo worker](worker.md) melhora drasticamente o desempenho, mas sua
aplicação precisa ser adaptada para ser compatível com este modo: você precisa
criar um worker script e garantir que a aplicação não esteja com vazamento de
memória.
## Não use `musl`
A variante Alpine Linux das imagens oficiais do Docker e os binários padrão que
fornecemos usam [a biblioteca C `musl`](https://musl.libc.org).
O PHP é conhecido por ser
[mais lento](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381)
ao usar esta biblioteca C alternativa em vez da biblioteca GNU tradicional,
especialmente quando compilado no modo ZTS (thread-safe), necessário para o
FrankenPHP.
A diferença pode ser significativa em um ambiente com muitas threads.
Além disso,
[alguns bugs só acontecem ao usar `musl`](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl).
Em ambientes de produção, recomendamos o uso do FrankenPHP vinculado à `glibc`.
Isso pode ser feito usando as imagens Docker do Debian (o padrão), baixando o
binário com sufixo -gnu de nossos
[Lançamentos](https://github.com/dunglas/frankenphp/releases) ou
[compilando o FrankenPHP a partir dos fontes](compile.md).
Como alternativa, fornecemos binários `musl` estáticos compilados com
[o alocador `mimalloc`](https://github.com/microsoft/mimalloc), o que alivia os
problemas em cenários com threads.
## Configuração de Tempo de Execução do Go
O FrankenPHP é escrito em Go.
Em geral, o tempo de execução do Go não requer nenhuma configuração especial,
mas em certas circunstâncias, configurações específicas melhoram o desempenho.
Você provavelmente deseja definir a variável de ambiente `GODEBUG` como
`cgocheck=0` (o padrão nas imagens Docker do FrankenPHP).
Se você executa o FrankenPHP em contêineres (Docker, Kubernetes, LXC...) e
limita a memória disponível para os contêineres, defina a variável de ambiente
`GOMEMLIMIT` para a quantidade de memória disponível.
Para mais detalhes,
[a página de documentação do Go dedicada a este assunto](https://pkg.go.dev/runtime#hdr-Environment_Variables)
é uma leitura obrigatória para aproveitar ao máximo o tempo de execução.
## `file_server`
Por padrão, a diretiva `php_server` configura automaticamente um servidor de
arquivos para servir arquivos estáticos (assets) armazenados no diretório raiz.
Este recurso é conveniente, mas tem um custo.
Para desativá-lo, use a seguinte configuração:
```caddyfile
php_server {
file_server off
}
```
## `try_files`
Além de arquivos estáticos e arquivos PHP, `php_server` também tentará servir o
arquivo index da sua aplicação e os arquivos index de diretório (`/path/` ->
`/path/index.php`).
Se você não precisa de arquivos index de diretório, pode desativá-los definindo
explicitamente `try_files` assim:
```caddyfile
php_server {
try_files {path} index.php
root /raiz/da/sua/aplicacao # adicionar explicitamente a raiz aqui permite um melhor armazenamento em cache
}
```
Isso pode reduzir significativamente o número de operações desnecessárias com
arquivos.
Uma abordagem alternativa com 0 operações desnecessárias no sistema de arquivos
seria usar a diretiva `php` e dividir os arquivos do PHP por caminho.
Essa abordagem funciona bem se toda a sua aplicação for servida por um arquivo
de entrada.
Um exemplo de [configuração](config.md#configuracao-do-caddyfile) que serve
arquivos estáticos a partir de uma pasta `/assets` poderia ser assim:
```caddyfile
route {
@assets {
path /assets/*
}
# tudo a partir de /assets é gerenciado pelo servidor de arquivos
file_server @assets {
root /raiz/da/sua/aplicacao
}
# tudo o que não está em /assets é gerenciado pelo seu arquivo index ou worker PHP
rewrite index.php
php {
root /raiz/da/sua/aplicacao # adicionar explicitamente a raiz aqui permite um melhor armazenamento em cache
}
}
```
## Placeholders
Você pode usar
[placeholders](https://caddyserver.com/docs/conventions#placeholders) nas
diretivas `root` e `env`.
No entanto, isso impede o armazenamento em cache desses valores e acarreta um
custo significativo de desempenho.
Se possível, evite placeholders nessas diretivas.
## `resolve_root_symlink`
Por padrão, se o diretório raiz for um link simbólico, ele será resolvido
automaticamente pelo FrankenPHP (isso é necessário para o funcionamento correto
do PHP).
Se o diretório raiz não for um link simbólico, você pode desativar esse recurso.
```caddyfile
php_server {
resolve_root_symlink false
}
```
Isso melhorará o desempenho se a diretiva `root` contiver
[placeholders](https://caddyserver.com/docs/conventions#placeholders).
O ganho será insignificante em outros casos.
## Logs
O logging é obviamente muito útil, mas, por definição, requer operações de E/S e
alocações de memória, o que reduz consideravelmente o desempenho.
Certifique-se de
[definir o nível de logging](https://caddyserver.com/docs/caddyfile/options#log)
corretamente e registrar em log apenas o necessário.
## Desempenho do PHP
O FrankenPHP usa o interpretador PHP oficial.
Todas as otimizações de desempenho usuais relacionadas ao PHP se aplicam ao
FrankenPHP.
Em particular:
- verifique se o [OPcache](https://www.php.net/manual/pt_BR/book.opcache.php)
está instalado, habilitado e configurado corretamente.
- habilite as
[otimizações do carregador automático do Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md).
- certifique-se de que o cache do `realpath` seja grande o suficiente para as
necessidades da sua aplicação.
- use
[pré-carregamento](https://www.php.net/manual/pt_BR/opcache.preloading.php).
Para mais detalhes, leia
[a entrada dedicada na documentação do Symfony](https://symfony.com/doc/current/performance.html)
(a maioria das dicas é útil mesmo se você não usar o Symfony).

170
docs/pt-br/production.md Normal file
View File

@@ -0,0 +1,170 @@
# Implantando em produção
Neste tutorial, aprenderemos como implantar uma aplicação PHP em um único
servidor usando o Docker Compose.
Se você estiver usando o Symfony, prefira ler a entrada de documentação
[Implantar em produção](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)
do projeto Docker do Symfony (que usa FrankenPHP).
Se você estiver usando a API Platform (que também usa FrankenPHP), consulte
[a documentação de implantação do framework](https://api-platform.com/docs/deployment/).
## Preparando sua aplicação
Primeiro, crie um `Dockerfile` no diretório raiz do seu projeto PHP:
```dockerfile
FROM dunglas/frankenphp
# Certifique-se de substituir "seu-nome-de-dominio.example.com" pelo seu nome de
# domínio
ENV SERVER_NAME=seu-nome-de-dominio.example.com
# Se quiser desabilitar o HTTPS, use este valor:
#ENV SERVER_NAME=:80
# Habilita as configurações de produção do PHP
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# Copia os arquivos PHP do seu projeto para o diretório public
COPY . /app/public
# Se você usa Symfony ou Laravel, precisa copiar o projeto inteiro:
#COPY . /app
```
Consulte [Criando uma imagem Docker personalizada](docker.md) para mais detalhes
e opções, e para aprender como personalizar a configuração, instalar extensões
PHP e módulos Caddy.
Se o seu projeto usa o Composer, certifique-se de incluí-lo na imagem Docker e
instalar suas dependências.
Em seguida, adicione um arquivo `compose.yaml`:
```yaml
services:
php:
image: dunglas/frankenphp
restart: always
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "443:443/udp" # HTTP/3
volumes:
- caddy_data:/data
- caddy_config:/config
# Volumes necessários para certificados e configuração do Caddy
volumes:
caddy_data:
caddy_config:
```
> [!NOTE]
>
> Os exemplos anteriores são destinados ao uso em produção.
> Em desenvolvimento, você pode querer usar um volume, uma configuração PHP
> diferente e um valor diferente para a variável de ambiente `SERVER_NAME`.
>
> Consulte o projeto [Symfony Docker](https://github.com/dunglas/symfony-docker)
> (que usa FrankenPHP) para um exemplo mais avançado usando imagens
> multiestágio, Composer, extensões PHP extras, etc.
Finalmente, se você usa Git, faça o commit e o push desses arquivos.
## Preparando um servidor
Para implantar sua aplicação em produção, você precisa de um servidor.
Neste tutorial, usaremos uma máquina virtual fornecida pela DigitalOcean, mas
qualquer servidor Linux pode funcionar.
Se você já possui um servidor Linux com o Docker instalado, pode pular direto
para [a próxima seção](#configurando-um-nome-de-domínio).
Caso contrário, use [este link de afiliado](https://m.do.co/c/5d8aabe3ab80) para
obter US$ 200 em créditos gratuitos, crie uma conta e clique em "Create a
Droplet".
Em seguida, clique na aba "Marketplace" na seção "Choose an image" e procure a
aplicação "Docker".
Isso provisionará um servidor Ubuntu com as versões mais recentes do Docker e do
Docker Compose já instaladas!
Para fins de teste, os planos mais baratos serão suficientes.
Para uso real em produção, você provavelmente escolherá um plano na seção
"General Purpose" que atenda às suas necessidades.
![Implantando o FrankenPHP na DigitalOcean com Docker](digitalocean-droplet.png)
Você pode manter os padrões para outras configurações ou ajustá-los de acordo
com suas necessidades.
Não se esqueça de adicionar sua chave SSH ou criar uma senha e, em seguida,
clicar no botão "Finalize and create".
Em seguida, aguarde alguns segundos enquanto seu Droplet é provisionado.
Quando seu Droplet estiver pronto, use SSH para se conectar:
```console
ssh root@<droplet-ip>
```
## Configurando um nome de domínio
Na maioria dos casos, você precisará associar um nome de domínio ao seu site.
Se você ainda não possui um nome de domínio, precisará comprar um por meio de um
registrar.
Em seguida, crie um registro DNS do tipo `A` para o seu nome de domínio,
apontando para o endereço IP do seu servidor:
```dns
seu-nome-de-dominio.example.com. IN A <ip-do-seu-servidor>
```
Exemplo com o serviço DigitalOcean Domains ("Networking" > "Domains"):
![Configurando DNS na DigitalOcean](digitalocean-dns.png)
> [!NOTE]
>
> O Let's Encrypt, o serviço usado por padrão pelo FrankenPHP para gerar
> automaticamente um certificado TLS, não suporta o uso de endereços IP.
> O uso de um nome de domínio é obrigatório para usar o Let's Encrypt.
## Implantando
Copie seu projeto para o servidor usando `git clone`, `scp` ou qualquer outra
ferramenta que atenda às suas necessidades.
Se você usa o GitHub, pode ser útil usar
[uma chave de implantação](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys).
Chaves de implantação também são [suportadas pelo GitLab](https://docs.gitlab.com/ee/user/project/deploy_keys/).
Exemplo com Git:
```console
git clone git@github.com:<usuario>/<nome-do-projeto>.git
```
Acesse o diretório que contém seu projeto (`<nome-do-projeto>`) e inicie a
aplicação em modo de produção:
```console
docker compose up --wait
```
Seu servidor está funcionando e um certificado HTTPS foi gerado automaticamente
para você.
Acesse `https://seu-nome-de-dominio.example.com` e divirta-se!
> [!CAUTION]
>
> O Docker pode ter uma camada de cache; certifique-se de ter a compilação
> correta para cada implantação ou reconstrua seu projeto com a opção
> `--no-cache` para evitar problemas de cache.
## Implantando em múltiplos nós
Se você deseja implantar sua aplicação em um cluster de máquinas, pode usar o
[Docker Swarm](https://docs.docker.com/engine/swarm/stack-deploy/), que é
compatível com os arquivos Compose fornecidos.
Para implantar no Kubernetes, consulte o
[chart do Helm fornecido com a API Platform](https://api-platform.com/docs/deployment/kubernetes/),
que usa FrankenPHP.

208
docs/pt-br/static.md Normal file
View File

@@ -0,0 +1,208 @@
# Criar uma compilação estática
Em vez de usar uma instalação local da biblioteca PHP, é possível criar uma
compilação estática ou principalmente estática do FrankenPHP graças ao excelente
[projeto static-php-cli](https://github.com/crazywhalecc/static-php-cli) (apesar
do nome, este projeto suporta todas as SAPIs, não apenas CLI).
Com este método, um único binário portátil conterá o interpretador PHP, o
servidor web Caddy e o FrankenPHP!
Executáveis nativos totalmente estáticos não requerem dependências e podem até
ser executados na
[imagem Docker `scratch`](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch).
No entanto, eles não podem carregar extensões PHP dinâmicas (como o Xdebug) e
têm algumas limitações por usarem a `libc` `musl`.
A maioria dos binários estáticos requer apenas `glibc` e pode carregar extensões
dinâmicas.
Sempre que possível, recomendamos o uso de compilações principalmente estáticas
baseadas na `glibc`.
O FrankenPHP também suporta
[a incorporação da aplicação PHP no binário estático](embed.md).
## Linux
Fornecemos imagens Docker para compilar binários estáticos para Linux:
### Compilação totalmente estática baseada na `musl`
Para um binário totalmente estático que roda em qualquer distribuição Linux sem
dependências, mas não suporta carregamento dinâmico de extensões:
```console
docker buildx bake --load static-builder-musl
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-musl
```
Para melhor desempenho em cenários com alta concorrência, considere usar o
alocador [`mimalloc`](https://github.com/microsoft/mimalloc).
```console
docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl
```
### Compilação principalmente estática baseada na `glibc` (com suporte a extensões dinâmicas)
Para um binário que suporta o carregamento dinâmico de extensões PHP, mantendo
as extensões selecionadas compiladas estaticamente:
```console
docker buildx bake --load static-builder-gnu
docker cp $(docker create --name static-builder-gnu dunglas/frankenphp:static-builder-gnu):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-gnu
```
Este binário suporta todas as versões 2.17 e superiores da `glibc`, mas não roda
em sistemas baseados em `musl` (como o Alpine Linux).
O binário principalmente estático (exceto a `glibc`) resultante é chamado
`frankenphp` e está disponível no diretório atual.
Se você quiser compilar o binário estático sem o Docker, consulte as instruções
para macOS, que também funcionam para Linux.
### Extensões personalizadas
Por padrão, as extensões PHP mais populares são compiladas.
Para reduzir o tamanho do binário e a superfície de ataque, você pode escolher a
lista de extensões a serem compiladas usando o `ARG` `PHP_EXTENSIONS` do Docker.
Por exemplo, execute o seguinte comando para compilar apenas a extensão
`opcache`:
```console
docker buildx bake --load --set static-builder-musl.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder-musl
# ...
```
Para adicionar bibliotecas que habilitem funcionalidades adicionais às extensões
habilitadas, você pode passar o `ARG` `PHP_EXTENSION_LIBS` do Docker:
```console
docker buildx bake \
--load \
--set static-builder-musl.args.PHP_EXTENSIONS=gd \
--set static-builder-musl.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
static-builder-musl
```
### Módulos Caddy extras
Para adicionar módulos Caddy extras ou passar outros argumentos para o
[`xcaddy`](https://github.com/caddyserver/xcaddy), use o `ARG` `XCADDY_ARGS` do
Docker:
```console
docker buildx bake \
--load \
--set static-builder-musl.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
static-builder-musl
```
Neste exemplo, adicionamos o módulo de cache HTTP [Souin](https://souin.io) para
o Caddy, bem como os módulos
[cbrotli](https://github.com/dunglas/caddy-cbrotli),
[Mercure](https://mercure.rocks) e [Vulcain](https://vulcain.rocks).
> [!TIP]
>
> Os módulos cbrotli, Mercure e Vulcain são incluídos por padrão se
> `XCADDY_ARGS` estiver vazio ou não definido.
> Se você personalizar o valor de `XCADDY_ARGS`, deverá incluí-los
> explicitamente se desejar que sejam incluídos.
Veja também como [personalizar a compilação](#personalizando-a-compilação).
### Token do GitHub
Se você atingir o limite de taxa da API do GitHub, defina um Token de Acesso
Pessoal do GitHub em uma variável de ambiente chamada `GITHUB_TOKEN`:
```console
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl
# ...
```
## macOS
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
cd frankenphp
./build-static.sh
```
Observação: este script também funciona no Linux (e provavelmente em outros
sistemas Unix) e é usado internamente pelas imagens Docker que fornecemos.
## Personalizando a compilação
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.
- `PHP_EXTENSIONS`: as extensões PHP a serem compiladas
([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.
- `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.
- `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.
- `DEBUG_SYMBOLS`: quando definida, os símbolos de depuração não serão removidos
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.
- `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.
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.
Para isso, você pode compilar e executar o contêiner Docker
`static-builder-gnu`, acessá-lo remotamente e compilar as extensões com
`./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config`.
Passos de exemplo para [a extensão Xdebug](https://xdebug.org):
```console
docker build -t gnu-ext -f static-builder-gnu.Dockerfile --build-arg FRANKENPHP_VERSION=1.0 .
docker create --name static-builder-gnu -it gnu-ext /bin/sh
docker start static-builder-gnu
docker exec -it static-builder-gnu /bin/sh
cd /go/src/app/dist/static-php-cli/buildroot/bin
git clone https://github.com/xdebug/xdebug.git && cd xdebug
source scl_source enable devtoolset-10
../phpize
./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config
make
exit
docker cp static-builder-gnu:/go/src/app/dist/static-php-cli/buildroot/bin/xdebug/modules/xdebug.so xdebug-zts.so
docker cp static-builder-gnu:/go/src/app/dist/frankenphp-linux-$(uname -m) ./frankenphp
docker stop static-builder-gnu
docker rm static-builder-gnu
docker rmi gnu-ext
```
Isso criará `frankenphp` e `xdebug-zts.so` no diretório atual.
Se você mover `xdebug-zts.so` para o diretório de extensões, adicione
`zend_extension=xdebug-zts.so` ao seu `php.ini` e execute o FrankenPHP, ele
carregará o Xdebug.

203
docs/pt-br/worker.md Normal file
View File

@@ -0,0 +1,203 @@
# Usando workers do FrankenPHP
Inicialize sua aplicação uma vez e mantenha-a na memória.
O FrankenPHP processará as requisições recebidas em poucos milissegundos.
## Iniciando worker scripts
### Docker
Defina o valor da variável de ambiente `FRANKENPHP_CONFIG` como
`worker /caminho/para/seu/script/worker.php`:
```console
docker run \
-e FRANKENPHP_CONFIG="worker /app/caminho/para/seu/script/worker.php" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
### Binário independente
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
```
Se a sua aplicação PHP estiver [embutida no binário](embed.md), você pode
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)
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"
```
## Tempo de execução do Symfony
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):
```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:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-e APP_RUNTIME=Runtime\\FrankenPhpSymfony\\Runtime \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
## Laravel Octane
Consulte [a documentação dedicada](laravel.md#laravel-octane).
## Aplicações personalizadas
O exemplo a seguir mostra como criar seu próprio worker script sem depender de
uma biblioteca de terceiros:
```php
<?php
// public/index.php
// Impede o encerramento do worker script quando uma conexão do cliente for
// interrompida
ignore_user_abort(true);
// Inicializa sua aplicação
require __DIR__.'/vendor/autoload.php';
$myApp = new \App\Kernel();
$myApp->boot();
// Manipulador fora do loop para melhor desempenho (fazendo menos trabalho)
$handler = static function () use ($myApp) {
// Chamado quando uma requisição é recebida,
// superglobals, php://input e similares são redefinidos
echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER);
};
$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
$keepRunning = \frankenphp_handle_request($handler);
// Faz algo depois de enviar a resposta HTTP
$myApp->terminate();
// Chama o coletor de lixo para reduzir as chances de ele ser acionado no
// meio da geração de uma página
gc_collect_cycles();
if (!$keepRunning) break;
}
// Limpeza
$myApp->shutdown();
```
Em seguida, inicie sua aplicação e use a variável de ambiente
`FRANKENPHP_CONFIG` para configurar seu worker:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
Por padrão, 2 workers por CPU são iniciados.
Você também pode configurar o número de workers a serem iniciados:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php 42" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
### Reiniciar o worker após um certo número de requisições
Como o PHP não foi originalmente projetado para processos de longa duração,
ainda existem muitas bibliotecas e códigos legados que vazam memória.
Uma solução alternativa para usar esse tipo de código no modo worker é reiniciar
o worker script após processar um certo número de requisições:
O trecho de código de worker anterior permite configurar um número máximo de
requisições a serem processadas, definindo uma variável de ambiente chamada
`MAX_REQUESTS`.
### Reiniciar os workers manualmente
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
[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
endpoint de reinicialização com uma simples requisição POST como esta:
```console
curl -X POST http://localhost:2019/frankenphp/workers/restart
```
### Falhas de worker
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.
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).
## Comportamento das superglobais
As
[superglobais do PHP](https://www.php.net/manual/pt_BR/language.variables.superglobals.php)
(`$_SERVER`, `$_ENV`, `$_GET`...) se comportam da seguinte maneira:
- antes da primeira chamada para `frankenphp_handle_request()`, as superglobais
contêm valores vinculados ao próprio worker script.
- durante e após a chamada para `frankenphp_handle_request()`, as superglobais
contêm valores gerados a partir da requisição HTTP processada.
Cada chamada para `frankenphp_handle_request()` altera os valores das
superglobais.
Para acessar as superglobais do worker script dentro do retorno de chamada, você
deve copiá-las e importar a cópia para o escopo do retorno de chamada:
```php
<?php
// Copia a superglobal $_SERVER do worker antes da primeira chamada para
// frankenphp_handle_request()
$workerServer = $_SERVER;
$handler = static function () use ($workerServer) {
var_dump($_SERVER); // $_SERVER vinculada à requisição
var_dump($workerServer); // $_SERVER do worker script
};
// ...
```

80
docs/pt-br/x-sendfile.md Normal file
View File

@@ -0,0 +1,80 @@
# Servindo arquivos estáticos grandes com eficiência (`X-Sendfile`/`X-Accel-Redirect`)
Normalmente, arquivos estáticos podem ser servidos diretamente pelo servidor
web, mas às vezes é necessário executar algum código PHP antes de enviá-los:
controle de acesso, estatísticas, cabeçalhos HTTP personalizados...
Infelizmente, usar PHP para servir arquivos estáticos grandes é ineficiente em
comparação com o uso direto do servidor web (sobrecarga de memória, desempenho
reduzido...).
O FrankenPHP permite delegar o envio de arquivos estáticos ao servidor web
**após** a execução de 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.
O FrankenPHP cuida do resto.
Esse recurso é conhecido como **`X-Sendfile`** para Apache e
**`X-Accel-Redirect`** para NGINX.
Nos exemplos a seguir, assumimos que o diretório raiz do projeto é o diretório
`public/` e que queremos usar PHP para servir arquivos armazenados fora do
diretório `public/`, de um diretório chamado `arquivos-privados/`.
## Configuração
Primeiro, adicione a seguinte configuração ao seu `Caddyfile` para habilitar
este recurso:
```patch
root public/
# ...
+ # Necessário para Symfony, Laravel e outros projetos que usam o componente
+ # Symfony HttpFoundation
+ request_header X-Sendfile-Type x-accel-redirect
+ request_header X-Accel-Mapping ../arquivos-privados=/arquivos-privados
+
+ intercept {
+ @accel header X-Accel-Redirect *
+ handle_response @accel {
+ root arquivos-privados/
+ rewrite * {resp.header.X-Accel-Redirect}
+ method * GET
+
+ # Remove o cabeçalho X-Accel-Redirect definido pelo PHP para maior
+ # segurança
+ header -X-Accel-Redirect
+
+ file_server
+ }
+ }
php_server
```
## PHP simples
Defina o caminho relativo do arquivo (de `arquivos-privados/`) como o valor do
cabeçalho `X-Accel-Redirect`:
```php
header('X-Accel-Redirect: arquivo.txt');
```
## Projetos que utilizam o componente Symfony HttpFoundation (Symfony, Laravel, Drupal...)
Symfony HttpFoundation
[suporta nativamente este recurso](https://symfony.com/doc/current/components/http_foundation.html#serving-files).
Ele determinará automaticamente o valor correto para o cabeçalho
`X-Accel-Redirect` e o adicionará à resposta.
```php
use Symfony\Component\HttpFoundation\BinaryFileResponse;
BinaryFileResponse::trustXSendfileTypeHeader();
$response = new BinaryFileResponse(__DIR__.'/../arquivos-privados/arquivo.txt');
// ...
```