Переезд на новую версию PHP — это не просто смена бинарника. В проде вас интересует повторяемость окружения, контролируемые изменения поведения, масштабируемость настройки и мгновенный откат. Ниже — практический чек‑лист для миграции на PHP 8.3/8.4 с фокусом на депрекейты, ini‑директивы и организацию безопасного rollback.
Когда и зачем обновляться до 8.3/8.4
Новые минорные релизы PHP устойчивее и быстрее за счёт улучшений в ядре и расширениях. Улучшается работа JIT/OPcache, усиливается типобезопасность, а предупреждения о потенциально опасных практиках помогают заранее вычистить технический долг. Для продакшна важны патчи безопасности и стабильность зависимостей. Обычно через несколько месяцев после выхода минорной версии экосистема библиотек и CMS её догоняет — это окно удобно для миграции.
Общий план миграции
Стратегия перехода строится вокруг дублирования окружения и постепенного включения трафика. Сначала — сбор сигналов совместимости (статический и динамический анализ), затем — канареечный запуск, и только после — переключение 100% нагрузки. Важно заранее подготовить план возврата на прежний рантайм.
1) Инвентаризация и статическая проверка
- Обновите зависимости: задайте платформу в Composer, чтобы собрать дерево под целевую версию PHP. Это выявит пакеты, не поддерживающие 8.3/8.4.
- Прогоните статический анализ совместимости (правила PHPCompatibility) на весь код проекта и сторонние модули.
- Проверьте расширения и PECL‑модули: их бинарную совместимость и минимальные версии под 8.3/8.4.
2) Дублирование рантайма и окружения
Держите два набора интерпретаторов и пулов PHP‑FPM: «текущий» и «новый». В веб‑сервере задайте два upstream, чтобы быстро переключаться. В cron и CLI используйте явный путь к нужному бинарнику, а не «php» из PATH. На VDS это позволяет гибко разводить пулы и конфиги без ограничений окружения.
# Примеры сокетов FPM
# /run/php/php81-fpm.sock
# /run/php/php83-fpm.sock
3) Динамический анализ и прогон тестов
- В стейджинге включите
error_reporting=E_ALL
иdisplay_errors=0
,log_errors=On
, чтобы собрать все предупреждения и депрекейты. - Прогоните unit/интеграционные тесты под новым интерпретатором.
- Смоделируйте синтетическую нагрузку: тёплый OPcache, прогрев кешей, сеансовая логика.
Депрекейты в 8.3/8.4: что всплывает чаще всего
Ниже — то, на что админы и девопсы натыкаются в логах чаще всего при переводе проектов с 8.1/8.2 на 8.3/8.4. Эти предупреждения не всегда «ломают» код, но в продакшне забивают логи и указывают на будущие ошибки.
Динамические свойства
Создание свойств «на лету» без объявления в классе помечено как устаревшее. Временная мера — атрибут #[AllowDynamicProperties]
, но лучше явное объявление свойств или переход на массивы/DTO.
// Плохо: создаёт динамическое свойство
$obj = new User();
$obj->foo = 'bar';
// Лучше: объявить свойство в классе User
class User { public string $foo = ''; }
Передача null не‑null параметрам внутренних функций
С 8.1 передача null
параметрам, которые не допускают null, помечается как deprecated. Решение — явная нормализация входных значений и строгие сигнатуры.
Устаревшие функции для работы с кодировками
utf8_encode()
и utf8_decode()
устарели в пользу mb_convert_encoding()
. Замените вызовы, иначе логи будут расти, а поведение на краевых кейсах — непредсказуемым.
Сигнатуры магических методов
Несоответствие сигнатур магических методов требованиям приводит к предупреждениям и ошибкам. Актуализируйте __toString
(всегда строка) и переходите на __serialize/__unserialize
вместо Serializable
.
Строгость типизации и предупреждения из расширений
На 8.3/8.4 чаще встречаются строгие проверки типов в MBString, Intl, XML и более «говорящие» предупреждения из Date/Time при некорректных форматах. Нормализуйте входные данные и проверяйте коды возврата.
Депрекейт — это «технический долг, который завтра станет TypeError». Чистим на стейджинге до запуска на весь трафик.
ini‑изменения: как не промахнуться
Надёжно сравнить поведение можно, сняв «снимки» конфигурации своим окружением и проверив критичные зоны руками. Сравните вывод php -i
и php -m
для обеих версий и просмотрите OPcache/JIT, Session, MBString, PDO/MySQL, cURL, PCRE, Date, Error handling, Random/Sodium.
Раздел Error handling
Для стейджинга и канареек: error_reporting=E_ALL
, display_errors=0
, log_errors=On
, а error_log
указывает на отдельный файл под новую версию. Это отделит шум старого рантайма.
OPcache и JIT
Пересмотрите opcache.enable
, opcache.jit
, opcache.memory_consumption
, opcache.max_accelerated_files
, opcache.validate_timestamps
, opcache.preload
, opcache.preload_user
. На типичном веб‑трафике JIT редко даёт выигрыш, а вот память и лимиты кэша критичны.
Session
Проверьте session.cookie_samesite
, session.cookie_secure
, session.use_strict_mode
, session.sid_length
, session.save_handler
, session.save_path
. При параллельной работе двух версий избегайте конфликта файловых сессий: используйте раздельные save_path
для канареек. Для высоконагруженных инсталляций рассмотрите сессии в Redis — см. материал «Redis для сессий и object cache» по ссылке Redis для сессий и object cache.
MBString/PCRE
Перепроверьте флаги регулярных выражений и режимы кодировок. Жёстко задавайте нужную кодировку через mb_internal_encoding
и не полагайтесь на системные локали. В тестах покрывайте «грязные» входы: смешанные UTF‑8/CP1251 и некорректные байт‑последовательности.
PDO/MySQL и Date/Time
Для PDO уточните DSN и драйверные опции: включите исключения и строгий режим. Для даты/времени задайте date.timezone
и покройте тестами края: переходы на летнее время, несуществующие даты, парсинг нестандартных форматов.
Переключение веб‑сервера и быстрый откат
Ключ к безопасной миграции — возможность мгновенно вернуться на прежний рантайм. Два типовых паттерна: переключение backend‑сокета на веб‑сервере и фиксация бинарника для CLI.
Nginx: два upstream под разные FPM
upstream php81 { server unix:/run/php/php81-fpm.sock; }
upstream php83 { server unix:/run/php/php83-fpm.sock; }
server {
listen 80;
server_name example.local;
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Для канареек переключаем target здесь
fastcgi_pass php83;
}
}
Откат — это правка/деплой инклуда с fastcgi_pass php81;
и быстрый reload веб‑сервера. Практикуйте dry‑run конфигурации перед переключением.
Apache + PHP‑FPM
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php83-fpm.sock|fcgi://localhost/"
</FilesMatch>
# Для отката меняем на php81-fpm.sock и перезагружаем Apache
CLI и cron
Для задач по расписанию используйте фиксированный путь к нужной версии:
/usr/bin/php83 artisan schedule:run
/usr/bin/php83 bin/console app:job
hash -r
Для быстрого возврата держите симлинк /usr/local/bin/php-current
и переключайте его на нужный бинарник с последующим hash -r
в интерактивных шеллах. В cron прописывайте именно симлинк, чтобы не править десятки задач при переключении.

Порядок тестирования на трафике
Даже после чистых логов на стейджинге закладывайте «подушку» на проде: начинайте с канареечной доли запросов и наблюдайте метрики.
- Канарейка: 1–5% запросов на пул 8.3/8.4, остальное на старую версию.
- Метрики: QPS, p95/p99 latency, ошибки 5xx, доля
fastcgi
таймаутов, размер логов PHP, количество депрекейтов/минута. - Логи PHP: выделенный файл и отдельный парсер, чтобы не смешивать с основным рантаймом.
- OPcache: аккуратно прогревайте кеш при релоде, чтобы не ловить холодный старт на всей доле трафика.
Практика чистки депрекейтов
План работ рационально строить от самых «шумных» до редких:
- Динамические свойства: расширьте классы явными свойствами или временно пометьте
#[AllowDynamicProperties]
и заведите задачу на рефакторинг. null
в non‑nullable: нормализуйте входные значения, добавьте проверки и корректные типы в сигнатурах.- Кодировки: замените устаревшие функции на
mb_*
, определите общую стратегию кодировок. - Магические методы: приведите сигнатуры и возвраты к современным требованиям.
- Тесты: добавьте кейсы на «грязные» данные и крайние случаи дат/регулярок.
php.ini и .user.ini: что держать под контролем
Даже если дефолты не менялись, обновление — хороший повод зафиксировать явные значения. Почти всегда стоит прописать: error_reporting
, display_errors
, log_errors
, error_log
, memory_limit
, max_execution_time
, max_input_vars
, post_max_size
, upload_max_filesize
, date.timezone
, default_charset
, параметры opcache.*
и session.*
(с раздельным save_path
на параллельных пулах).
Если вы на виртуальном хостинге, используйте проектные .user.ini
как слой переопределений. Подробный разбор подхода — в статье «.user.ini: значения и лимиты» по ссылке .user.ini: значения и лимиты.
Согласованность окружения
Следите, чтобы CLI и FPM использовали одну и ту же версию PHP и одинаковые расширения. Несогласованность бинарников — классическая причина «на кроне всё упало», когда в вебе уже 8.3, а крон запускает 8.1 с иными расширениями.
Базы данных, миграции и совместимость артефактов
Переезд PHP может совпадать с обновлением библиотек, которые меняют SQL‑миграции, сериализаторы и формат кеша. Планируйте бэкапы, согласование версий миграций, совместимый формат сериализации для кеша и сессий и раздельные storage для канареек.
Чек‑лист безопасного отката
- Два пула FPM: старый и новый; переключение в конфиге веб‑сервера одной строкой.
- Раздельные логи PHP и метка версии в логах для быстрых фильтров.
- Симлинк на «текущий» бинарник CLI и документированный способ переключения.
- Резервная копия
php.ini
,php-fpm.conf
,pool.d/*.conf
,.user.ini
. - Чёткий «go/no‑go»: метрики, SLA, окно принятия решения об откате.
Типичные симптомы и лечение
- Вырос p95/p99: проверьте холодный OPcache,
opcache.validate_timestamps
, лимиты памяти OPcache, горячие точки на regex/mbstring. - Сыпятся депрекейты: агрегируйте по типам и начните с самых «шумных» источников.
- 500/timeout на части запросов: проверьте связку веб‑сервер ↔ FPM, лимиты пула (
pm.max_children
),request_terminate_timeout
, FPM slowlog. - Крон работает иначе, чем веб: убедитесь, что используется один и тот же PHP и одинаковые расширения.
Итоги
Миграция на PHP 8.3/8.4 безопасна, если у вас есть параллельные пулы, явные ini‑настройки, сбор сигналов (логи и метрики) и план отката. Не спорьте с депрекейтами — исправляйте их до запуска на весь трафик. Начинайте с канареек, держите быстрый переключатель рантайма и отделяйте логи — так переход пройдёт предсказуемо, без ночных аварий и «внезапных» сюрпризов.
Если остались уникальные условия инфраструктуры (нестандартные расширения, зашифрованные модули, самописные обвязки FPM) — сперва репетируйте в полном клоне прод‑окружения, а уже потом переносите практику на боевые инстансы.