Старый проект на 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 бьют по старому коду сильнее всего:
- Внутренние функции стали строгими. Раньше
array_key_exists($key, null)выдавал предупреждение и возвращалfalse. В PHP 8 этоTypeErrorи фатальная ошибка. То же сcount()от не-массива, с арифметикой над нечисловыми строками и сgmdate(), которому передали строку. - Пропал
mbstring.func_overload. Очень старые версии ядра Битрикса опирались на перегрузку строковых функций. В PHP 8 этой настройки нет вообще, поэтому сначала ядро нужно обновить до версии с поддержкой PHP 8 — иначе чинить свой код бессмысленно. - Кастомный код никто не обновлял. Ядро и модули Маркетплейса обновляются, а
init.php, свои компоненты и доработки чужих модулей — нет. Именно там и живёт большинство поломок.
Шаг 1. Увидеть настоящую ошибку
Ошибка 500 сама по себе ничего не говорит. Включите подробный вывод исключений в /bitrix/.settings.php — временно и лучше на копии сайта. В секции exception_handling переключите debug и задайте файл лога:
'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 перед вызовом:
if (is_array($arOption) && array_key_exists($key, $arOption)) {
// Execute logic
}Такая правка не меняет логику там, где данные корректные, и перестаёт ронять страницу там, где их нет. Это общий рецепт для большинства TypeError в legacy-коде: не переписывать модуль целиком, а закрыть каждое место, куда может прийти не тот тип.
Шаг 3. Структура базы данных
«Проверка системы» (Настройки → Инструменты → Проверка системы) сравнивает таблицы с эталоном и показывает расхождения. Здесь поля оказались не того типа, что ожидает платформа. Привёл их к стандарту прямым 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. Добавил проверку перед вызовом метода:
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 перед вызовом. Схема исправления:
// 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
Порядок, который экономит время:
- Сделать копию сайта и бэкап базы, переводить сначала копию.
- Обновить ядро и модули до версий с поддержкой PHP 8 — без этого остальное бессмысленно.
- Включить логирование исключений и пройти по ключевым сценариям: публичная часть, админка, корзина и оформление заказа, формы.
- Отдельно запустить фоновые процессы — агенты, крон, импорт из 1С — и прочитать лог.
- Прогнать «Проверку системы» и исправить расхождения в БД.
- Пройтись по своему коду в поисках опасных мест:
array_key_exists,count,implode, работа с датами и арифметика над значениями из запросов и настроек. - Только потом переключать PHP на рабочем сервере — с планом отката.
FAQ
Почему после перехода на PHP 8 админка Битрикса отдаёт ошибку 500?
Чаще всего из-за кастомного кода, который передаёт null или строку туда, где PHP 8 строго требует массив или число. Включите логирование исключений в .settings.php, найдите файл и строку и добавьте проверку типа.
Работает ли старый Битрикс на PHP 8?
Только после обновления ядра до версии с поддержкой PHP 8. Очень старые версии зависят от mbstring.func_overload, которой в PHP 8 нет.
Как исправить неверные типы полей в базе Битрикса?
Запустить «Проверку системы» и исправить расхождения штатно. Если не помогает — привести поля к эталону через ALTER TABLE, предварительно сделав бэкап.
Что делать с TypeError в агентах и импорте? Найти обработчик по логу и привести входные данные к нужному типу до вызова функции. Такие ошибки видны только в фоне, поэтому проверяйте крон и импорт отдельно от публичной части.