# Жизненият цикъл на заявката: старт, изпълнение, shutdown

> PHP Request Lifecycle, обяснен: MINIT, RINIT, RSHUTDOWN, MSHUTDOWN, повторно използване на worker процеси и защо CVE-2026-109619 засяга точно тук.

Източник: https://phpsos.de/bg/php/request-lebenszyklus/  
Актуалност: 25.10.2026  
Доставчик: PHP SOS, Спешна помощ за PHP и сигурност на сървъри. Незабавна помощ по WhatsApp: https://wa.me/491736494914?text=%D0%97%D0%B4%D1%80%D0%B0%D0%B2%D0%B5%D0%B9%D1%82%D0%B5%2C%20PHP%20SOS%2C%20%D0%B8%D0%BC%D0%B0%D0%BC%D0%B5%20%D0%BD%D1%83%D0%B6%D0%B4%D0%B0%20%D0%BE%D1%82%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%20%D1%81%20%D0%BD%D0%B0%D1%88%D0%B8%D1%8F%20PHP%20%D1%81%D1%8A%D1%80%D0%B2%D1%8A%D1%80.%20%D0%A4%D0%B8%D1%80%D0%BC%D0%B0%3A%20%E2%80%A6%20%D0%A3%D0%B5%D0%B1%D1%81%D0%B0%D0%B9%D1%82%3A%20%E2%80%A6

**Накратко:** Жизненият цикъл на заявката (PHP Request Lifecycle) има две нива. Веднъж за процес PHP зарежда своите разширения (фаза MINIT) и накрая ги демонтира (MSHUTDOWN). За всяка отделна заявка има собствена фаза на старт и на почистване (RINIT и RSHUTDOWN). В уеб режим един worker обработва много заявки последователно, затова между две заявки PHP трябва надеждно да нулира цялото състояние на заявката. Именно на тази граница се намираше CVE-2026-109619, втората уязвимост PHP2Shell.

## Две нива: процесът и заявката

За нетехническите читатели помага една картина: PHP worker е като гише в учреждение. Гишето се подрежда веднъж сутринта, с формуляри, печати и компютър. След това идват последователно много посетители. След всеки посетител масата трябва да е празна, за да не види следващият чужди документи.

PHP работи по същия начин. При старта на процеса зарежда разширения и настройки, които са еднакви за всички заявки. Всяка заявка получава след това собствена памет, собствени променливи и собствени данни на заявката като `$_GET`, `$_POST` или `$_FILES`. В края на заявката PHP освобождава тази памет. PHP Internals Book го обобщава накратко: PHP забравя паметта на заявката в края на всяка заявка.

## Фазите на PHP Request Lifecycle накратко

Имената идват от вътрешността на PHP; всяко разширение може да се закачи към тези фази.

| Фаза | Кога се изпълнява | Колко често | Типична задача |
|---|---|---|---|
| MINIT (Module Init) | при старта на процеса | веднъж за процес | регистриране на разширения, създаване на ini записи, трайни данни |
| GINIT | при създаването на глобалните данни | за процес, при нишки за нишка | подготовка на глобалните променливи на разширение |
| RINIT (Request Init) | в началото на всяка заявка | за заявка | подготовка на паметта и състоянието на заявката |
| Изпълнение | между тях | за заявка | работи Вашият PHP код |
| RSHUTDOWN | в края на всяка заявка | за заявка | освобождаване на ресурсите на заявката |
| PRSHUTDOWN | след RSHUTDOWN | за заявка, рядко използвана | последни почистващи дейности |
| MSHUTDOWN | при приключване на процеса | веднъж за процес | освобождаване на трайните данни |

Едно важно изречение от PHP Internals Book за разработчиците на разширения: глобалните данни не се изчистват автоматично след всяка заявка. Онова, което трябва да е ново за всяка заявка, принадлежи в RINIT за нулиране. Това правило показва защо границата между две заявки е критично за сигурността място.

В края на една заявка редът също е определен: първо се изпълняват shutdown функциите, регистрирани с `register_shutdown_function()`, след това деструкторите, след това изходният буфер се изпразва. Едва тогава следва RSHUTDOWN.

## Защо worker процесите се използват повторно

В командния ред един процес обработва точно една заявка и приключва. В уеб режим това би било твърде бавно. Затова PHP-FPM, Apache с mod_php, LiteSpeed и FrankenPHP държат наготово worker процеси, които обслужват много заявки поред. MINIT тогава се изпълнява веднъж, а RINIT и RSHUTDOWN – хиляди пъти. Как FPM управлява своите worker процеси, обяснява [PHP-FPM, обяснен](https://phpsos.de/bg/php/php-fpm-erklaert/).

В PHP-FPM `pm.max_requests` определя след колко заявки един worker се рестартира. По подразбиране е `0`, т.е. без ограничение. Ръководството посочва настройката като средство срещу течове на памет в библиотеки на трети страни:

```ini
; FPM пул: рестартиране на worker след 500 заявки (срещу течове на памет)
pm = dynamic
pm.max_children = 20
pm.max_requests = 500
```

Още по-далеч отива worker режимът на FrankenPHP: там цялото приложение се стартира веднъж и остава в паметта. Състоянието на приложението тогава оцелява и на ниво PHP. Примерът показва разликата:

```php
<?php
function anfrageZaehler(): int
{
    static $zaehler = 0;   // в класически FPM режим за всяка заявка отново е 0
    return ++$zaehler;     // в worker режим стойността продължава да расте през заявките
}
echo anfrageZaehler();
```

В класически режим извикването винаги извежда 1, защото PHP отхвърля паметта на заявката. В worker режим стойността расте, докато worker-ът живее. Документацията на FrankenPHP затова съветва състоянието, свързано със заявката, съзнателно да се нулира между заявките.

## Къде се намира CVE-2026-109619

CVE-2026-109619 не засяга кода на Вашето приложение, а самия PHP. Според php.net вътрешно състояние, принадлежащо на заявката, е могло да оцелее след освобождаването на паметта си. По-късна заявка в същия worker е могла да го използва, преди инициализацията да го нулира. В картината: върху масата на гишето е останал лист, който всъщност вече е бил прибран. Подробности за експлоатацията съзнателно не публикуваме; оценката е на страницата [CVE-2026-109619](https://phpsos.de/bg/php2shell/cve-2026-109619/).

Временните мерки на php.net за тази уязвимост се опират на потвърдени пътища, те не са пълна защита:

1. `session.upload_progress.enabled=0` в php.ini, FPM пула или конфигурацията на сървъра, след това рестартиране на всички worker процеси; не чрез `ini_set()` или `.user.ini`.
2. При PHP 8.5 допълнително `fatal_error_backtraces=0` и рестартиране.
3. Премахване на невалидни или остарели настройки за всяка заявка, които пораждат съобщения преди инициализацията. Само скриването на съобщенията според php.net не помага.
4. Отстраняване в приложението на фатални грешки и надхвърляния на времето, които могат да бъдат предизвикани отвън, включително в callbacks, при обработка на качвания и импорт.

Същинското средство е актуализацията до PHP 8.5.12, 8.4.27, 8.3.36 или 8.2.35. Тъй като грешката се намира в работещия worker, тя действа едва след рестартиране на всички процеси; стъпките са описани в [Рестартиране на PHP-FPM и на всички worker процеси](https://phpsos.de/bg/php2shell/php-fpm-neustart/).

> **Съвет:**
> След рестартирането проверете с `ps -eo pid,lstart,cmd | grep php` дали всички PHP процеси са стартирани след момента на актуализацията.

## Какво означава жизненият цикъл за експлоатацията и разработката

От модела могат да се изведат ясни правила. За администраторите: актуализацията действа едва когато не работи нито един процес със стар код. Всеки режим на работа с дълготрайни worker процеси изисква документирано рестартиране. За разработчиците: код, който работи в worker режими, не бива да оставя състояние, свързано със заявка, в статични променливи или singletons, например данни за потребители или права. И за двете групи важи: пазете логовете от 20.10.2026 г., за да може по-късно да се прецени дали преди пача е станало нещо.

## Как помага PHP SOS конкретно?

С [услугата за PHP пачове](https://phpsos.de/bg/leistungen/php-patch-service/) прилагаме модела на практика:

- **Определяне на режима на работа:** Установяваме дали работи PHP-FPM, mod_php, LiteSpeed, FrankenPHP или php-cgi и как се управляват worker процесите.
- **Актуализация и рестартиране:** Внедряваме коригираната версия и рестартираме контролирано всеки worker.
- **Проверка на процесите:** Доказваме по времената на стартиране и по версиите, че не е останал стар процес.
- **Проверка на worker режимите:** При дълготрайни приложения разглеждаме дали състоянието, свързано със заявката, се нулира чисто.


## Често задавани въпроси

**PHP стартира ли нов процес за всяка заявка?**  
В командния ред да, в уеб режим най-често не. PHP-FPM, Apache с mod_php, LiteSpeed или FrankenPHP оставят един процес да обработва много заявки една след друга. Между заявките PHP почиства състоянието на заявката.

**Каква е разликата между MINIT и RINIT?**  
MINIT се изпълнява веднъж, когато PHP процесът стартира, и създава трайни структури. RINIT се изпълнява в началото на всяка отделна заявка и подготвя онова, което е нужно само на тази заявка.

**pm.max_requests помага ли срещу CVE-2026-109619?**  
php.net не посочва по-честите рестартирания на worker процесите като временна мярка. Настройката ограничава само колко заявки обработва един worker, преди да се рестартира. Срещу уязвимостта помага актуализацията с последващо рестартиране на всички worker процеси.

**Какво е worker режимът на FrankenPHP?**  
В worker режим приложението се стартира веднъж и остава в паметта. Това спестява време, но статичните променливи, глобалните променливи и кешовете се запазват между заявките. Според документацията на FrankenPHP състоянието, свързано със заявката, затова трябва съзнателно да се нулира.

## Източници

- [php.net: Съобщение за сигурност от 21.10.2026 г.](https://www.php.net/)
- [Ръководство на PHP: Конфигурация на FPM](https://www.php.net/manual/en/install.fpm.configuration.php)
- [PHP Internals Book: Жизнен цикъл на PHP](https://www.phpinternalsbook.com/)
