# Životní cyklus požadavku: start, provedení, shutdown

> Životní cyklus požadavku PHP vysvětlen: MINIT, RINIT, RSHUTDOWN, MSHUTDOWN, opětovné využití workerů a proč CVE-2026-109619 míří právě sem.

Zdroj: https://phpsos.de/cs/php/request-lebenszyklus/  
Stav: 25.10.2026  
Poskytovatel: PHP SOS, Nouzová pomoc pro PHP a bezpečnost serverů. Okamžitá pomoc přes WhatsApp: https://wa.me/491736494914?text=Dobr%C3%BD%20den%2C%20PHP%20SOS%2C%20pot%C5%99ebujeme%20pomoc%20s%20na%C5%A1%C3%ADm%20PHP%20serverem.%20Firma%3A%20%E2%80%A6%20Web%3A%20%E2%80%A6

**Stručně:** Životní cyklus požadavku PHP má dvě úrovně. Jednou za proces PHP načte svá rozšíření (fáze MINIT) a nakonec je zase odbourá (MSHUTDOWN). Pro každý jednotlivý požadavek existuje vlastní fáze startu a úklidu (RINIT a RSHUTDOWN). Ve webovém provozu vyřizuje worker mnoho požadavků po sobě, proto musí PHP mezi dvěma požadavky spolehlivě vynulovat veškerý stav požadavku. Právě na této hranici ležela CVE-2026-109619, druhá zranitelnost PHP2Shell.

## Dvě úrovně: proces a požadavek

Pro netechniky pomůže přirovnání: worker PHP je jako přepážka na úřadě. Přepážka se ráno jednou zařídí, s formuláři, razítky a počítačem. Poté přicházejí postupně mnozí návštěvníci. Po každém návštěvníkovi musí být stůl prázdný, aby další neviděl cizí podklady.

PHP funguje stejně. Při startu procesu načte rozšíření a nastavení, která jsou pro všechny požadavky stejná. Každý požadavek pak dostane vlastní paměť, vlastní proměnné a vlastní data požadavku, jako jsou `$_GET`, `$_POST` nebo `$_FILES`. Na konci požadavku PHP tuto paměť uvolní. PHP Internals Book to shrnuje stručně: PHP na konci každého požadavku zapomene paměť požadavku.

## Fáze životního cyklu požadavku PHP v přehledu

Názvy pocházejí z nitra PHP; každé rozšíření se může do těchto fází zapojit.

| Fáze | Kdy probíhá | Jak často | Typický úkol |
|---|---|---|---|
| MINIT (Module Init) | při startu procesu | jednou na proces | registrovat rozšíření, zřídit položky ini, trvalá data |
| GINIT | při vytváření globálních proměnných | na proces, u vláken na vlákno | připravit globální proměnné rozšíření |
| RINIT (Request Init) | na začátku každého požadavku | na požadavek | připravit paměť a stav požadavku |
| Provedení | mezitím | na požadavek | běží váš kód PHP |
| RSHUTDOWN | na konci každého požadavku | na požadavek | uvolnit zdroje požadavku |
| PRSHUTDOWN | po RSHUTDOWN | na požadavek, používá se zřídka | poslední úklidové práce |
| MSHUTDOWN | při ukončení procesu | jednou na proces | uvolnit trvalá data |

Důležitá věta z PHP Internals Book pro vývojáře rozšíření: globální proměnné se po každém požadavku automaticky nevyprazdňují. Co musí být pro každý požadavek čerstvé, patří do RINIT k resetování. Toto pravidlo ukazuje, proč je hranice mezi dvěma požadavky bezpečnostně kritickým místem.

Na konci požadavku je pořadí rovněž pevně dané: nejdřív proběhnou shutdown funkce, které jste zaregistrovali pomocí `register_shutdown_function()`, poté destruktory, poté se vyprázdní výstupní buffer. Teprve potom následuje RSHUTDOWN.

## Proč se workery používají opakovaně

Na příkazové řádce vyřídí proces právě jeden požadavek a skončí. Ve webovém provozu by to bylo příliš pomalé. PHP-FPM, Apache s mod_php, LiteSpeed a FrankenPHP proto drží workery, které obsluhují mnoho požadavků za sebou. MINIT pak proběhne jednou, RINIT a RSHUTDOWN tisíckrát. Jak FPM své workery řídí, vysvětluje stránka [PHP-FPM vysvětleno](https://phpsos.de/cs/php/php-fpm-erklaert/).

V PHP-FPM určuje `pm.max_requests`, po kolika požadavcích se worker restartuje. Výchozí hodnota je `0`, tedy neomezeně. Příručka toto nastavení uvádí jako prostředek proti únikům paměti v knihovnách třetích stran:

```ini
; pool FPM: restartovat worker po 500 požadavcích (proti únikům paměti)
pm = dynamic
pm.max_children = 20
pm.max_requests = 500
```

Ještě dál jde režim worker u FrankenPHP: tam se celá aplikace spustí jednou a zůstává v paměti. Stav aplikace pak přežívá i na úrovni PHP. Příklad ukazuje rozdíl:

```php
<?php
function anfrageZaehler(): int
{
    static $zaehler = 0;   // v klasickém provozu FPM je na každý požadavek opět 0
    return ++$zaehler;     // v režimu worker hodnota počítá dál napříč požadavky
}
echo anfrageZaehler();
```

V klasickém provozu vypíše volání vždy 1, protože PHP paměť požadavku zahodí. V režimu worker hodnota roste, dokud worker žije. Dokumentace FrankenPHP proto radí stav vázaný na požadavek mezi požadavky vědomě resetovat.

## Kde CVE-2026-109619 míří

CVE-2026-109619 se netýká vašeho aplikačního kódu, ale samotného PHP. Podle php.net mohl interní stav patřící k požadavku přežít uvolnění své paměti. Pozdější požadavek ve stejném workeru jej mohl využít dříve, než jej inicializace vynulovala. V obrazu: na stole přepážky zůstal lístek, který měl být už dávno uklizen. Podrobnosti zneužití záměrně nezveřejňujeme; zařazení najdete v [CVE-2026-109619](https://phpsos.de/cs/php2shell/cve-2026-109619/).

Přechodná opatření php.net k této zranitelnosti míří na potvrzené cesty, nejsou úplnou ochranou:

1. `session.upload_progress.enabled=0` v php.ini, poolu FPM nebo konfiguraci serveru, poté restartovat všechny workery; ne přes `ini_set()` nebo `.user.ini`.
2. U PHP 8.5 navíc `fatal_error_backtraces=0` a restart.
3. Odstranit neplatná nebo zastaralá nastavení pro jednotlivé požadavky, která před inicializací vyvolávají hlášení. Pouhé skrytí hlášení podle php.net nepomáhá.
4. V aplikaci odstranit zvenčí vyvolatelné fatální chyby a překročení časových limitů, i v callbacích, při zpracování nahrávání a importů.

Skutečnou nápravou je aktualizace na PHP 8.5.12, 8.4.27, 8.3.36 nebo 8.2.35. Protože chyba sídlí v běžícím workeru, projeví se až po restartu všech procesů; kroky najdete v článku [Restart PHP-FPM a všech workerů](https://phpsos.de/cs/php2shell/php-fpm-neustart/).

> **Tip:**
> Po restartu zkontrolujte pomocí `ps -eo pid,lstart,cmd | grep php`, zda byly všechny procesy PHP spuštěny po okamžiku aktualizace.

## Co životní cyklus znamená pro provoz a vývoj

Z modelu lze odvodit jasná pravidla. Pro administrátory: aktualizace je účinná teprve tehdy, když neběží žádný proces se starým kódem. Každý provozní režim s dlouhověkými workery potřebuje zdokumentovaný restart. Pro vývojáře: kód, který běží v režimech worker, nesmí nechat stav vázaný na požadavek ve statických proměnných nebo singletonech, například uživatelská data nebo oprávnění. Pro oba platí: uchovejte logy od 20. 10. 2026, abyste mohli později posoudit, zda se před záplatou něco nestalo.

## Jak konkrétně pomáhá PHP SOS?

Pomocí [služby záplatování PHP](https://phpsos.de/cs/leistungen/php-patch-service/) model převádíme do praxe:

- **Určit provozní režim:** Zjistíme, zda běží PHP-FPM, mod_php, LiteSpeed, FrankenPHP nebo php-cgi a jak se workery spravují.
- **Aktualizace a restart:** Nasadíme opravenou verzi a řízeně restartujeme každého workera.
- **Kontrola procesů:** Podle časů startu a verzí doložíme, že nezůstal žádný starý proces.
- **Prověřit režimy worker:** U dlouho běžících aplikací se podíváme, zda se stav vázaný na požadavek čistě resetuje.


## Časté dotazy

**Spouští PHP pro každý požadavek nový proces?**  
Na příkazové řádce ano, ve webovém provozu většinou ne. PHP-FPM, Apache s mod_php, LiteSpeed nebo FrankenPHP nechají jeden proces vyřizovat mnoho požadavků po sobě. Mezi požadavky PHP stav požadavku uklidí.

**Jaký je rozdíl mezi MINIT a RINIT?**  
MINIT proběhne jednou při startu procesu PHP a vytvoří trvalé struktury. RINIT proběhne na začátku každého jednotlivého požadavku a připraví to, co potřebuje jen tento požadavek.

**Pomáhá pm.max_requests proti CVE-2026-109619?**  
php.net častější restarty workerů jako přechodné opatření neuvádí. Nastavení omezuje pouze to, kolik požadavků worker vyřídí, než se restartuje. Proti zranitelnosti pomáhá aktualizace s následným restartem všech workerů.

**Co je režim worker u FrankenPHP?**  
V režimu worker se aplikace spustí jednou a zůstává v paměti. To šetří čas, ale statické proměnné, globální proměnné a cache zůstávají mezi požadavky zachovány. Podle dokumentace FrankenPHP je proto nutné stav vázaný na požadavek vědomě resetovat.

## Zdroje

- [php.net: Bezpečnostní oznámení z 21. 10. 2026](https://www.php.net/)
- [Příručka PHP: Konfigurace FPM](https://www.php.net/manual/en/install.fpm.configuration.php)
- [PHP Internals Book: Životní cyklus PHP](https://www.phpinternalsbook.com/)
