# Životni ciklus zahtjeva: pokretanje, izvršavanje, gašenje

> PHP Request Lifecycle objašnjen: MINIT, RINIT, RSHUTDOWN, MSHUTDOWN, ponovna uporaba workera i zašto CVE-2026-109619 cilja upravo ovdje.

Izvor: https://phpsos.de/hr/php/request-lebenszyklus/  
Stanje: 25.10.2026  
Pružatelj: PHP SOS, PHP hitna pomoć i sigurnost poslužitelja. Hitna pomoć putem WhatsAppa: https://wa.me/491736494914?text=Pozdrav%20PHP%20SOS%2C%20trebamo%20pomo%C4%87%20s%20na%C5%A1im%20PHP%20poslu%C5%BEiteljem.%20Tvrtka%3A%20%E2%80%A6%20Web-stranica%3A%20%E2%80%A6

**Ukratko:** PHP Request Lifecycle ima dvije razine. Jednom po procesu PHP učitava svoje ekstenzije (faza MINIT) i na kraju ih ponovno ruši (MSHUTDOWN). Za svaki pojedini zahtjev postoji zasebna faza pokretanja i čišćenja (RINIT i RSHUTDOWN). U web-radu jedan worker obrađuje mnogo zahtjeva jedan za drugim, pa PHP između dva zahtjeva mora pouzdano vratiti na početno sva stanja zahtjeva. Upravo na toj granici nalazila se CVE-2026-109619, druga ranjivost PHP2Shella.

## Dvije razine: proces i zahtjev

Za ljude koji nisu tehničari pomaže slika: PHP worker je poput šaltera u uredu. Šalter se ujutro jednom opremi obrascima, pečatima i računalom. Nakon toga dolaze redom brojni posjetitelji. Nakon svakog posjetitelja stol mora biti prazan kako sljedeći ne bi vidio tuđe dokumente.

PHP radi isto tako. Pri pokretanju procesa učitava ekstenzije i postavke koje su jednake za sve zahtjeve. Svaki zahtjev zatim dobiva vlastitu memoriju, vlastite varijable i vlastite podatke zahtjeva poput `$_GET`, `$_POST` ili `$_FILES`. Na kraju zahtjeva PHP tu memoriju oslobađa. PHP Internals Book to sažima kratko: PHP na kraju svakog zahtjeva zaboravlja memoriju zahtjeva.

## Faze PHP Request Lifecyclea u pregledu

Nazivi potječu iz unutrašnjosti PHP-a; svaka se ekstenzija može uključiti u te faze.

| Faza | Kada se izvršava | Koliko često | Tipična zadaća |
|---|---|---|---|
| MINIT (Module Init) | pri pokretanju procesa | jednom po procesu | registrirati ekstenzije, stvoriti ini unose, trajni podaci |
| GINIT | pri stvaranju globalnih varijabli | po procesu, kod threadova po threadu | pripremiti globalne varijable ekstenzije |
| RINIT (Request Init) | na početku svakog zahtjeva | po zahtjevu | pripremiti memoriju i stanje zahtjeva |
| Izvršavanje | između | po zahtjevu | izvodi se Vaš PHP kod |
| RSHUTDOWN | na kraju svakog zahtjeva | po zahtjevu | osloboditi resurse zahtjeva |
| PRSHUTDOWN | nakon RSHUTDOWN | po zahtjevu, rijetko korišteno | posljednji radovi čišćenja |
| MSHUTDOWN | pri završetku procesa | jednom po procesu | osloboditi trajne podatke |

Važna rečenica iz PHP Internals Booka za razvojne programere ekstenzija: globalne varijable ne prazne se automatski nakon svakog zahtjeva. Što mora biti svježe po zahtjevu, treba vratiti na početno u RINIT-u. Ovo pravilo pokazuje zašto je granica između dva zahtjeva sigurnosno kritično mjesto.

Na kraju zahtjeva redoslijed je također utvrđen: prvo se izvršavaju shutdown funkcije koje ste registrirali s `register_shutdown_function()`, zatim destruktori, potom se prazni međuspremnik izlaza. Tek nakon toga slijedi RSHUTDOWN.

## Zašto se workeri ponovno koriste

U naredbenom retku jedan proces obrađuje točno jedan zahtjev i završava. U web-radu bi to bilo presporo. PHP-FPM, Apache s mod_php-om, LiteSpeed i FrankenPHP stoga drže workere koji poslužuju mnogo zahtjeva zaredom. MINIT se tada izvršava jednom, a RINIT i RSHUTDOWN tisućama puta. Kako FPM upravlja svojim workerima, objašnjava [PHP-FPM objašnjen](https://phpsos.de/hr/php/php-fpm-erklaert/).

U PHP-FPM-u `pm.max_requests` određuje nakon koliko se zahtjeva worker ponovno pokreće. Zadano je `0`, dakle neograničeno. Priručnik tu postavku navodi kao sredstvo protiv curenja memorije u bibliotekama trećih strana:

```ini
; FPM pool: ponovno pokrenuti worker nakon 500 zahtjeva (protiv curenja memorije)
pm = dynamic
pm.max_children = 20
pm.max_requests = 500
```

Još dalje ide worker mod FrankenPHP-a: ondje se cijela aplikacija pokrene jednom i ostaje u memoriji. Stanje aplikacije tada preživljava i na razini PHP-a. Primjer pokazuje razliku:

```php
<?php
function anfrageZaehler(): int
{
    static $zaehler = 0;   // u klasičnom FPM radu po zahtjevu opet 0
    return ++$zaehler;     // u worker modu vrijednost nastavlja rasti kroz zahtjeve
}
echo anfrageZaehler();
```

U klasičnom radu poziv uvijek ispisuje 1 jer PHP odbacuje memoriju zahtjeva. U worker modu vrijednost raste dok worker živi. FrankenPHP dokumentacija zato savjetuje svjesno vraćanje stanja vezanog uz zahtjev na početno između zahtjeva.

## Gdje CVE-2026-109619 cilja

CVE-2026-109619 ne tiče se koda Vaše aplikacije, nego samog PHP-a. Prema php.net interno stanje koje pripada zahtjevu moglo je preživjeti oslobađanje svoje memorije. Kasniji zahtjev u istom workeru mogao ga je upotrijebiti prije nego što ga inicijalizacija vrati na početno. U slici: na stolu šaltera ostao je listić koji je zapravo već trebao biti sklonjen. Pojedinosti iskorištavanja namjerno ne objavljujemo; razvrstavanje se nalazi na stranici [CVE-2026-109619](https://phpsos.de/hr/php2shell/cve-2026-109619/).

Prijelazne mjere php.net za ovu ranjivost ciljaju potvrđene putove i nisu potpuna zaštita:

1. `session.upload_progress.enabled=0` u php.ini, FPM poolu ili konfiguraciji poslužitelja, zatim ponovno pokrenuti sve workere; ne putem `ini_set()` ili `.user.ini`.
2. Na PHP-u 8.5 dodatno `fatal_error_backtraces=0` i ponovno pokretanje.
3. Ukloniti nevaljane ili zastarjele postavke po zahtjevu koje prije inicijalizacije stvaraju poruke. Samo sakrivanje poruka prema php.net ne pomaže.
4. U aplikaciji otkloniti izvana izazvane fatalne pogreške i prekoračenja vremena, također u callbackovima te u obradi uploada i uvoza.

Pravi je lijek ažuriranje na PHP 8.5.12, 8.4.27, 8.3.36 ili 8.2.35. Budući da je pogreška u radnom workeru, djeluje tek nakon ponovnog pokretanja svih procesa; koraci su na stranici [Ponovno pokretanje PHP-FPM-a i svih workera](https://phpsos.de/hr/php2shell/php-fpm-neustart/).

> **Savjet:**
> Nakon ponovnog pokretanja provjerite s `ps -eo pid,lstart,cmd | grep php` jesu li svi PHP procesi pokrenuti nakon trenutka ažuriranja.

## Što životni ciklus znači za rad i razvoj

Iz modela se mogu izvesti jasna pravila. Za administratore: ažuriranje je djelotvorno tek kad više ne radi nijedan proces sa starim kodom. Svaki način rada s dugovječnim workerima treba dokumentirano ponovno pokretanje. Za programere: kod koji se izvodi u worker modovima ne smije ostavljati stanje vezano uz zahtjev u statičkim varijablama ili singletonima, primjerice podatke o korisnicima ili ovlasti. Za oboje vrijedi: sačuvati zapisnike od 20. 10. 2026. kako bi se kasnije moglo procijeniti je li se prije zakrpe nešto dogodilo.

## Kako PHP SOS konkretno pomaže?

Pomoću [PHP patch servisa](https://phpsos.de/hr/leistungen/php-patch-service/) model provodimo u praksi:

- **Odrediti način rada:** Utvrđujemo radi li PHP-FPM, mod_php, LiteSpeed, FrankenPHP ili php-cgi i kako se upravlja workerima.
- **Ažuriranje i ponovno pokretanje:** Instaliramo ispravljenu verziju i kontrolirano ponovno pokrećemo svakog workera.
- **Provjera procesa:** Na temelju vremena pokretanja i verzija dokazujemo da nije ostao nijedan stari proces.
- **Provjeriti worker modove:** Kod dugovječnih aplikacija pregledavamo vraća li se stanje vezano uz zahtjev ispravno na početno.


## Česta pitanja

**Pokreće li PHP novi proces za svaki zahtjev?**  
U naredbenom retku da, u web-radu uglavnom ne. PHP-FPM, Apache s mod_php-om, LiteSpeed ili FrankenPHP puštaju da jedan proces obrađuje mnogo zahtjeva jedan za drugim. Između zahtjeva PHP čisti stanje zahtjeva.

**Koja je razlika između MINIT i RINIT?**  
MINIT se izvršava jednom, kad se PHP proces pokrene, i stvara trajne strukture. RINIT se izvršava na početku svakog pojedinog zahtjeva i priprema ono što treba samo tom zahtjevu.

**Pomaže li pm.max_requests protiv CVE-2026-109619?**  
php.net češća ponovna pokretanja workera ne navodi kao prijelaznu mjeru. Postavka samo ograničava koliko zahtjeva worker obradi prije ponovnog pokretanja. Protiv ranjivosti pomaže ažuriranje uz naknadno ponovno pokretanje svih workera.

**Što je Worker Mode u FrankenPHP-u?**  
U worker modu aplikacija se pokrene jednom i ostaje u memoriji. To štedi vrijeme, ali statičke varijable, globalne varijable i cacheovi ostaju sačuvani između zahtjeva. Prema FrankenPHP dokumentaciji stanje vezano uz zahtjev zato se mora svjesno vraćati na početno.

## Izvori

- [php.net: Sigurnosna obavijest od 21. 10. 2026.](https://www.php.net/)
- [PHP priručnik: FPM konfiguracija](https://www.php.net/manual/en/install.fpm.configuration.php)
- [PHP Internals Book: Životni ciklus PHP-a](https://www.phpinternalsbook.com/)
