Call to a member function on null - причины по убыванию частоты
Страница падает с сообщением об обращении к методу у пустого значения. Разбираем причины в порядке убывания частоты.
Как проверить
Читаем сообщение целиком:
grep -i "on null" /var/log/php-fpm/error.log | tail -5# имя метода сразу называет виновника: IncludeComponent - это $APPLICATION,# IsAuthorized и GetUserGroupArray - $USER, getBasket - объект заказаСообщение называет метод, файл и строку, и метод здесь важнее всего. По его имени понятно, какой именно объект оказался пустым, а дальше остаётся ответить, почему его не существует в этом месте.
Объявляем глобальные объекты во включаемом файле:
global $APPLICATION, $USER; // во включаемом файле их нет по умолчанию$APPLICATION->IncludeComponent('bitrix:news.list', '', []);// это самая частая причина: файл из /include/ подключается внутри функции// в шаблоне компонента объявлять их не нужно: платформа делает это самаВключаемые файлы подключаются внутри функции ядра, а не в глобальной области.
Переменные $APPLICATION и $USER там не видны, пока их не объявили глобальными
явно - одной строкой в начале файла.
Переносим ранний код в событие:
// в init.php объекта пользователя ещё не существует: он создаётся позже\Bitrix\Main\EventManager::getInstance()->addEventHandler( 'main', 'OnPageStart', ['\\Vendor\\Init', 'onPageStart']);// в обработчике этого события глобальные объекты ядра уже на месте// создавать объект пользователя руками не стоит: методы вернут пустотуФайл начальной настройки выполняется раньше, чем создаётся объект пользователя. Обращение к нему прямо в этом файле падает всегда, и лечится это переносом кода в событие начала страницы, а не созданием объекта руками.
Проверяем, что объект вообще нашёлся:
$order = \Bitrix\Sale\Order::load($orderId);if ($order === null) { return; // заказа нет: дальше идти нельзя}$element = \Bitrix\Iblock\ElementTable::getById($id)->fetchObject();// выборка возвращает пустоту молча, а падает уже следующая строка с методомif (!$element) { throw new \RuntimeException("элемент {$id} не найден"); // понятное сообщение}Выборка, не нашедшая записи, не бросает исключений. Она возвращает пустое значение, и ошибка появляется строкой ниже, в первом же обращении к методу - на чужой, совершенно исправной строке кода.
Причины
-
Глобальные объекты не объявлены во включаемом файле примерно 30% случаев
ПризнакПадает файл из включаемых областей, а метод в сообщении принадлежит приложению или пользователю.
ПроверкаСмотрим начало файла: есть ли объявление нужных глобальных переменных.
Что делатьОбъявляем их глобальными в первой строке файла; в шаблонах компонентов это делает сама платформа.
-
Код выполняется до создания объектов ядра примерно 25% случаев
ПризнакОшибка приходит из файла начальной настройки или из обработчика самого раннего события.
ПроверкаПроверяем, на каком этапе выполняется код и существует ли к этому моменту нужный объект.
Что делатьПереносим обращение в событие начала страницы, где объекты ядра уже созданы платформой.
-
Результат выборки не проверен примерно 20% случаев
ПризнакМетод в сообщении принадлежит заказу, элементу или корзине, а ошибка появляется не на всех страницах.
ПроверкаСмотрим строку выше по коду: есть ли проверка того, что запись нашлась.
Что делатьДобавляем проверку результата и осмысленный ответ на его отсутствие вместо продолжения работы.
-
Ядро платформы не подключено вовсе примерно 15% случаев
ПризнакФайл открывается по прямой ссылке или запускается из консоли, а падает на первой же строке.
ПроверкаПроверяем, подключается ли пролог платформы до обращения к её объектам.
Что делатьПодключаем пролог в начале файла либо переносим код туда, где ядро уже поднято.
-
Файлы разошлись после переноса или обновления примерно 10% случаев
ПризнакОшибка появилась разом на всём сайте сразу после переноса, копирования или обновления.
ПроверкаСравниваем состав файлов с исходной копией и смотрим журнал обновления системы.
Что делатьВозвращаем недостающие файлы из резервной копии; частичное копирование каталога ядра всегда заканчивается так.
Если ничего не помогло
Смотрим трассировку снизу вверх, а не сверху вниз. Верхняя строка называет место падения, а причину обычно видно в нижних: там указан файл проекта, который вызвал упавший код из неподходящего места.
Проверяем совпадение символьных кодов свойств с именами полей. В новом ядре свойство с кодом вроде даты активности перекрывает одноимённое поле, и обращение к нему возвращает пустоту вместо значения.
Ищем ошибку в почтовом шаблоне. Тело письма выполняется отдельно от страницы, и вставленный туда вызов компонента падает именно с этим сообщением, заваливая журнал одинаковыми записями.
Частые вопросы
Почему $APPLICATION пустой во включаемом файле?
Файл подключается внутри функции ядра, а не в глобальной области, поэтому глобальные переменные там не видны. Их объявляют глобальными в первой строке файла - в шаблонах компонентов это делается за вас.
Как обратиться к пользователю в файле начальной настройки?
Никак: объект пользователя создаётся позже этого файла. Код переносят в обработчик события начала страницы, где объект уже существует и работает штатно.
Создавать объект пользователя вручную - нормальное решение?
Это известный обходной путь, но он маскирует ошибку, а не решает её: методы такого объекта возвращают пустоту. Правильный путь - выполнять код тогда, когда платформа объект уже создала.
Ошибка появляется только на некоторых страницах.
Значит, падает не платформа, а непроверенный результат выборки: на этих страницах запись не нашлась. Проверка результата перед обращением к методу закрывает такие случаи полностью.
После переноса сайта ошибка на всех страницах сразу.
Скорее всего, каталог ядра скопирован не полностью: часть файлов не доехала. Копируют его целиком или разворачивают из штатной резервной копии, а не через выборочную загрузку по FTP.
Смежное
- Ошибки сервера - оглавление подтемы
- Call to undefined function: причины по убыванию частоты - соседняя ошибка с другой причиной
- Ошибки 500 и 502: где искать причину и чем они отличаются - как это выглядит снаружи
- Обработчик события: регистрация, аргументы, отмена действия - куда переносить ранний код
- Перенос сайта на другой сервер: через восстановление и вручную - как переносить файлы целиком
- Инфраструктура - устройство сервера и окружения