События
Иногда бывает необходимо повлиять на ход выполнения какого-нибудь процесса, а поскольку изменять ядро продукта запрещено в системе реализован механизм событий. В ходе выполнения некоторых API функций или частях системы, в определенных точках установлены вызовы определённых функций, так называемых обработчиков события.
Событие, это сообщение программного обеспечения либо его части, которое указывает, что произошло. Обработчик события — код который исполняется для обработки события
Само событие описывается тремя ключевыми составляющими:
- Модуль, являющийся источником события
- Название события
- Параметры события (аргументы события)
Например, событие OnPageStart
модуля main
не имеет параметров события, а просто оповещает что происходит начало выполнения пользовательского кода, а событие OnBeforeCrmDealAdd
модуля crm
передает параметр содержащий поля добавляемой сделки.
Как это работает
В разрезе продукта существует глобальный метод \Bitrix\Main\EventManager
, который хранит все зарегистрированные обработчики событий. Некоторый код инициирует отправку события, EventManager
выполняет поиск всех обработчиков удовлетворяющих условиям поиска и последовательно в порядке приоритетов и добавления обработчиков передает его в функцию-обработчик события.
Что может выступать обработчиком события?
Callback
функцияphp
файл с кодом
Как подписаться на события
Существует 2 способа подписки на события: регистрация и добавление.
- Регистрация обработчика событий осуществляется обычно при модульной разработке, в таком случае все события добавляются в базу данных, таблица называется
b_module_to_module
, добавление необходимо сделать один раз при инсталяции модуля - Добавления обработчика событий, осуществляется в ходе выполнения программы и существует только в рамках исполняемого скрипта, скрипт закончил исполнение, соответственно программа в целом забыла про событие
Помимо способов подписки на события существуют типы событий: старого ядра и нового ядра.
- Обработчики событий старого ядра, принимают сколько угодно аргументов, передаваемых по значению или по ссылке и могут ожидать чего-угодно в результате своей работы. Например, уже упомянутое событие
OnBeforeCrmDealAdd
модуляcrm
принимает массив полей добавляемого элемента по ссылке, а это значит что его можно изменить. Другой пример событиеOnAfterCrmDealAdd
, которое не обращает и не обрабатывает возвращаемый результат - Обработчики событий нового ядра, принимают ровно один аргумент, наследник класса
\Bitrix\Main\Event
и ожидают возврата\Bitrix\Main\EventResult
объекта, допустим null/void
Регистрация обработчика
Для подписки на события необходимо получить инстанс класса EventManager
:
$registerEventHandler = \Bitrix\Main\EventManager::getInstance();
$registerEventHandler->registerEventHandler(
// идентификатор модуля-источника события
$fromModuleId,
// событие на которое мы подписываемся
$eventType,
// идентификатор модуля, который подписывается
$toModuleId,
// класс выполняющий обработку для callback-обработчика, если файловый значит пустая строка
$toClass = '',
// метод класса выполняющий обработку для callback-обработчика, если файловый значит пустая строка
$toMethod = '',
// порядок вызова, чем меньше цифра, тем раньше выполнится
$sort = 100,
// относительный путь к файлу-обработчику, относительно DOCUMENT_ROOT, для callback будет пустая строка
$toPath = '',
// дополнительные аргументы передаваемые callback обработчик или доступные в $args переменной файлового обработчика
$toMethodArg = []
);
Сигнатура метода регистрации обработчика событий старого ядра отличается от нового ядра только названием метода: registerEventHandlerCompatible
$registerEventHandler->registerEventHandlerCompatible(
// идентификатор модуля-источника события
$fromModuleId,
// событие на которое мы подписываемся
$eventType,
// идентификатор модуля, который подписывается
$toModuleId,
// класс выполняющий обработку для callback-обработчика, если файловый значит пустая строка
$toClass = '',
// метод класса выполняющий обработку для callback-обработчика, если файловый значит пустая строка
$toMethod = '',
// порядок вызова, чем меньше цифра, тем раньше выполнится
$sort = 100,
// относительный путь к файлу-обработчику, относительно DOCUMENT_ROOT, для callback будет пустая строка
$toPath = '',
// дополнительные аргументы передаваемые callback обработчик или доступные в $args переменной файлового обработчика
$toMethodArg = []
);
Добавления обработчика
Для подписки на события необходимо получить инстанс класса EventManager
:
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandler(
// идентификатор модуля-источника события
$fromModuleId,
// событие на которое мы подписываемся
$eventType,
// callable обработчик событий, или null если обработка файлом
$callback,
// путь к файлу - обработчика, если указан $callback значит пишем false
$includeFile = false,
// порядок вызова, чем меньше цифра, тем раньше выполнится
$sort = 100
)
В качестве возвращаемого значения методов добавления обработчика событий, возвращается порядковый номер этой регистрации, нужен для отписывания от события.
Сигнатура метода добавления обработчика событий старого ядра, отличается от нового ядра только названием метода addEventHandlerCompatible
$eventManager->addEventHandlerCompatible(
// идентификатор модуля-источника события
$fromModuleId,
// событие на которое мы подписываемся
$eventType,
// callable обработчик событий, или null если обработка файлом
$callback,
// путь к файлу - обработчика, если указан $callback значит пишем false
$includeFile = false,
// порядок вызова, чем меньше цифра, тем раньше выполнится
$sort = 100
)
Применение
Если мы не разрабатываем модуль и хотим подписаться на событие OnAfterCrmDealAdd
модуля crm
, то у нас получиться следующий код:
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandlerCompatible(
'crm',
'OnAfterCrmDealAdd',
function( &$arFields )
{
// ...
}
);
Как отписаться от события
Подобно подписке на события существует механизм отписки который обычно используется в двух случаях:
- Удаление модуля, когда нужно удалить все зарегистрированные события
- Выключение обработчика, когда обработчик события нужен в конкретном месте для конкретного действия и более не требуется
$registerEventHandler->unRegisterEventHandler(
// идентификатор модуля-источника события
$fromModuleId,
// событие на которое мы подписываемся
$eventType,
// идентификатор модуля, который подписывается
$toModuleId,
// класс выполняющий обработку для callback-обработчика, если файловый значит пустая строка
$toClass = "",
// метод класса выполняющий обработку для callback-обработчика, если файловый значит пустая строка
$toMethod = "",
// относительный путь к файлу-обработчику, относительно DOCUMENT_ROOT, для callback будет пустая строка
$toPath = "",
// дополнительные аргументы передаваемые callback обработчик или доступные в $args переменной файлового обработчика
$toMethodArg = []
)
$registerEventHandler->removeEventHandle (
// идентификатор модуля-источника события
$fromModuleId,
// событие на которое мы подписываемся
$eventType,
// идентификатор обработчика события в списке зарегистрированных обработчиков (то число которое вернул метод addEventHandler или addEventHandlerCompatible
$iEventHandlerKey
)
Создание своих событий
В продукте существует возможность дополнять свой код собственными событиями.
Для этого в своем коде нужно создать объект-события \Bitrix\Main\Event
и отправить его на обработку, метод send
на объекте события, а затем по желанию обработать результат.
Пример подписки на события из документации Битрикса:
event = new \Bitrix\Main\Event("mymodule", "OnMacrosProductCreate",array($basketId));
$event->send();
if ($event->getResults())
{
foreach($event->getResults() as $evenResult)
{
if ( $evenResult->getResultType() == \Bitrix\Main\EventResult::SUCCESS )
{
$arMacros["PRODUCTS"] = $evenResult->getParameters();
}
}
}
Несколько советов по созданию свои собственных событий:
- К названию события подойдите максимально осознанно. Не следует создавать по событию на каждый чих — старайтесь группировать их по смыслу
- Обрабатывайте все результаты обработчиков. В приведенном примере, если несколько обработчиков будет установлено одному событию, то в массиве
$arMacros["PRODUCTS"]
будет результата последнего успешно завершенного обработчика - В параметрах события старайтесь передавать объекты. Все объекты в php передаются по ссылке, а значит каждый обработчик будет иметь доступ к последнему актуальному состоянию. Передача скалярных значений и массивов осуществляется по значению, т.е. изменить их в обработчиках будет нельзя
Отладка собственных событий
Иногда возникает необходимость в отладке собственных событий, но не имеет смысла собирать информацию со всей системы. Именно для таких ситуаций в каждом объекте события присутствует 2 дополнительных свойства:
debugMode
обозначающий, что событие работает в режиме отладкиdebugInfo
массив произвольной информации, которой можно получить через методgetDebugInfo
Что нужно, чтобы добавить отладку в свой код?
$event->turnDebugOn();
$event->send();
$event
, необходимо вызвать получение отладочной информации$debugInfo = $event->getDebugInfo();
$debugInfo
будет содержать массив любой информации которую туда добавят обработчики.
$event->addDebugInfo($ar);
В данном случае на $ar
не накладывается никаких ограничений. Это может быть объект, скаляр, массив или любая другая информация которая будет добавлена в ходе выполнения обработчиков событий.
Важное примечание: добавлять туда параметры самого события, обработчик или результат обработки НЕ нужно — эти записи уже будут внесены EventManager
-классом. Вы можете дополнить их своими техническими параметрами в ходе своего обработчика для ясности.