Слухи о медленности PHP родились не на пустом месте. Классический PHP-процесс двадцать лет компилировал скрипт заново на каждый запрос, ничего не хранил между обращениями и выполнял бутстрап фреймворка десятки раз в секунду. Поколение PHP 7.0, вышедшее в декабре 2015 года, переписало движок и удвоило скорость против версии 5.6. PHP 7.4, релиз которого датирован 28 ноября 2019 года, добавил типизированные свойства и предзагрузку, но 28 ноября 2022 года ветка умерла: последним патчем остался 7.4.33, и с тех пор дыры в ней закрывать некому. PHP 8.3 из релиза 23 ноября 2023 года получает заплатки безопасности до 31 декабря 2027 года, а рядом уже живут ветки 8.4 и 8.5. Между этими строками и живёт практический вопрос: что даёт переход с 7.4 на 8.3, кроме чувства современности. Ответ честнее, чем кажется: прирост зависит от профиля приложения и расходится от трёх процентов до двадцати с лишним. Разберём, откуда берётся такая разница и что типизированный код даёт помимо скорости.
Откуда взялась репутация медленного PHP и как её меняли
Историческая медленность складывалась из трёх слагаемых. Первое, интерпретатор без кэша байткода компилировал каждый файл при каждом запросе, и большой фреймворк тратил на это заметную долю времени. Второе, архитектура без общего состояния означала, что между запросами не живёт ничего: соединение с базой, дерево классов, конфигурация собирались заново. Третье, кэш байткода долгие годы оставался факультативом, хотя в дистрибутив он встроен с ветки 5.5, и на дешёвых хостингах расширение часто просто не включалось. Следы той эпохи живы до сих пор: в панелях управления хостингом встречаются галочки выбора PHP 5.6, и за ними стоят реальные сайты.
Поколение 7.0 в декабре 2015 года поменяло правила: значения стали компактнее, хеш-таблицы экономнее, появилось абстрактное синтаксическое дерево, и по официальным замерам производительность выросла примерно вдвое против 5.6. Ветка 7.4, вышедшая 28 ноября 2019 года, добавила типизированные свойства, стрелочные функции и предзагрузку opcache preloading, которая впервые позволила держать классы фреймворка в памяти между запросами. Именно эта ветка закрыла линейку 7, и именно она до сих пор встречается на живых серверах, хотя формально мертва почти четыре года.
Реальные ускорители выполнения opcache preloading и JIT
Оптимизатор opcache превращает скрипты в байткод один раз и хранит его в разделяемой памяти процессов php-fpm. Продакшен-стандарт выглядит так:
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=50000
opcache.validate_timestamps=0
Ноль в последней строке запрещает проверять время изменения файлов на каждом запросе, экономя системные вызовы, и потому релиз сопровождается перезапуском php-fpm. Буфер интернированных строк собирает повторяющиеся литералы в общую память, на больших проектах это заметные мегабайты. Предзагрузка из PHP 7.4 идёт дальше: файл preload компилируется один раз при старте мастера, разрешает циклические зависимости и оставляет перечисленные классы в памяти навсегда:
opcache.preload=/var/www/app/preload.php
opcache.preload_user=www-data
// preload.php: компилируем сущности один раз при старте php-fpm
foreach (glob(__DIR__.'/src/*/Entity/*.php') as $file) {
opcache_compile_file($file);
}
JIT из PHP 8.0 добавляет последний слой: горячие трассы байткода компилируются в машинный код прямо в памяти воркера. Включается режим явно, ненулевым буфером:
opcache.jit=tracing
opcache.jit_buffer_size=64M
Проверить фактические значения в бою помогает одна строка:
php -i | grep -E "opcache.jit|opcache.preload"
Здесь нужна трезвость. На счётных синтетических тестах JIT даёт кратный прирост, но реальный веб-запрос упирается в базу, сеть и ожидания, поэтому в типичном приложении выигрыш измеряется единицами процентов. JIT лечит тяжёлую математику, обработку изображений и парсеры, а не медленные запросы к MySQL.
Здоровье кэша удобно наблюдать в цифрах: доля попаданий показывает, сколько запросов обошлось без компиляции:
$stats = opcache_get_status()['opcache_statistics'];
echo $stats['opcache_hit_rate'];
Значения выше 99 процентов считаются нормой продакшена, падение означает нехватку памяти или лишние файлы в дереве проекта.
Честные цифры при переходе с 7.4 на 8.3 на идентичном стенде
Замеры 2026 года на одной конфигурации, nginx и php-fpm, opcache включён, JIT выключен, память скрипта 128 МБ, дают наглядную картину. WordPress 6.8 на PHP 7.4 выдаёт 139.06 запроса в секунду, на 8.3 уже 142.75, а на 8.5 доходит до 148.30. Прирост перехода с 7.4 на 8.3 около трёх процентов, на уровне погрешности мониторинга. Линейка 8.2, 8.3 и 8.4 держится в коридоре от 142 до 148 запросов в секунду, скачков внутри поколения нет. Зато WooCommerce 10.2.2 на том же стенде поднимается с 44.20 запроса в секунду на 7.4 до 53.74 на 8.3, это плюс почти двадцать два процента без изменения кода магазина.
Разница объясняется профилем. Простая страница упирается в кэш и выборку из базы, движку оставался узкий зазор. Тяжёлый стек с тысячами классов, событиями и плагинами получает от каждой новой ветки движка свою долю. Отдельная честность в том, что прыжок WooCommerce до 71.02 запроса в секунду на 8.5 объясняется уменьшившимся размером ответа, а не внезапным ускорением языка: цифры всегда читают вместе с условиями замера. Вывод практичный: обновление не замедляет никого, а ускоряет тем заметнее, чем толще framework-слой вокруг бизнес-логики. Для внутренней панели на самописном коде прирост скромный, для магазина на тяжёлом фреймворке он ощущается в счетах за железо.
Типизированный код PHP 8.3 что он даёт движку и команде
Синтаксис типов развивался линейно: свойства с типами пришли в 7.4, продвижение аргументов конструктора и выражение match в 8.0, перечисления и readonly-свойства в 8.1, readonly-классы в 8.2. Версия 8.3 закрыла контур типизированными константами классов и функцией json_validate. Константы тоже получили право на тип, и опечатка в значении ловится сразу: