# O ciclo de vida do pedido: arranque, execução, shutdown

> O ciclo de vida do pedido PHP explicado: MINIT, RINIT, RSHUTDOWN, MSHUTDOWN, reutilização de workers e por que razão a CVE-2026-109619 atua precisamente aqui.

Fonte: https://phpsos.de/pt/php/request-lebenszyklus/  
Atualizado em: 25/10/2026  
Prestador: PHP SOS, Assistência de emergência PHP e segurança de servidores. Ajuda imediata por WhatsApp: https://wa.me/491736494914?text=Ol%C3%A1%2C%20PHP%20SOS%2C%20precisamos%20de%20ajuda%20com%20o%20nosso%20servidor%20PHP.%20Empresa%3A%20%E2%80%A6%20Site%3A%20%E2%80%A6

**Em resumo:** O ciclo de vida do pedido PHP tem dois níveis. Uma vez por processo, o PHP carrega as suas extensões (fase MINIT) e desmonta-as no fim (MSHUTDOWN). Para cada pedido individual há uma fase própria de arranque e de limpeza (RINIT e RSHUTDOWN). Na operação web, um worker trata muitos pedidos em sequência, pelo que o PHP tem de repor de forma fiável todo o estado do pedido entre dois pedidos. Foi precisamente nesta fronteira que esteve a CVE-2026-109619, a segunda vulnerabilidade do PHP2Shell.

## Dois níveis: o processo e o pedido

Para quem não é técnico, ajuda uma imagem: um worker PHP é como um balcão numa repartição. O balcão é montado uma vez de manhã, com formulários, carimbos e computador. Depois chegam, um após o outro, muitos visitantes. Após cada visitante, a mesa tem de ficar vazia, para que o seguinte não veja documentos alheios.

O PHP funciona da mesma maneira. No arranque de um processo, carrega extensões e definições que são iguais para todos os pedidos. Cada pedido recebe depois a sua própria memória, as suas próprias variáveis e os seus próprios dados de pedido, como `$_GET`, `$_POST` ou `$_FILES`. No fim do pedido, o PHP liberta essa memória. O PHP Internals Book resume-o de forma concisa: o PHP esquece a memória do pedido no fim de cada pedido.

## As fases do ciclo de vida do pedido PHP em resumo

Os nomes vêm do interior do PHP; cada extensão pode ligar-se a estas fases.

| Fase | Quando corre | Com que frequência | Tarefa típica |
|---|---|---|---|
| MINIT (Module Init) | no arranque do processo | uma vez por processo | registar extensões, criar entradas ini, dados permanentes |
| GINIT | ao criar as globais | por processo, em threads por thread | preparar variáveis globais de uma extensão |
| RINIT (Request Init) | no início de cada pedido | por pedido | preparar a memória e o estado do pedido |
| Execução | entretanto | por pedido | o seu código PHP corre |
| RSHUTDOWN | no fim de cada pedido | por pedido | libertar recursos do pedido |
| PRSHUTDOWN | após o RSHUTDOWN | por pedido, raramente usada | últimos trabalhos de limpeza |
| MSHUTDOWN | ao terminar o processo | uma vez por processo | libertar dados permanentes |

Uma frase importante do PHP Internals Book para quem desenvolve extensões: as globais não são esvaziadas automaticamente após cada pedido. O que tem de estar fresco em cada pedido deve ser reposto no RINIT. Esta regra mostra por que razão a fronteira entre dois pedidos é um ponto crítico para a segurança.

No fim de um pedido, a ordem também está definida: primeiro correm as funções de shutdown que registou com `register_shutdown_function()`, depois os destrutores, depois o buffer de saída é esvaziado. Só depois se segue o RSHUTDOWN.

## Porque é que os workers são reutilizados

Na linha de comandos, um processo trata exatamente um pedido e termina. Na operação web seria demasiado lento. O PHP-FPM, o Apache com mod_php, o LiteSpeed e o FrankenPHP mantêm por isso workers prontos, que atendem muitos pedidos seguidos. O MINIT corre então uma vez, o RINIT e o RSHUTDOWN milhares de vezes. Como o FPM controla os seus workers explica-o [PHP-FPM explicado](https://phpsos.de/pt/php/php-fpm-erklaert/).

No PHP-FPM, o `pm.max_requests` define após quantos pedidos um worker reinicia. A predefinição é `0`, ou seja, ilimitado. O manual indica a definição como meio contra fugas de memória em bibliotecas de terceiros:

```ini
; pool FPM: reiniciar o worker após 500 pedidos (contra fugas de memória)
pm = dynamic
pm.max_children = 20
pm.max_requests = 500
```

O modo worker do FrankenPHP vai ainda mais longe: aí, a aplicação inteira arranca uma vez e permanece em memória. O estado da aplicação sobrevive então também ao nível do PHP. O exemplo mostra a diferença:

```php
<?php
function anfrageZaehler(): int
{
    static $zaehler = 0;   // na operação clássica com FPM, volta a 0 em cada pedido
    return ++$zaehler;     // no modo worker, o valor continua a contar ao longo dos pedidos
}
echo anfrageZaehler();
```

Na operação clássica, a chamada imprime sempre 1, porque o PHP descarta a memória do pedido. No modo worker, o valor cresce enquanto o worker viver. A documentação do FrankenPHP aconselha por isso a repor deliberadamente, entre pedidos, o estado ligado ao pedido.

## Onde atua a CVE-2026-109619

A CVE-2026-109619 não diz respeito ao código da sua aplicação, mas ao próprio PHP. Segundo o php.net, o estado interno pertencente ao pedido podia sobreviver à libertação da sua memória. Um pedido posterior no mesmo worker podia usá-lo antes de a inicialização o repor. Na imagem: na mesa do balcão ficou um papel que já devia ter sido arrumado. Não publicamos deliberadamente detalhes de exploração; o enquadramento está em [CVE-2026-109619](https://phpsos.de/pt/php2shell/cve-2026-109619/).

As medidas provisórias do php.net para esta vulnerabilidade atuam sobre caminhos confirmados e não são uma proteção completa:

1. `session.upload_progress.enabled=0` no php.ini, no pool FPM ou na configuração do servidor, depois reiniciar todos os workers; não através de `ini_set()` nem de `.user.ini`.
2. No PHP 8.5, adicionalmente `fatal_error_backtraces=0` e reinício.
3. Remover, em cada pedido, definições inválidas ou obsoletas que geram mensagens antes da inicialização. Segundo o php.net, apenas ocultar as mensagens não ajuda.
4. Corrigir na aplicação os erros fatais e as expirações de tempo que podem ser desencadeados do exterior, também em callbacks, no processamento de uploads e de importações.

O remédio verdadeiro é a atualização para o PHP 8.5.12, 8.4.27, 8.3.36 ou 8.2.35. Como o erro está no worker em execução, só produz efeito após o reinício de todos os processos; os passos estão em [Reiniciar o PHP-FPM e todos os workers](https://phpsos.de/pt/php2shell/php-fpm-neustart/).

> **Dica:**
> Depois do reinício, verifique com `ps -eo pid,lstart,cmd | grep php` se todos os processos PHP foram iniciados depois do momento da atualização.

## O que o ciclo de vida significa para a operação e o desenvolvimento

Do modelo podem deduzir-se regras claras. Para administradores: uma atualização só é eficaz quando já não corre nenhum processo com código antigo. Cada modo de operação com workers de longa duração precisa de um reinício documentado. Para programadores: o código que corre em modos worker não pode deixar estado ligado ao pedido em variáveis estáticas ou singletons, por exemplo dados de utilizadores ou permissões. Para ambos vale: conservar os registos a partir de 20/10/2026, para poder avaliar mais tarde se algo aconteceu antes do patch.

## Como ajuda concretamente a PHP SOS?

Com o [serviço de patches PHP](https://phpsos.de/pt/leistungen/php-patch-service/), aplicamos o modelo na prática:

- **Determinar o modo de operação:** verificamos se corre PHP-FPM, mod_php, LiteSpeed, FrankenPHP ou php-cgi e como os workers são geridos.
- **Atualização e reinício:** aplicamos a versão corrigida e reiniciamos cada worker de forma controlada.
- **Verificação dos processos:** comprovamos, com base nas horas de arranque e nas versões, que não resta nenhum processo antigo.
- **Verificar os modos worker:** nas aplicações de longa duração, examinamos se o estado ligado ao pedido é reposto corretamente.


## Perguntas frequentes

**O PHP inicia um processo novo para cada pedido?**  
Na linha de comandos, sim; na operação web, geralmente não. O PHP-FPM, o Apache com mod_php, o LiteSpeed ou o FrankenPHP deixam um processo tratar muitos pedidos em sequência. Entre pedidos, o PHP limpa o estado do pedido.

**Qual é a diferença entre MINIT e RINIT?**  
O MINIT corre uma só vez, quando o processo PHP arranca, e cria estruturas permanentes. O RINIT corre no início de cada pedido individual e prepara o que só esse pedido precisa.

**O pm.max_requests ajuda contra a CVE-2026-109619?**  
O php.net não indica reinícios mais frequentes dos workers como medida provisória. A definição limita apenas quantos pedidos um worker trata antes de reiniciar. Contra a vulnerabilidade ajuda a atualização, seguida do reinício de todos os workers.

**O que é o modo worker do FrankenPHP?**  
No modo worker, a aplicação arranca uma só vez e permanece em memória. Poupa tempo, mas as variáveis estáticas, as globais e as caches mantêm-se entre pedidos. Segundo a documentação do FrankenPHP, o estado ligado ao pedido tem por isso de ser reposto deliberadamente.

## Fontes

- [php.net: aviso de segurança de 21/10/2026](https://www.php.net/)
- [Manual do PHP: configuração do FPM](https://www.php.net/manual/en/install.fpm.configuration.php)
- [PHP Internals Book: ciclo de vida do PHP](https://www.phpinternalsbook.com/)
