Перейти к содержимому

Обработчик события не срабатывает - разбор причин

Обработчик написан и зарегистрирован, а событие проходит мимо него. Разбираем причины по убыванию частоты, начиная со списка зарегистрированных обработчиков.

С чего начать

Смотрим, видит ли платформа обработчик вообще:

$handlers = \Bitrix\Main\EventManager::getInstance()
->findEventHandlers('iblock', 'OnAfterIBlockElementAdd');
printf("обработчиков: %d\n", count($handlers));
print_r(array_column($handlers, 'CALLBACK'));
// SORT в каждой записи показывает порядок вызова обработчиков между собой
// пустой список означает, что дело не в коде обработчика, а в регистрации

Список показывает и постоянные обработчики из базы, и добавленные на текущем запросе. Пустой ответ снимает половину версий сразу: код обработчика тут ни при чём, до него дело не доходит.

Проверяем, что файл регистрации вообще подключается:

// первая строка /local/php_interface/init.php
\Bitrix\Main\Diag\Debug::writeToFile(date('H:i:s'), 'init', 'init.log');
// файла нет в журнале - значит, подключается другой init.php или никакой

Файл обработчиков в своём каталоге отменяет одноимённый файл в каталоге ядра. Скопированный, а не перенесённый файл - типовая причина «код есть, а не работает», и обнаруживается она за одну строку записи в журнал.

Сверяем модуль и имя события:

$em = \Bitrix\Main\EventManager::getInstance();
$em->addEventHandler('sale', 'OnSaleOrderSaved', $callback); // модуль sale
$em->addEventHandlerCompatible('main', 'OnBeforeUserAdd', $cb2); // старое событие
// событие принадлежит модулю: чужой модуль в регистрации даёт тишину

Имя модуля - часть адреса события, а не пояснение. Регистрация того же события с именем другого модуля проходит без ошибок и не срабатывает никогда.

Смотрим, что приходит в сам обработчик:

$em->addEventHandler('iblock', 'OnAfterIBlockElementAdd',
static function ($arg) {
\Bitrix\Main\Diag\Debug::writeToFile(
is_object($arg) ? get_class($arg) : gettype($arg), 'аргумент', 'ev.log');
});
// объект вместо массива означает, что нужен совместимый способ регистрации

Одна строка в журнале отвечает сразу на два вопроса: вызывается ли обработчик и в каком виде приходят аргументы. Дальше разбор идёт либо по регистрации, либо по коду внутри обработчика.

Проверяем, что правка вообще идёт через методы платформы:

$element = new CIBlockElement();
$element->Update($id, $fields); // события поднимает API, а не база
// прямой запрос UPDATE b_iblock_element ... событий не поднимает вовсе

Причины

  1. Файл с регистрацией не подключается примерно 30% случаев

    ПризнакСписок обработчиков пуст, записи из файла регистрации в журнал не попадают.

    ПроверкаПишем строку в журнал первой строкой файла и обновляем страницу сайта.

    Что делатьПереносим код в подключаемый файл своего каталога: одноимённый файл ядра при нём не читается.

  2. Указан не тот модуль или имя события примерно 25% случаев

    ПризнакРегистрация проходит без ошибок, обработчик в списке этого события отсутствует.

    ПроверкаСверяем имя модуля-владельца и точное написание события по документации.

    Что делатьПравим имя модуля и события: регистрация чужого имени не вызывает ошибок и молчит.

  3. Нужен совместимый формат обработчика примерно 20% случаев

    ПризнакОбработчик вызывается, но аргументы приходят не те и код внутри ничего не делает.

    ПроверкаПечатаем полученные аргументы в журнал и смотрим, объект это или привычные значения.

    Что делатьДля старых событий ядра берём совместимый способ регистрации: он передаёт позиционные аргументы.

  4. Запись идёт мимо события примерно 15% случаев

    ПризнакЧерез админку обработчик работает, а при обмене или своём скрипте - нет.

    ПроверкаСмотрим, каким способом меняются данные: методами платформы или прямым запросом к базе.

    Что делатьПереводим правку на методы платформы: события поднимает API, а не сама база данных.

  5. Обработчик отработал, а изменения потерялись примерно 10% случаев

    ПризнакЗаписи в журнале есть, но данные сохраняются в исходном виде.

    ПроверкаПроверяем, возвращает ли обработчик результат и меняет ли поля по ссылке.

    Что делатьВозвращаем значение нужного вида либо правим массив по ссылке: копия изменений никуда не уезжает.

Частые вопросы

Чем регистрация в базе отличается от регистрации на запросе?

Постоянная регистрация хранится в базе и переживает перезагрузку: её делают при установке модуля. Регистрация в подключаемом файле живёт один запрос, не нагружает базу, но её сложнее найти при разборе.

Почему обработчик срабатывает дважды?

Он зарегистрирован и в базе, и в подключаемом файле, либо файл подключается дважды. Список обработчиков события покажет оба вхождения.

Как понять, какие события вообще есть у модуля?

По документации модуля и по вызовам отправки события в его коде. Названия событий не угадываются: опечатка выглядит как отсутствие события.

Обработчик не срабатывает при обмене с 1С.

Обмен пользуется своими методами и поднимает свои события. Ищут событие обмена, а не событие правки элемента вручную.

Можно ли отладить обработчик отладчиком?

На стенде - да, на боевом сайте безопаснее записи в журнал. Строка с аргументами в журнале отвечает на большинство вопросов быстрее пошаговой отладки.

Смежное

Первоисточник