Увійти Реєстрація
Вакансії
Вакансії Laravel Компанії Статистика
Підготовка
Як підготуватися < href="https://laravelukraine.com/interviews" wire:navigate.hover class="flex items-center gap-3 rounded-md px-3 py-2 text-base font-medium text-gray-600 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-900 dark:hover:text-white" >Співбесіди Тестування Документація
Блог
Всі статті Серії
Екосистема
Пакети Ресурси Проєкти Інструменти Події Відео
Інше
Про нас Реклама
Новини 14 листопада 2026

RFC пропонує хуки життєвого циклу запиту для воркер-режиму PHP

Воркер-режим PHP може отримати офіційні межі запиту. 9 листопада Кевін Дюнгла, автор FrankenPHP, виніс на обговорення в internals RFC «Request lifecycle hooks for SAPIs with a worker mode»: два нові хуки, через які рушій і розширення дізнаються, де закінчується один логічний HTTP-запит і починається наступний. Для всіх, хто тримає Laravel в пам'яті через Octane, це спроба прибрати цілий клас помилок, які досі латали вручну.

Воркер-режим простими словами

Класична модель PHP - «запустив і забув»: на кожен запит FPM чи Apache виконує скрипт з нуля, а після відповіді все, що скрипт створив, зникає. Автозавантажувач Composer, контейнер Laravel, конфігурація - усе будується заново щоразу. Повільно, зате надійно: стан одного запиту фізично не може дістатися до іншого, а витік пам'яті живе рівно до кінця запиту.

У воркер-режимі довгоживучий скрипт стартує разом із сервером, один раз завантажує застосунок і далі в циклі обслуговує запит за запитом. Так працює Laravel Octane: фреймворк лишається в пам'яті, і на кожен запит не треба знову піднімати сервіс-контейнер. Ціна - стан між запитами вже ніщо не ізолює, і за чистоту відповідає хтось інший.

Проблема, яку описує RFC, у тому, що з погляду PHP увесь цей цикл - один-єдиний запит. Хуки RINIT і RSHUTDOWN, якими розширення готуються до запиту і прибирають після нього, спрацьовують лише двічі: коли воркер стартує і коли зупиняється. Окремих HTTP-запитів між ними рушій не бачить.

Які баги вже були

Тож FrankenPHP скидає стан запиту сам, залазячи у внутрішні структури рушія і вбудованих розширень. RFC називає цей підхід крихким: внутрішній код скидання може змінитися в новій версії PHP без попередження, а кожен пропуск означає або витік стану між запитами, або помилку роботи з пам'яттю. Лише цього року FrankenPHP виправляв три такі випадки:

  • витік session ID іншим клієнтам (високий рівень загрози). Коли розширення session підключене як спільний модуль (extension=session, як у пакетах RPM і Debian), код скидання сесії просто не потрапляв у збірку FrankenPHP. session_start() бачив ідентифікатор попереднього запиту, ігнорував cookie і віддавав клієнтові чужу сесію;
  • use-after-free потоків php://temp. FrankenPHP звільняв внутрішній потік SplTempFileObject, який пережив межу запиту, і наступне звернення до об'єкта читало вже звільнену пам'ять. Виправлено у FrankenPHP 1.13.4;
  • double free після не-формового POST (JSON, XML), якщо воркер сам завершує цикл. Advisory прямо згадує Octane: він за замовчуванням перезапускає воркер кожні 500 запитів. Теж виправлено в 1.13.4, а саму причину в рушії закрили для PHP 8.6.

Інші воркер-SAPI (Turbine, bext, worker API бібліотеки ext-php-rs) копіюють кожен свою частину коду ініціалізації та завершення запиту, і кожен щось пропускає. А сторонні розширення ловлять межі запиту самі: Xdebug і профайлер Datadog підміняють функції SAPI, трейсер Datadog, Tideways і New Relic (поки у відкритих pull request) перехоплюють frankenphp_handle_request(), Blackfire і Sentry обгортають запит в userland. Жоден із цих способів не працює з іншими воркер-SAPI.

Чому б просто не викликати RSHUTDOWN і RINIT між запитами? Бо ці хуки змішують стан, який треба скидати, зі станом, який скрипт має зберегти. RSHUTDOWN розширення session записує сесію, але заодно звільняє зареєстровані обробники збереження. Розділити одне від іншого можуть тільки мейнтейнери кожного розширення.

Що пропонується

RFC додає два необов'язкові хуки модуля, які SAPI викликає між двома логічними запитами одного скрипта:

  • request_end - звільнити стан завершеного запиту, не чіпаючи нічого, що ще досяжне з userland. На цей момент буфери виводу вже скинуті, а заголовки надіслані;
  • request_begin - підготувати наступний запит, коли його заголовки й суперглобальні змінні вже доступні. Наприклад, для session.auto_start чи тригерів Xdebug.

Для самих SAPI є дві функції рушія, php_request_begin() і php_request_end(). Вони роблять те, що кожен воркер-SAPI зараз копіює у себе: вмикають і вимикають шар виводу, імпортують суперглобальні змінні нового запиту, запускають таймер виконання і викликають хуки. Цикл воркера зводиться до кількох рядків:

  while (next_request()) {
      /* fill SG(request_info) and SG(server_context) */
      php_request_begin();
      /* call the userland handler */
      php_request_end();
  }

Межа скидання окреслена вузько: тільки стан, побудований з вхідного запиту або пов'язаний з відповіддю. Це суперглобальні змінні (крім $_ENV), тіло запиту, сесія і стан виводу. А от mt_rand(), error_get_last(), зміни через putenv(), кеш stat, налаштування з ini_set() і часовий пояс за замовчуванням лишаються спільними, як у довгоживучому CLI-скрипті. Хуки ніколи не звільняють того, до чого ще може дістатися код: об'єкти, ресурси, колбеки, автозавантажувачі.

Вбудовані розширення отримують хуки одразу: standard скидає URL rewriter (output_add_rewrite_var()), session записує й закриває сесію, зберігаючи обробники збереження, filter звільняє копії вхідних даних, які зараз просто течуть на кожному запиті. Сторонні розширення переходять на хуки у власному темпі, а старі компілюються без змін.

Для userland нового API немає. В RFC прямо сказано: на userland-код зміна не впливає і нічого в ньому не вимагає.

Що це означає для Octane

Тут важливо, на якому сервері працює ваш Octane. RFC ділить рантайми на три групи:

  • FrankenPHP (а також Turbine, bext і Rust-SAPI на ext-php-rs) сам є SAPI і може замінити власні копії коду на php_request_begin() і php_request_end(). Дюнгла вже показав гілку FrankenPHP на нових хуках: з неї зникає ручне скидання сесії, URL rewriter і більшість скопійованого коду рушія;
  • Swoole, RoadRunner і Workerman крутять цикл в userland поверх CLI SAPI. SAPI вони не контролюють, тож викликати ці функції не можуть, і RFC їх напряму не стосується;
  • FPM, Apache, NGINX Unit виконують один скрипт на запит і не бачать жодних змін.

Тобто виграють передусім ті, хто запускає Octane на FrankenPHP: менше коду, який ламається з кожною новою версією PHP, і менше шансів на ще один витік між запитами. Ваш код Laravel переписувати не доведеться, а масив flush у config/octane.php і скидання стану самого фреймворку лишаються як були. Хуки стосуються стану рушія й розширень, а не ваших синглтонів.

Окремо про сесії: Laravel має власний компонент сесій і не використовує session_start(), тож витік session ID насамперед зачіпав код на нативних сесіях PHP. Але приклад показовий: помилка ховалася не в застосунку, а в тому, як зібране розширення.

Що це дасть профайлерам і APM

Для авторів розширень це головна новина. Xdebug, Datadog, Tideways, New Relic отримали б одну абстракцію, яка працює з будь-яким SAPI з воркер-режимом, замість окремих латок під FrankenPHP. Перехід на хуки дасть їм заодно й підтримку Rust-SAPI.

Крім того, хуки дозволяють відрізнити «головний» запит (сам воркер, від RINIT до RSHUTDOWN) від логічних запитів, які він обслуговує. RFC наводить приклад Xdebug: можна було б окремо відлагоджувати код, що завантажує застосунок і виконується між запитами, і окремо - один конкретний HTTP-запит, запущений його власними тригерами. Профайлер так само відправлятиме профіль на кожен логічний запит у request_end, а не один гігантський профіль на весь час життя воркера.

RFC також окремо розбирає зв'язок із RFC Scope і доходить висновку, що пропозиції не конкурують: Scope дає ідентичність паралельним запитам, але не має точки, де запит закінчується. Якщо пройдуть обидві, php_request_begin() створюватиме новий scope.

Статус і що робити зараз

Це пропозиція, а не ухвалене рішення. RFC має версію 0.1 і статус Under Discussion, голосування ще не призначене, а для ухвалення знадобиться дві третини голосів. Цільова версія - PHP 8.7. Обговорення в internals щойно почалося, тож деталі ще можуть змінитися. Паралельно в internals обговорюють і RFC про закриття PECL у 2029 році.

Практично для Laravel-проєкту на Octane нічого міняти не треба, а до PHP 8.7 ще далеко. Що варто зробити вже зараз:

  • якщо Octane працює на FrankenPHP, оновіться щонайменше до 1.13.4, де закриті use-after-free і double free;
  • якщо ставите FrankenPHP з пакетів RPM чи Debian, де session - спільне розширення, перевірте advisory про витік сесій і свою версію;
  • якщо пишете розширення для PHP або підтримуєте APM-агента, почитайте RFC і реалізацію та висловіться в треді, поки API ще можна змінити.
7

Читати в документації

Коментарі

Увійдіть, щоб залишити коментар

Будьте першим, хто залишить коментар!

Читайте також

Новини 14 листопада 2026

Laravel Storage Dedupler: один файл на диску замість десятка копій

Пакет maxkhim/laravel-storage-dedupler зберігає кожен файл один раз: ключем стає SHA-1 хеш вмісту, а моделі через трейт Deduplable отримують посилання на спільний UniqueFile. У версії 1.0.6 з'явився DEDUPLER_REMOVE_ORPHAN_ON_DETACH, що вирішує долю файлу після останнього detach.

Новини 13 листопада 2026

APM: менеджер залежностей для навичок, промптів і MCP-серверів AI-агентів

Agent Package Manager від Microsoft описує навички, інструкції, плагіни й MCP-сервери агента в apm.yml, ставить їх командою apm install, а apm.lock.yaml фіксує коміти й SHA-256 хеші. Розбираємо, як це лягає на Laravel-проєкт поруч із Laravel Boost і чому lock-файл тут важливий для безпеки.

Вакансії за темою

Hunt.IT Recruitment
26 днів тому

Back-End Developer

Розробник бекенд-сервісів і API для ігрових платформ, платіжних систем та модулів керування користувачами. Основний стек: PHP 8 і Laravel, також потрібен Go (щонайменше 3 роки). Вимоги: досвід REST API, бази даних (PostgreSQL, MySQL, Redis, MongoDB), мікросервіси та брокери повідомлень (Kafka, RabbitMQ), ELK, покриття unit-тестами. Кандидат має проєктувати масштабовану архітектуру, інтегрувати сторонні сервіси та наставляти молодших розробників.

ВебФорза Нова
Сьогодні

Програміст PHP (Laravel, WordPress, гіг-контракт)

Розробка на фреймворку Laravel та створення тем і плагінів для WordPress. Завдання включає роботу з jQuery, JS, CSS, HTML і PHP, використання git та оптимізацію швидкості завантаження сторінок. Оформлення за гіг-контрактом з погодинною оплатою.

Senior Full-Stack Developer (PHP / Go / Vue.js)

Senior Full-Stack розробник матиме розвивати й підтримувати існуючі веб-платформи, писати модулі, сервіси та API, а також розробляти клієнтську частину на Vue.js і Vanilla JS. Основний стек: PHP 8.x з Yii2 та/або Laravel, Go для сервісів, MySQL/MariaDB, REST API і Webhooks. Вимоги: досвід від 3 років, знання ООП і шаблонів проєктування, ES6+, Vue.js, SPA, Git, Docker і Composer; англійська B1-B2.

Пакети за темою

Kalimera

welldigit/kalimera

Interactive installer that scaffolds Laravel applications the Well Digit way.

v1.0.0 17

Bagisto

bagisto/bagisto

Bagisto — це платформа для електронної комерції, побудована на Laravel. Вона надає готове рішення для створення та управління інтернет-магазинами з підтримкою каталогу товарів, замовлень, платежів та клієнтів.

28,369 v2.5.1 13 28