Обновление PHP на продакшене пугает многих разработчиков и сисадминов. Кажется, что это как менять колесо на ходу: чуть ошибся — сайт лежит, клиенты недовольны, телефон разрывается. На практике всё решаемо, если действовать по плану, а не наугад.
Зачем вообще обновлять PHP
PHP 8.3 быстрее PHP 7.4 примерно на 25–30% на реальных нагрузках — это не маркетинг, это замеры Phoronix и Kinsta. Помимо скорости, старые ветки перестают получать патчи безопасности. PHP 7.4 вышел из поддержки ещё в ноябре 2022 года. Если у вас до сих пор крутится 7.4 — это не технический долг, это дыра.
Ещё аргумент: современные библиотеки и фреймворки постепенно отказываются от старых версий. Laravel 11 требует минимум PHP 8.2. Symfony 7 — тоже. Значит, рано или поздно обновляться придётся, лучше по своему расписанию, а не в панике когда что-то сломалось.
Что может пойти не так
Перед тем как лезть в продакшен, нужно понимать основные риски:
Несовместимость кода. Между PHP 7 и PHP 8 много breaking changes. array_key_exists ведёт себя иначе, match — новое ключевое слово, str_contains появился только в 8.0, динамические вызовы на $this убрали. Если кодовая база не обновлялась давно, там могут быть тихие ошибки, которые не падают с исключением, а просто дают неправильный результат.
Расширения PHP. ext-redis, ext-imagick, ext-gd, ext-intl — их нужно перекомпилировать или переустановить под новую версию. Про них часто забывают.
Конфиги php.ini. Значения по умолчанию меняются от версии к версии. error_reporting, precision, serialize_precision — если в коде есть что-то, что завязано на конкретные значения, после обновления поведение изменится.
Composer-зависимости. Пакеты могут требовать конкретную версию PHP в секции require. После обновления composer install может отказаться работать.
Правило номер один: сначала стейджинг
Никогда не обновляй PHP сразу на продакшене. Никогда. Даже если уверен на 100%. Стейджинг должен быть максимально близким к боевому окружению: та же база данных (или дамп), те же переменные окружения, та же версия nginx/apache, те же расширения.
На стейджинге гоняем:
composer diagnose
composer check-platform-reqs
php -l path/to/file.php # синтаксическая проверка
composer check-platform-reqs скажет, какие пакеты несовместимы с новой версией PHP. Это экономит часы поиска ошибок.
Если есть автотесты — запускаем всё. Если нет — плохо, но хотя бы прошёлся руками по критическим сценариям: авторизация, оплата, отправка форм.
Мультиверсионность PHP — ваш главный инструмент
Самый безопасный способ обновиться — держать на сервере обе версии PHP одновременно. Это не экзотика, это стандартная практика.
На Ubuntu/Debian это делается через репозиторий Ondrej Sury:
add-apt-repository ppa:ondrej/php
apt update
apt install php8.3 php8.3-fpm php8.3-cli php8.3-mysql php8.3-redis php8.3-gd
Теперь у вас есть и PHP 7.4 (или 8.1), и PHP 8.3. Они работают параллельно через разные сокеты FPM:
/var/run/php/php7.4-fpm.sock
/var/run/php/php8.3-fpm.sock
Nginx переключается между ними на уровне конфига. Это ключевой момент.
Переключение в Nginx
Типичный конфиг до обновления:
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
Меняем одну строку:
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
Проверяем конфиг и перезагружаем без остановки сервера:
nginx -t
nginx -s reload
nginx -s reload — это graceful reload. Nginx дочитывает текущие запросы, а новые уже идут через новый воркер. Даунтайма нет совсем.
Если что-то пошло не так — меняем строку обратно и снова nginx -s reload. Откат занимает 30 секунд.
Apache + PHP-FPM
С Apache принцип тот же, только синтаксис другой. Если используете mod_php — это сложнее, потому что модуль загружается при старте Apache. Лучше перейти на PHP-FPM + mod_proxy_fcgi, это даёт такую же гибкость как в Nginx.
Пример виртуального хоста Apache с FPM:
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/example
<FilesMatch \.php$>
SetHandler "proxy:unix:/var/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>
</VirtualHost>
Перезагрузка тоже graceful:
apachectl graceful
Конфиги PHP-FPM
Не забудьте перенести настройки из старого пула в новый. Файл пула находится в /etc/php/8.3/fpm/pool.d/www.conf. Смотрите на:
pm.max_children — максимальное количество воркеров
pm.start_servers, pm.min_spare_servers, pm.max_spare_servers
request_terminate_timeout — таймаут выполнения запроса
env[...] — переменные окружения, если прокидываете их через FPM
И обязательно сверьте /etc/php/8.3/fpm/php.ini с текущим /etc/php/7.4/fpm/php.ini. Особенно memory_limit, upload_max_filesize, post_max_size, max_execution_time.
Расширения: не забыть и не сломать
Список расширений текущей версии PHP:
php7.4 -m
Список расширений новой:
php8.3 -m
Сравниваем, доустанавливаем то, чего нет:
apt install php8.3-redis php8.3-imagick php8.3-intl php8.3-bcmath php8.3-zip
PECL-расширения (например, xdebug или нестандартный swoole) нужно перекомпилировать отдельно. Для xdebug:
pecl -d php_suffix=8.3 install xdebug
После установки добавляем в /etc/php/8.3/fpm/conf.d/99-xdebug.ini.
Supervisor, очереди и CLI-процессы
Если у вас крутятся воркеры очередей (Laravel Horizon, Symfony Messenger, Artisan queue:work) — они используют свою версию PHP, не ту, что в FPM. После переключения Nginx FPM на 8.3 воркеры продолжают работать на 7.4.
Меняем бинарь в конфиге Supervisor:
[program:laravel-worker]
command=/usr/bin/php8.3 /var/www/artisan queue:work
Перезагружаем:
supervisorctl reread
supervisorctl update
supervisorctl restart laravel-worker:*
Аналогично для крон-задач в crontab — проверьте, что там явно указан путь к нужной версии:
* * * * * /usr/bin/php8.3 /var/www/artisan schedule:run
Мониторинг после переключения
После nginx -s reload не уходите пить кофе. Смотрите:
Логи PHP-FPM:
tail -f /var/log/php8.3-fpm.log
Логи ошибок Nginx:
tail -f /var/log/nginx/error.log
Логи приложения — там могут быть PHP Notices и Warnings, которые не крашат сайт, но сигнализируют о проблемах.
Через 15–30 минут смотрим метрики: время ответа, процент ошибок (5xx), утилизацию CPU и памяти. Если что-то ухудшилось — откатываемся и разбираемся.
Постепенное переключение через несколько виртуальных хостов
Если сайт большой и страшно переключать всё сразу — можно сделать канареечный деплой на уровне PHP. Создаём второй виртуальный хост для тестовой группы пользователей или внутреннего IP:
server {
listen 80;
server_name beta.example.com;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
...
}
}
Прогоняем часть трафика через beta, смотрим результат, потом переключаем основной домен.
Как это выглядит в проектах студии
В REEXY при обслуживании корпоративных сайтов обновление PHP — часть планового технического обслуживания. Мы всегда сначала разворачиваем стейджинг, прогоняем тесты, и только после проверки переключаем продакшен. Весь процесс занимает от 2 до 4 часов в зависимости от сложности проекта, при этом сайт не уходит в офлайн ни на секунду.
Что делать, если что-то сломалось
Откат:
# Меняем обратно в nginx.conf
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
# Перезагружаем
nginx -s reload
Собираем логи, разбираемся с конкретной ошибкой. Чаще всего это:
- Устаревшая функция — заменяем или добавляем полифилл
- Пакет несовместим с PHP 8.3 — ищем обновление или форк
- Тип-несоответствие — PHP 8 строже относится к типам, где 7.4 молчал
Для поиска проблем отлично работает phpstan или rector. Rector умеет автоматически переписывать код для совместимости с новой версией:
composer require rector/rector --dev
vendor/bin/rector process src --dry-run
Флаг --dry-run покажет, что изменится, не трогая файлы.
Краткий чеклист
- Обновить и протестировать на стейджинге
- Запустить
composer check-platform-reqs на новой версии
- Установить новую версию PHP + FPM + все нужные расширения
- Скопировать и настроить php.ini и pool.conf
- Переключить Nginx/Apache на новый FPM-сокет
- Сделать graceful reload веб-сервера
- Обновить Supervisor и crontab
- Мониторить логи и метрики 30 минут
- Убедиться, что старая версия PHP ещё не удалена — на случай отката
Обновление PHP — это не страшно. Страшно делать это без плана и без возможности откатиться. Если всё подготовлено заранее, переключение занимает меньше минуты, а сайт продолжает работать без перерыва.