PHP 8.4 и Bitrix в production: что реально ломается после обновления
В release notes Bitrix написано: «PHP 8.4 поддерживается». Первый клиент, который обновил staging, получил 47 deprecated notices за один запрос главной страницы и сломанный платёжный модуль. Ни одна из этих проблем не была в ядре Bitrix.
Вот что мы теперь проверяем перед каждым Bitrix PHP 8.4 обновлением production-сервера.
Что изменилось в PHP 8.4, что актуально для Bitrix-проектов
PHP 8.4 вышел в ноябре 2024. Большинство изменений — новые возможности: property hooks, asymmetric visibility, lazy objects. Ломают production три других вещи.
Первое — implicitly nullable types стали E_DEPRECATED. Раньше function foo(CIBlockResult $result = null) без ? работало молча. Теперь PHP 8.4 пишет deprecated notice при каждом вызове. В кастомных компонентах пятилетней давности это встречается везде.
Второе — PHP 8.4 добавил нативные array_find(), array_find_key(), array_any(), array_all(). Если в /local/php_interface/ уже есть функция с таким именем — при первом запросе после апгрейда фатальная ошибка «Cannot redeclare».
Третье — сторонние модули, которые вендоры не обновляли с 2019 года. Устаревшие паттерны DOM, захардкоженная проверка версии PHP в compatibility-check. Это не изменения в 8.4 — это накопленный долг, который апгрейд просто обнажает.
Три места, где legacy Bitrix-код гарантированно падает
Implicitly nullable parameters в кастомных компонентах
Паттерн встречается везде, где компонент принимает объект Bitrix-класса с fallback на null:
// PHP 8.3 — работало молча
// PHP 8.4 — E_DEPRECATED в каждом вызове
function MyComponent(CIBlockResult $arResult = null, CPrice $arPrice = null) { ... }
На магазине с 28 000 SKU таких мест оказалось 23. На каждой странице каталога — несколько deprecated notices. В логах это выглядит как «всё сломалось», хотя реально работало.
Проверить до апгрейда:
grep -rn 'function.*[A-Z][A-Za-z]*\s\+\$[a-z].*=\s*null' /path/to/local/
Это не убивает сайт сразу. Но засоряет error_log настолько, что реальные ошибки в нём не видны.
Коллизии имён функций
Старый добрый паттерн — определить array_find() как хелпер в php_interface.php или в глобальном include утилит. PHP 8.4 добавил array_find() в stdlib. Теперь:
PHP Fatal error: Cannot redeclare array_find() in /local/php_interface/utils.php on line 47
Это убивает сайт на первом же запросе после апгрейда.
Grep до апгрейда:
grep -rn 'function array_find\|function array_any\|function array_all\|function array_find_key' /path/to/local/
Найти — переименовать. Пять минут работы. Но только если найти до, а не после апгрейда production.
Платёжные и доставочные модули
Платёжный модуль Robokassa образца 2020 года, CloudPayments-интеграция, DPD API — они часто содержат собственный PHP-compatibility check, захардкоженный на «поддерживаем до 8.2». На PHP 8.4 они молча выходят с ошибкой, не принимая транзакции. Клиент видит пустую страницу на шаге оплаты.
У нас был ровно такой случай. Модуль доставки обновился последний раз в 2021 году. Вендор ответил через три дня. Три дня магазин не принимал заказы через этот метод доставки.
7 шагов staging-чеклиста перед апгрейдом
Staging должен быть копией production: та же версия Bitrix, тот же набор модулей, те же /local/ файлы. Если staging отличается — результаты не показательны.
- Включить E_DEPRECATED | E_NOTICE в php.ini на staging. Записывать в отдельный лог, чтобы не мешать с application-логами. Смотреть только этот лог после первого захода на сайт.
- Grep по implicitly nullable паттерну — команда выше. Исправить все найденные сигнатуры: добавить
?перед типом или убрать= null.
- Grep по коллизиям array-функций — команда выше. Переименовать кастомные хелперы.
- Обновить все marketplace-модули через «Обновление решений» в административной панели до того, как менять версию PHP. Не после.
- HTTP smoke-check: curl по 20 ключевым URL (главная, каталог, карточка товара, корзина, поиск). Проверить, что все отдают 200 или ожидаемый 302.
- Тестовая транзакция в каждом платёжном модуле. Отдельно. Это занимает 15 минут, но экономит дни.
- Мониторинг error_log первые 48 часов после перехода на production. Не первый день — именно 48 часов. Часть deprecated notices появляется только при определённых сценариях использования (например, при оплате заказа больше определённой суммы).
Когда апгрейд стоит отложить
Три ситуации, когда разумнее подождать.
Активный сезон. Ноябрь — начало предновогоднего пика. Апгрейд PHP в период высокой нагрузки — это риск, который не окупается технически. Лучше задокументировать план и сделать в январе.
Платёжный модуль не обновлялся больше 8 месяцев. Если вендор не выпускал обновлений и не отвечает на вопросы о PHP 8.4 совместимости — апгрейд до получения ответа может остановить приём платежей.
Нет staging, идентичного production. Это единственная ситуация, когда я рекомендую отложить не апгрейд, а сначала поднять staging. Апгрейдить PHP в production без предварительной проверки — это не смелость. Это беспечность.
О том, как планировать апгрейды заранее — и закладывать их ещё до старта проекта — я писал в статье «Мы планируем следующий апгрейд Bitrix ещё до запуска проекта».
Что в итоге
PHP 8.4 — не страшно. Разработчики Bitrix сделали свою часть работы. Страшен пятилетний платёжный модуль, который вендор не обновлял, и хелпер array_find() в php_interface.php, про который никто уже не помнит.
Семь шагов выше занимают полдня на staging. На production — ноль неожиданностей.
Если после апгрейда TTFB вырос — стоит проверить настройки OPcache: PHP OPcache и Bitrix в production. После смены версии PHP validate_timestamps часто стоит временно включить, а потом отключить обратно.
Если проблемы с производительностью шире, чем OPcache, — начать с диагностики проще через «Bitrix не тормозит. Тормозит то, как с ним работают».