РозробкаБудь-який рівень

PHP

PHP — серверна мова, що обробляє кожен вебзапит у воркері, який стартує порожнім і забуває все, щойно відповідь надіслано. Цей один факт — модель shared-nothing — пояснює, чому стан живе в Redis, чому повільна робота йде в чергу і чому стандартно налаштований сервер обробляє п’ять запитів одночасно. Сама мова змінилася більше, ніж її репутація: PHP 8 має типи, енуми, суворий режим і нову велику можливість щолистопада. Сторінка починається з того, що забуває воркер, і дванадцяти наслідків; далі — пул воркерів і його арифметика, мова такою, як вона є у 8.5, фреймворки й стандарти, база даних і виклики, які варто шукати в коді, довгоживучі сервери та п’ять задокументованих кейсів.

Поточна версія
8.5 · 8.6 — 19 лист. 2026
Модель
Один запит на воркер, потім забуто
Підтримка
2 роки активна + 2 безпекова

Що забуває воркер PHP — і дванадцять наслідків

Сервіс на Java, Node.js чи Go стартує один раз і далі тижнями обслуговує запити з того самого процесу. Усе, що він поклав у пам’ять, там і лишається: кеш, пул з’єднань, лічильник, таймер. Воркер PHP робить навпаки. Він будує застосунок з нуля для кожного запиту і викидає все це, щойно відповідь надіслано. Майже все, що дивує розробника, який прийшов з іншої мови, випливає з цієї однієї різниці. Ось дванадцять наслідків — для кожного звичка, яку він ламає, і те, що PHP робить натомість. Наступні розділи посилаються на рядки за номерами.

#Звичка з довгоживучого середовищаЩо відбувається в PHPЩо робити натомість
1Кешувати значення в статичній властивості чи глобальній зміннійВоно зникає з надсиланням відповіді; наступний запит обчислює його зновуСпільний кеш поза воркером: APCu на одному сервері, Redis на кількох
2Тримати сесії в пам’ятіТиповий обробник пише файли сесій на локальний диск сервераСесії в Redis чи базі даних, щойно з’являється другий сервер
3Писати завантажені файли на локальний дискНаступний запит може потрапити на сервер, де цього файлу немаєОб’єктне сховище, а ключ файлу — у базі даних
4Рахувати в пам’яті — ліміт запитів, лічильникКожен воркер рахує сам, і кожен підрахунок помирає зі своїм запитомАтомарний лічильник у Redis (INCR) чи базі даних
5Тримати пул з’єднань усередині застосункуЖоден пул не виживає; кожен запит відкриває власне з’єднанняПулер перед базою даних (PgBouncer, ProxySQL)
6Робити повільну роботу після відповіді — лист, PDF, вебхукВоркер зайнятий, доки не закінчить, і не може взяти наступний запитЧерга, яку споживає окремий довгоживучий процес-воркер
7Запустити таймер чи фоновий потікМіж запитами немає живого процесу, який би його виконавCron, що щохвилини викликає планувальник фреймворку
8Тримати відкритий WebSocketВоркер, якого тримає одне з’єднання, втрачений для всіх інших користувачівОкремий сервер реального часу: Laravel Reverb, Mercure, сервіс на Node чи Go
9Платити за старт один разФреймворк завантажується на кожен запитOPcache, попереднє завантаження і кешовані конфіг, маршрути й контейнер
10Спостерігати, як витік пам’яті росте годинамиВитік помирає зі своїм запитом — крім воркерів черги, що живуть годинамиПерезапускати воркери черги після N задач чи за лімітом пам’яті
11Деплоїти, копіюючи файли поверх робочого кодуOPcache далі віддає байт-код старих файлівПеремкнутися на нову теку релізу одним кроком, потім перезавантажити PHP-FPM
12Брати стільки запитів одночасно, скільки приносить трафікПул обслуговує рівно pm.max_children одночасно — 5 у стандартному конфігуРозрахувати пул від пам’яті й тримати кожен запит коротким

Один запит, один чистий процес — і пул зі стелею

PHP майже ніколи не говорить із мережею сам. Вебсервер — nginx, Apache чи Caddy — приймає з’єднання й передає запит протоколом FastCGI до PHP-FPM, менеджера процесів FastCGI. FPM тримає пул робочих процесів. Вільний воркер бере запит, завантажує фреймворк, виконує ваш код, пише відповідь — і викидає кожен створений об’єкт, змінну й з’єднання. Наступний запит отримує воркер у тому самому порожньому стані. Це модель shared-nothing: запити навмисно не ділять у пам’яті нічого.

Воркер PHP піднімає застосунок, обробляє рівно один запит, віддає відповідь — і викидає все, що створив: кожен об’єкт, кожну змінну, кожне з’єднання. Наступний запит починається в порожньому процесі, тож у пам’яті не переноситься нічого, а все, що має пережити запит, живе в базі даних, кеші чи сховищі сесій. Саме тому PHP масштабують додаванням воркерів, витік у межах одного запиту нешкідливий, а кеш усередині процесу тут не дає нічого.

Плюс у тому, що цілий клас помилок просто не може існувати. Витік об’єкта, зіпсована глобальна змінна, з’єднання в поламаному стані — усе це викидається за мілісекунди, тож запит здатен отруїти лише сам себе. Фатальна помилка забирає один запит, а не сервер. Масштабування так само прямолінійне: воркери нічого не ділять, тож більше потужності — це більше воркерів, а більше воркерів на більшій кількості серверів не потребують жодної координації, крім бази даних.

Пул — це стеля

Рядок 12 — той, що спричиняє аварії. Воркер обробляє один запит за раз, тож розмір пулу — це кількість запитів, які PHP може обслуговувати одночасно, а стандартний конфіг FPM задає pm.max_children = 5. Свіже встановлення обробляє п’ять одночасних запитів; шостий чекає в черзі. Правильне число береться з пам’яті, а не з процесора: виміряйте, скільки займає один воркер під реальним трафіком, поділіть на це пам’ять, яку можна віддати PHP, і лишіть місце для решти на машині. За 4 ГБ для PHP і 64 МБ на воркер це близько шістдесяти воркерів.

Тоді в пулу є стеля пропускної здатності, і це проста арифметика: кількість воркерів, поділена на час одного запиту. Шістдесят воркерів по 100 мс на запит обслуговують 600 запитів за секунду. Тепер нехай платіжний провайдер сповільниться до 2 секунд. Кожен запит, що його викликає, тримає свій воркер 2 секунди, ті самі шістдесят воркерів обслуговують 30 запитів за секунду, а все понад це стає в чергу. Схема показує це на пулі з восьми. Графік процесора весь цей час показує майже вільний сервер, бо очікування не коштує процесорного часу, — тому цю аварію так часто хибно читають як «замало серверів».

Воркер PHP-FPM обслуговує один запит за раз, тож пропускна здатність пулу — це кількість воркерів, поділена на час одного запиту. Вісім воркерів і залежність, що відповідає за 100 мс, дають 80 запитів за секунду; сповільніть цю залежність до 2 секунд — і той самий пул обслуговує 4, кожен воркер зайнятий очікуванням, а нові запити стоять у черзі, доки вебсервер не здасться з кодом 504. Процесор увесь цей час майже вільний, бо очікування не коштує процесорного часу, — тому цю аварію часто хибно читають як брак серверів. Шістдесят воркерів дають 600 і 30 запитів за секунду.
Що ви бачитеЩо це означаєКуди дивитися
server reached pm.max_children setting у лозі FPMУсі воркери були зайняті; запити стали в чергуСпершу повільні запити, потім розмір пулу
504 Gateway Timeout від nginxЗапит чекав чи виконувався довше, ніж дозволяє вебсерверНайповільніша залежність цього ендпоінта
502 Bad GatewayFPM відмовив у з’єднанні або воркер помер посеред запитуЛог FPM: повна черга, падіння, вбитий воркер
Allowed memory size of 134217728 bytes exhaustedОдин запит уперся в memory_limit (типово 128 МБ)Запит, що вантажить усю таблицю; читайте потоком чи частинами
Maximum execution time of 30 seconds exceededСам PHP обчислював 30 секунд — очікування не рахуєтьсяЦикл, а не повільний API

Мова, яку пам’ятають, і мова, яка існує

Більшість думок про PHP склалася на PHP 5: жодних типів, порівняння без логіки й проблема зі швидкістю, настільки серйозна, що Facebook написав власний компілятор. PHP 7.0, у грудні 2015 року, переписав рушій і став до двох разів швидшим за 5.6, а кожен реліз відтоді додавав щось до системи типів. Тепер реліз виходить щолистопада, отримує два роки виправлень помилок і ще два — виправлень безпеки, а потім — нічого. Таблиця перелічує, що кожен із них додав такого, що ви справді зустрінете в коді, і як довго він отримує патчі.

ВерсіяВипущеноЩо ви зустрінете в кодіПатчі безпеки до
7.0груд. 2015Скалярні типи параметрів і повернення, ??, рушій до двох разів швидшийЗавершено січ. 2019
7.4лист. 2019Типізовані властивості, стрілкові функції fn, preloadingЗавершено лист. 2022
8.0лист. 2020Типи-об’єднання, match, іменовані аргументи, атрибути, просування в конструкторі, ?->, розумніші порівняння, JITЗавершено лист. 2023
8.1лист. 2021Енуми, властивості readonly, файбери, never, first-class callablesЗавершено груд. 2025
8.2груд. 2022Readonly-класи, DNF-типи, динамічні властивості застарілі31 груд. 2026
8.3лист. 2023Типізовані константи класів, #[\Override], json_validate()31 груд. 2027
8.4лист. 2024Хуки властивостей, асиметрична видимість, ліниві об’єкти, array_find()31 груд. 2028
8.5лист. 2025Оператор |>, розширення URI, clone() з новими значеннями, #[\NoDiscard], array_first()31 груд. 2029
8.6Очікується 19 лист. 2026Часткове застосування функцій, значення за замовчуванням для readonly, сесійні cookie з HttpOnly і SameSite=Lax за замовчуванням31 груд. 2030, за політикою

Суворі типи: вирішує файл, що викликає

Оголошення типів у PHP мають два режими. За замовчуванням вони приводять значення: передайте рядок "5" у параметр int, і PHP тихо перетворить його на 5. З declare(strict_types=1) угорі файлу вони перевіряють: той самий виклик кидає TypeError. Частина, у якій помиляються майже всі, — котрий файл вирішує. Словами посібника, суворі типи «застосовуються до викликів функцій, зроблених у файлі з увімкненою суворою типізацією, а не до функцій, оголошених у цьому файлі». Функція, написана в суворому файлі, однаково приводить значення, коли її викликають із файлу без цього рядка.

У PHP declare(strict_types=1) діє на виклики, зроблені з файлу, де він стоїть, а не на функції, оголошені в ньому. Тож функція, написана в суворому файлі, однаково приймає рядок "5" для параметра int, коли її викликають із файлу без цього рядка, — значення тихо перетворюється, — і кидає TypeError лише тоді, коли суворий сам файл, що викликає. Бібліотека не може зробити суворими тих, хто її викликає. Зі значеннями, що повертаються, навпаки: їх перевіряють у режимі файлу, де функцію оголошено.

Практичне правило випливає саме: ставте цей рядок у кожен файл, разом із тестами й шаблонами, і хай інструмент перевірки стилю за цим стежить. Бібліотека не може зробити власні функції суворими для тих, хто їх викликає. Її єдиний захист — статичний аналіз на боці того, хто викликає. PHPStan і Psalm читають оголошення й знаходять null, що доходить до виклику методу, раніше за користувача. Обидва працюють рівнями — у PHPStan їх одинадцять, від 0 до 10, з релізу 2.0 у листопаді 2024 року, — тож наявна кодова база може підніматися на рівень за раз, а не одним неможливим ривком.

Порівняння, які PHP 8 виправив, і ті, що лишилися

Репутацію «дивних порівнянь» заробив нестрогий оператор ==, що перетворює обидва боки, перш ніж порівняти. PHP 8 змінив одне правило: число, яке порівнюють із рядком, що не схожий на число, тепер порівнюється як рядок. Це прибрало найгірші випадки. Решта лишилася, і саме через неї ===, що порівнює тип і значення без перетворень, — типовий вибір у сучасному коді.

ВиразPHP 7.4PHP 8Чому
0 == "foo"truefalse"foo" не число, тож 8 порівнює "0" і "foo" як рядки
in_array("abc", [0])truefalseТе саме правило; in_array нестрогий, якщо третій аргумент не true
"1" == "01"truetrueОбидва схожі на числа, тож порівнюються як числа
"10" == "1e1"truetrue"1e1" — експоненційний запис числа 10
null == falsetruetrueОбидва перетворюються на false

Про -компілятор, доданий у 8.0, варто говорити чесно. Він помітно пришвидшує довгі числові обчислення й майже не впливає на типові вебзапити, бо вебзапит проводить час в очікуванні бази даних і мережі, а не в арифметиці. Якщо PHP-сторінка повільна, відповідь — у лозі запитів і в рядку 6, а не в JIT.

Laravel, Symfony і стандарти під ними

Майже ніхто не починає PHP-застосунок із порожньої теки. Composer, менеджер залежностей, ставить пакети з Packagist і записує точні обрані версії в composer.lock; комітьте цей файл і на сервері запускайте composer install, ніколи composer update. Composer також генерує автозавантажувач, тож ви не напишете жодного require для власних класів. Автозавантажувач працює завдяки стандарту: PHP-FIG, група авторів фреймворків і бібліотек, публікує PSR — PHP Standard Recommendations, — що дають коду з різних проєктів стикуватися між собою.

СтандартЩо він фіксуєДе ви його зустрінете
PSR-4Як ім’я класу відповідає шляху до файлуСекція autoload кожного composer.json
PSR-3Один інтерфейс логераMonolog, який використовують Laravel і Symfony
PSR-7, PSR-17Об’єкти запиту й відповіді та фабрики для нихHTTP-бібліотеки, Slim, Mezzio
PSR-15Middleware й обробники запитівMiddleware, написаний раз для багатьох фреймворків
PSR-11Отримання сервісу з контейнераКожен контейнер впровадження залежностей
PSR-6, PSR-16Детальний і простий інтерфейси кешуБібліотеки кешу, що працюють у будь-якому фреймворку
PSR-14, PSR-18Розсилання подій; HTTP-клієнтSDK, що дають обрати HTTP-клієнт
PSR-20«Зараз» як сервіс, який можна підмінитиТести, що заморожують час
PER Coding StyleОформлення коду; замінив PSR-12PHP-CS-Fixer, PHP_CodeSniffer, Laravel Pint

Над цим шаром поле тримають два фреймворки, і вибір між ними — це вибір, скільки фреймворк вирішує за вас.

LaravelSymfony
ПозиціяВирішує за вас: один ідіоматичний спосіб для більшості речей, доступний одразуДає компоненти, які ви збираєте й налаштовуєте; менше мається на увазі
Доступ до данихEloquent, ORM у стилі active record: модель — це рядокDoctrine, ORM у стилі data mapper: сутності нічого не знають про базу даних
РелізиМажорний реліз щороку; виправлення помилок 18 місяців, безпеки — 2 рокиМінорний реліз кожні пів року; LTS (x.4) раз на два роки, 3 роки виправлень і 4 — безпеки
Де доречнийПродуктові команди, що швидко постачають фічі; малі й середні командиДовгоживучі системи, складні домени, команди, яким потрібні явні межі
Головний ризикЗручність заманює бізнес-логіку в моделі й контролери, доки ніщо не піддається тестуваннюЦеремонія й конфігурація гальмують маленьку команду без жодної віддачі

Чесне резюме: типовій продуктовій команді Laravel швидше дає робочу фічу, а ризик — архітектурний дрейф. Для системи, яку банк експлуатуватиме ще дванадцять років, явність Symfony варта церемонії. Обидва підтримуються й достатньо швидкі. Різниця між доброю і поганою кодовою базою в кожному — це дисципліна щодо того, де живе бізнес-логіка, а не фреймворк.

База даних і шість викликів, які варто пошукати перед кожним релізом

Репутація PHP щодо SQL-ін’єкцій виросла зі справжньої епохи запитів, склеєних із рядків, а виправлення є стандартом уже понад десятиліття: підготовлені вирази. Підготовлений вираз надсилає форму запиту й значення окремо, тож значення ніколи не прочитають як SQL. Кожен і будівник запитів в екосистемі використовує їх під капотом. Єдиний шлях назад до вразливості — вручну вставити змінну в SQL.

Ще дві звички відрізняють кодову базу, що переживає зростання, від тієї, що ні. Тримайте транзакції короткими: транзакція, відкрита на час HTTP-виклику до платіжного провайдера, блокує свої рядки рівно стільки, скільки той провайдер гальмує, і ще й тримає воркер (рядок 12). І тримайте міграції в системі контролю версій поруч із кодом, якому вони потрібні, щоб деплой і зміна схеми рухалися разом.

Ін’єкція — не єдині двері. Більшість серйозних вразливостей PHP походить від короткого списку викликів, безпечних із вашими даними й небезпечних із чужими. Кожен легко знайти текстовим пошуком, тож цей список — дешевий крок рев’ю перед кожним релізом:

Що шукатиЩо йде не так зі стороннім вводомНатомість
unserialize(Ввід обирає, які класи створюються, і їхні магічні методи виконуються — об’єктна ін’єкціяjson_decode(); або allowed_classes => false
"SELECT … $SQL-ін’єкціяЗв’язані параметри
include $Виконує файл, який назвав зловмисникФіксована мапа дозволених імен
exec( · shell_exec( · system(Ін’єкція командМасив аргументів (Symfony Process) або escapeshellarg()
md5( · sha1( на пароляхЗламуються офлайн за годиниpassword_hash() і password_verify()
== на токенах і підписахНестроге порівняння й витік через часhash_equals()

Довгоживучий PHP: що worker mode повертає і що забирає

Тепер є й другий спосіб запускати PHP. Сервери застосунків завантажують фреймворк один раз і далі обслуговують багато запитів із того самого процесу. FrankenPHP, побудований на вебсервері Caddy й офіційно підтримуваний PHP Foundation з травня 2025 року, називає це worker mode. RoadRunner робить це з процесу на Go; Swoole й OpenSwoole — як розширення PHP із корутинами; Laravel Octane обгортає всі три, а Symfony працює на них через свій компонент Runtime.

Повертає це рядки 1, 5 і 9. Ініціалізація оплачується один раз, тож запит, що більшість часу завантажував фреймворк, стає в кілька разів швидшим. Кеш усередині процесу й постійне з’єднання знову щось означають. А забирає це страховку моделі shared-nothing. Пам’ять більше не стирається між запитами, тож усе, що запит лишив на довгоживучому об’єкті, видно наступному — а наступний належить іншому користувачеві.

Під PHP-FPM кожен запит стартує з порожньою пам’яттю, тож сервіс, що кешує поточного користувача, заповнюється знову для кожного запиту. У worker mode — FrankenPHP, RoadRunner, Swoole, Laravel Octane — один процес обслуговує багато запитів, і довгоживучі об’єкти переживають їх. Сервіс, що кешує користувача через ??=, заповнює його на першому запиті й лишає, бо на наступному властивість уже задана: другий користувач бачить дані першого. Іншого бага для витоку не треба, і саме тому код, написаний під FPM, треба перевірити, перш ніж запускати у worker mode.

Код, написаний під FPM, ніколи на це не тестувався, бо під FPM цього не могло статися. Перед переходом перевірте п’ять речей:

Що шукатиЩо стається у worker modeВиправлення
Дані запиту на синглтоні чи в статичній властивостіЗапит наступного користувача їх читаєПередавати аргументом або скидати після кожного запиту
Довгоживучий сервіс, що отримав запит, користувача чи конфіг у конструкторіВін тримає копію першого запиту весь час життя воркераОтримувати на кожен запит або впроваджувати резолвер замість значення
Масив, що працює як кешРосте, доки воркер не вб’ють за пам’ятьКеш з обмеженим розміром або APCu чи Redis
Глобальні налаштування на запит — setlocale(), date_default_timezone_set(), ini_set()Лишаються встановленими для наступного запитуЗадавати на кожен запит або взагалі ні
З’єднання з базою, відкрите на стартіСервер закриває його після тайм-ауту простоюПерепідключення при збої; більшість фреймворків у цьому режимі робить це за вас

П’ять кейсів і те, чого в кожному бракувало

Кожен кейс нижче задокументовано у власних оголошеннях, баг-трекері й поштовій розсилці PHP або в опублікованому дослідженні, що його знайшло. Разом вони покривають рушій, стик із вебсервером, репозиторій вихідного коду, вікно підтримки й шар фреймворку.

КейсЩо сталосяЧого бракувало
HipHop, HHVM і PHP 7, 2010–2019Facebook збудував власні рушії PHP; PHP 7 наздогнав, і HHVM відмовився від PHPШвидкість в еталонному рушії
CVE-2019-11043, жовт. 2019Перенесення рядка в URL ламало розбір шляху в nginx і давало переписати налаштування FPMВідповідальний за стик вебсервера й FPM
Бекдор у php-src, бер. 2021Два коміти від імені ключових розробників додали бекдор для віддаленого виконанняНадійна ідентичність на комітах — спіймало рев’ю
CVE-2024-4577, черв. 2024М’який дефіс ставав прапорцем командного рядка для PHP-CGI у WindowsПідтримувана версія — і жодного режиму CGI в інтернеті
Laravel APP_KEY, 2018–2025260 000 ключів на публічному GitHub; 600 застосунків, відкритих для віддаленого виконання кодуСканування секретів, ротація — і жодного unserialize() за одним ключем

Перший кейс — причина, з якої розділ про мову починається із заяви про швидкість. У лютому 2010 року Facebook оголосив HipHop, що перекладав PHP на C++, бо стандартний рушій не встигав за його трафіком, а згодом замінив його на HHVM — віртуальну машину з JIT. Кілька років найшвидший спосіб запускати PHP був не від самого PHP. Відповідь прийшла зсередини проєкту: PHP 7.0, випущений 3 грудня 2015 року, перебудував ключові структури даних рушія й став, словами оголошення про реліз, «до двох разів швидшим за PHP 5.6». У вересні 2018 року команда HHVM оголосила, що припиняє підтримку PHP, і HHVM 4.0, у лютому 2019 року, виконує лише Hack — власну мову Facebook. Команди, що перейшли на HHVM заради швидкості, перейшли назад. Форк, швидший за оригінал, — це ставка на дорожню карту однієї компанії.

Третій кейс — про власний код мови. 28 березня 2021 року в php-src на власному git-сервері PHP з’явилися два коміти, підписані іменами Расмуса Лердорфа, що створив PHP, і Нікіти Попова, одного з найактивніших розробників. Їх позначили як виправлення друкарських помилок. Вони додавали код, що виконував будь-який PHP, надісланий у заголовку User-Agentt — зверніть увагу на друге t, — який починався зі слова «zerodium». Розробники, що читали потік комітів, помітили перший за кілька годин і відкотили; другий повернув той самий код і теж був відкочений. Наступного дня проєкт зробив основним свій репозиторій на GitHub, з обов’язковою двофакторною автентифікацією для права запису. Пізніший висновок: git-сервер, імовірно, не зламували взагалі — найпевніше, витекла база користувачів за ним, тож зловмисник пушив зі справжніми обліковими даними. Рев’ю спрацювало. Ідентичність — ні.

П’ятий кейс поєднує перший рядок таблиці пошуку із секретом. Laravel шифрує та інші значення ключем застосунку APP_KEY, а його функція decrypt() за замовчуванням десеріалізує те, що розшифрувала. Тож хто має ключ, може зашифрувати об’єкт на свій вибір, передати його застосунку — і той його десеріалізує: віддалене виконання коду. Для старих версій Laravel це була -2018-15133. У липні 2025 року GitGuardian і Synacktiv опублікували, що це означає в масштабі: вони витягли понад 260 000 значень APP_KEY з публічних репозиторіїв GitHub, закомічених між 2018 роком і травнем 2025-го, і знайшли понад 600 живих застосунків, які можна було ними захопити. Ключ потрапляв у репозиторій разом із файлом .env чи вставленим у конфіг і ніколи не змінювався. Усі заходи були звичайними — сканер секретів на кожен пуш, ротація, коли ключ витік, — а урок дизайну старий: ніколи не дозволяйте одному секрету бути єдиним, що стоїть між інтернетом і unserialize().

Де він вироджується

Модель PHP поблажлива, і це водночас її сила і спосіб, у який вона відмовляє. Витік, погана глобальна змінна чи повільний цикл коштують одного запиту й забуваються, тож кодова база може нести чимало безладу, перш ніж щось помітно зламається. Коли ж ламається, це майже завжди один із дванадцяти рядків, який проігнорували, або один із шести викликів, якому довірили чужі дані:

  • Повільна робота всередині запиту — листи, PDF, сторонні виклики — замість черги (рядок 6).
  • Пул, залишений на стандартних п’яти воркерах або розрахований від процесора замість пам’яті (рядок 12).
  • Жодного тайм-ауту реального часу на вихідних викликах, тож одна повільна залежність тримає всі воркери.
  • Сесії чи завантаження на локальному диску: працює на одному сервері й тихо ламається на двох (рядки 2 і 3).
  • Запити за зручним зв’язком, невидимі, доки в таблиці не з’являться справжні дані.
  • Файли без strict_types: оголошення типів приводять значення замість перевірки й дають хибну впевненість.
  • Бізнес-логіка всередині контролерів і моделей — доки правило можна перевірити лише через HTTP-запит.
  • unserialize() на будь-чому, що пройшло мережею, або ключ фреймворку, закомічений у репозиторій.
  • Worker mode, увімкнений заради швидкості без перевірки, що довгоживучі об’єкти тепер тримають між користувачами.
  • Версія за межею останньої колонки таблиці, де наступний бюлетень безпеки не матиме патча.

Чесна межа — сама модель. Запит, що має лишатися відкритим, — чат, живий дашборд, стрим, — увесь час бореться з задумом «один запит — один воркер», і правильна відповідь зазвичай — другий, маленький сервіс на іншому середовищі поруч із PHP-застосунком, а не PHP-застосунок, вигнутий під цю форму. Те саме з важкими обчисленнями й конвеєрами даних: середовище здатне їх виконати, але бібліотеки, інструменти й люди — деінде. Worker mode звужує розрив, не закриваючи його, і купує свою швидкість саме тією страховкою, що робила PHP простим для міркувань.

Практична перевірка займає п’ять хвилин. Запустіть php -v на продакшн-сервері й знайдіть рядок у таблиці версій. Запустіть composer audit, що перелічує встановлені пакети з відомими бюлетенями безпеки. Відкрийте сторінку статусу FPM (pm.status_path) під реальним трафіком і порівняйте «active processes» з pm.max_children: якщо в пік вони рівні, рядок 12 уже ваше вузьке місце.

Коли це застосовувати

Застосовуйте, коли

  • Вебзастосунки й API, де робота на запит коротка, а трафік масштабується горизонтально.
  • Команди, яким потрібен робочий продукт швидко: фреймворк, ORM, черга й адмінка приходять одним пакетом.
  • Контентні й електронно-комерційні системи, де глибокі й хостинг, і ринок фахівців, і екосистема.
  • Наявні PHP-системи: кодова база на PHP 8 зі статичним аналізом — краща ставка, ніж переписування.

Уникайте, коли

  • Довгі з’єднання — , стримінг, чат, — де «один воркер на запит» опирається на кожному кроці.
  • Важкі обчислення й конвеєри даних: середовище здатне на це, але екосистема — деінде.
  • Worker mode на коді, написаному під FPM, до перевірки того, що його синглтони тримають між користувачами.
  • Версія після дати завершення патчів безпеки — або PHP-CGI, відкритий в інтернет узагалі.

Джерела й метод

На чому стоїть ця стаття

  • 16 джерел
  • 4 задокументовані збої
  • Перевірено 13 жовтня 2026 р.

Дослідження з Claude. Пошук і звірку першоджерел зроблено з Claude (Anthropic). Відбір, текст і перевірку фактів — редакція Anatomy of IT Product.

Було корисно?

Поділіться з тим, хто працює над тією ж задачею.