PHP 8BitrixRefactoring

Адаптация legacy-Битрикса под PHP 8: исправление ошибок ядра и админки

После переезда старого Битрикса на сервер с PHP 8 админка отдавала 500, импорт падал с TypeError, а drag-and-drop блоков завис. Разбор каждой ошибки, исправления кодом и SQL и порядок, в котором стоит переводить legacy-проект на PHP 8.

Результат100% стабильность админки
Александр Бутаков
Александр Бутаков
обновлено 4 мин чтения

Старый проект на 1С-Битрикс после перехода на PHP 8 обычно ломается не в ядре, а в своём коде: в кастомных модулях, init.php и обработчиках событий. PHP 8 перестал молча прощать null там, где ждут массив или число, и то, что годами «как-то работало», начинает падать с TypeError и ошибкой 500. Лечится это точечно: найти каждое место по логу, добавить проверку типов, привести структуру БД к эталону через «Проверку системы».

Что случилось

Проект переехал на новый сервер с PHP 8, и сразу посыпалось:

  • админка возвращала ошибку 500 — из-за строгой типизации PHP 8 в array_key_exists прилетал null вместо массива;
  • «Проверка системы» показывала неверные типы полей в таблицах БД;
  • drag-and-drop блоков на главной зависал: модуль Push and Pull отвечал 404, а Sortable.js падал;
  • фоновый импорт валился с TypeError, потому что в gmdate передавалось строковое значение.

Всё вместе блокировало управление сайтом и обновление каталога. Сайт вроде бы открывался, но работать с ним было нельзя.

Почему legacy-Битрикс ломается на PHP 8

Три изменения в PHP 8 бьют по старому коду сильнее всего:

  1. Внутренние функции стали строгими. Раньше array_key_exists($key, null) выдавал предупреждение и возвращал false. В PHP 8 это TypeError и фатальная ошибка. То же с count() от не-массива, с арифметикой над нечисловыми строками и с gmdate(), которому передали строку.
  2. Пропал mbstring.func_overload. Очень старые версии ядра Битрикса опирались на перегрузку строковых функций. В PHP 8 этой настройки нет вообще, поэтому сначала ядро нужно обновить до версии с поддержкой PHP 8 — иначе чинить свой код бессмысленно.
  3. Кастомный код никто не обновлял. Ядро и модули Маркетплейса обновляются, а init.php, свои компоненты и доработки чужих модулей — нет. Именно там и живёт большинство поломок.

Шаг 1. Увидеть настоящую ошибку

Ошибка 500 сама по себе ничего не говорит. Включите подробный вывод исключений в /bitrix/.settings.php — временно и лучше на копии сайта. В секции exception_handling переключите debug и задайте файл лога:

PHP
'exception_handling' => [
    'value' => [
        'debug' => true, // TODO: вернуть false после отладки
        'log' => [
            'settings' => [
                // TODO: каталог должен существовать и быть закрыт от веба
                'file' => 'local/logs/php_errors.log',
                'log_size' => 1000000,
            ],
        ],
        // остальные ключи секции оставьте как были
    ],
    'readonly' => false,
],

После этого вместо голой 500 вы увидите файл и строку. Параллельно смотрите лог PHP на сервере — туда попадают ошибки, которые случаются ещё до загрузки ядра.

Шаг 2. Ошибка 500 в админке: проверка типа перед вызовом

Падение админки нашлось в lib/traits/admin.php: в функцию прилетал null вместо массива настроек. Добавил проверку is_array перед вызовом:

PHP
if (is_array($arOption) && array_key_exists($key, $arOption)) {
    // Execute logic
}

Такая правка не меняет логику там, где данные корректные, и перестаёт ронять страницу там, где их нет. Это общий рецепт для большинства TypeError в legacy-коде: не переписывать модуль целиком, а закрыть каждое место, куда может прийти не тот тип.

Шаг 3. Структура базы данных

«Проверка системы» (Настройки → Инструменты → Проверка системы) сравнивает таблицы с эталоном и показывает расхождения. Здесь поля оказались не того типа, что ожидает платформа. Привёл их к стандарту прямым SQL:

SQL
ALTER TABLE b_intranet_invitation_link CHANGE CREATED_BY CREATED_BY int NULL DEFAULT NULL;
ALTER TABLE b_booking_wait_list_item CHANGE CREATED_BY CREATED_BY bigint NOT NULL;

Перед любым ALTER TABLE на рабочей базе — бэкап. И сначала пробуйте исправить расхождения штатно, из самой «Проверки системы»: прямой SQL нужен там, где штатный механизм не справился.

Шаг 4. Зависший drag-and-drop в настройках

Перетаскивание блоков на главной не работало по двум причинам сразу: 404 от модуля Push and Pull и падение скрипта в options.php. Скрипт пытался вызвать replace у undefined — у чекбокса не оказалось атрибута name. Добавил проверку перед вызовом метода:

javascript
var inputName = $(this).find('input[type="checkbox"]').attr('name');
if (inputName) {
  order.push(inputName.replace(current_type+' *', '').replace('* '+current_site, ''));
}

Одна JS-ошибка останавливает весь последующий код на странице. Поэтому «не работает перетаскивание» может означать, что упало вообще что-то другое, выше по коду. Начинайте с консоли браузера.

Шаг 5. TypeError в фоновом импорте

Импорт падал в обработчике CMyLastModified::apply из init.php: в gmdate передавалась дата строкой, а PHP 8 требует число. Исправил обработчик, чтобы он переводил текстовую дату в timestamp перед вызовом. Схема исправления:

PHP
// TODO: адаптируйте под свой обработчик
$timestamp = is_numeric($value) ? (int) $value : strtotime((string) $value);
if ($timestamp !== false) {
    $header = gmdate('D, d M Y H:i:s', $timestamp) . ' GMT';
}

Такие ошибки коварны тем, что всплывают не при открытии сайта, а только в фоне — в агентах, кроне и импорте. Проверяйте их отдельно.

Результат

  • 100% стабильность админки
  • 0 ошибок типов при фоновом импорте
  • 99.9% uptime сервиса после миграции
  • Ошибки структуры БД полностью устранены

Как переводить старый Битрикс на PHP 8

Порядок, который экономит время:

  1. Сделать копию сайта и бэкап базы, переводить сначала копию.
  2. Обновить ядро и модули до версий с поддержкой PHP 8 — без этого остальное бессмысленно.
  3. Включить логирование исключений и пройти по ключевым сценариям: публичная часть, админка, корзина и оформление заказа, формы.
  4. Отдельно запустить фоновые процессы — агенты, крон, импорт из 1С — и прочитать лог.
  5. Прогнать «Проверку системы» и исправить расхождения в БД.
  6. Пройтись по своему коду в поисках опасных мест: array_key_exists, count, implode, работа с датами и арифметика над значениями из запросов и настроек.
  7. Только потом переключать PHP на рабочем сервере — с планом отката.

FAQ

Почему после перехода на PHP 8 админка Битрикса отдаёт ошибку 500? Чаще всего из-за кастомного кода, который передаёт null или строку туда, где PHP 8 строго требует массив или число. Включите логирование исключений в .settings.php, найдите файл и строку и добавьте проверку типа.

Работает ли старый Битрикс на PHP 8? Только после обновления ядра до версии с поддержкой PHP 8. Очень старые версии зависят от mbstring.func_overload, которой в PHP 8 нет.

Как исправить неверные типы полей в базе Битрикса? Запустить «Проверку системы» и исправить расхождения штатно. Если не помогает — привести поля к эталону через ALTER TABLE, предварительно сделав бэкап.

Что делать с TypeError в агентах и импорте? Найти обработчик по логу и привести входные данные к нужному типу до вызова функции. Такие ошибки видны только в фоне, поэтому проверяйте крон и импорт отдельно от публичной части.

Статья была полезной?

Нужна помощь с похожей задачей?

Поддержка сайтов →