События
Событие — это действие или изменение состояния системы, например, нажатие кнопки пользователем или завершение загрузки данных. События уведомляют части приложения об изменениях, что позволяет системе реагировать на них.
Базовое использование
Чтобы создать и отправить событие, используйте класс Bitrix\Main\Event и его метод send.
Пример отправки события:
$event = new \Bitrix\Main\Event('my.helpdesk', 'TicketClosed');
$event->send();
Пример обработчика для этого события:
class TicketClosedEventHandler
{
public static function handle(\Bitrix\Main\Event $event)
{
// Код для обработки события
}
}
Если нужно передать дополнительные параметры, укажите их третьим аргументом:
$event = new \Bitrix\Main\Event('my.helpdesk', 'TicketClosed', [
'ticketId' => 123,
'closeReason' => '...',
]);
$event->send();
Чтобы обработчик получил данные, используйте методы getParameter и getParameters:
class TicketClosedEventHandler
{
public static function handle(\Bitrix\Main\Event $event)
{
// Получить один параметр
$ticketId = $event->getParameter('ticketId');
// Получить все параметры
$params = $event->getParameters();
$ticketId = $params['ticketId'];
// Обработка события
}
}
Консольные команды
Bitrix Framework предоставляет CLI-команды для удобной работы с событиями.
Создание события
Чтобы создать событие, используйте команду make:event.
Пример создания события:
php bitrix.php make:event TicketClosed -m my.helpdesk —no-interaction
Эта команда создаст файл /local/modules/my.helpdesk/lib/Public/Event/TicketClosedEvent.php:
namespace My\Helpdesk\Public\Event;
use Bitrix\Main\Event;
final class TicketClosedEvent extends Event
{
public function __construct(
public readonly int $ticketId,
public readonly ?string $closeReason,
)
{
parent::__construct(
'my.helpdesk',
'TicketClosed',
);
}
}
Теперь для отправки события можно использовать созданный класс, а не базовый:
$event = new TicketClosedEvent(
ticketId: 123,
closeReason: '...',
);
$event->send();
Данные передаются как свойства события, а не через массив параметров. Это облегчает работу с данными.
Создание обработчика
Чтобы создать обработчик, используйте команду make:eventhandler.
Пример создания обработчика:
php bitrix.php make:eventhandler TicketClosed —event-module my.helpdesk —handler-module my.helpdesk —no-interaction
Эта команда создаст файл local/modules/my.helpdesk/lib/Internals/Integration/My/Helpdesk/EventHandler/TicketClosedEventHandler.php:
namespace My\Helpdesk\Internals\Integration\My\Helpdesk\EventHandler;
use Bitrix\Main\EventResult;
final class TicketClosedEventHandler
{
public static function handle(TicketClosedEvent $event): EventResult
{
# process
return new EventResult(EventResult::SUCCESS);
}
}
В сгенерированном коде нет полного пути к классу события. Нужно указать его в аргументах метода handle или использовать конструкцию use.
Теперь свойства события можно использовать внутри обработчика:
namespace My\Helpdesk\Internals\Integration\My\Helpdesk\EventHandler;
use Bitrix\Main\EventResult;
use My\Helpdesk\Public\Event\TicketClosedEvent;
final class TicketClosedEventHandler
{
public static function handle(TicketClosedEvent $event): EventResult
{
$ticketId = $event->ticketId;
$closeReason = $event->closeReason;
// Обработка события
return new EventResult(EventResult::SUCCESS);
}
}
Результаты событий
Обработчик события может возвращать результат. Это необязательно — можно вернуть null или ничего для методов с типом void. Если обработчик должен повлиять на дальнейшую логику выполнения, например, запретить операцию или передать дополнительные параметры, используйте Bitrix\Main\EventResult.
В качестве примера рассмотрим событие BeforeTicketCloseEvent. Оно вызывается перед закрытием тикета и позволяет обработчикам проверить, можно ли выполнить операцию. Если любой из обработчиков вернет ошибку, процесс закрытия прервется.
final class BeforeTicketCloseEvent extends Event
{
public function __construct(
public readonly int $ticketId,
public readonly ?string $closeReason,
)
{
parent::__construct(
'my.helpdesk',
'BeforeTicketClose',
);
}
}
Проверка перед закрытием тикета:
use Bitrix\Main\Error;
use Bitrix\Main\EventResult;
use Bitrix\Main\Result;
final class TicketService
{
public function close(int $ticketId, string $closeReason): Result
{
$result = new Result();
$error = $this->canClose($ticketId, $closeReason);
if ($error)
{
return $result->addError($error);
}
// Код для закрытия тикета
return $result;
}
private function canClose(int $ticketId, string $closeReason): ?Error
{
$event = new BeforeTicketCloseEvent($ticketId, $closeReason);
$event->send();
foreach ($event->getResults() as $result)
{
if ($result->getType() === EventResult::ERROR)
{
return new Error(
(string)($result->getParameters()['message'] ?? 'Unknown'),
);
}
elseif ($result->getType() === EventResult::SUCCESS)
{
// Обработка успешного результата
}
elseif ($result->getType() === EventResult::UNDEFINED)
{
// Обработка неопределенного результата
}
}
return null;
}
}
Пример обработчика, который возвращает ошибку:
final class BeforeTicketCloseEventHandler
{
public static function handle(BeforeTicketCloseEvent $event): EventResult
{
if (self::hasOpenTasks($event->ticketId))
{
return new EventResult(
EventResult::ERROR,
parameters: [
'message' => \Bitrix\Main\Localization\Loc::getMessage('TICKET_HAS_OPEN_TASKS'),
],
moduleId: 'my.tasktracker',
);
}
return new EventResult(EventResult::SUCCESS);
}
}
Регистрация обработчиков
Чтобы обработчики BeforeTicketCloseEventHandler и TicketClosedEventHandler начали работать, их нужно зарегистрировать. Используйте для этого объект Bitrix\Main\EventManager и его метод registerEventHandler:
\Bitrix\Main\EventManager::getInstance()->registerEventHandler(
fromModuleId: 'my.helpdesk',
eventType: 'BeforeTicketClose',
toModuleId: 'my.helpdesk',
toClass: My\Helpdesk\Internals\Integration\My\Helpdesk\EventHandler\BeforeTicketCloseEventHandler::class,
toMethod: 'handle',
);
Регистрируйте обработчики один раз при установке модуля. Они хранятся в базе данных.
При удалении модуля удаляйте обработчики с помощью метода unRegisterEventHandler:
\Bitrix\Main\EventManager::getInstance()->unRegisterEventHandler(
fromModuleId: 'my.helpdesk',
eventType: 'BeforeTicketClose',
toModuleId: 'my.helpdesk',
toClass: My\Helpdesk\Internals\Integration\My\Helpdesk\EventHandler\BeforeTicketCloseEventHandler::class,
toMethod: 'handle',
);
Есть также способ добавить обработчик динамически через метод addEventHandler:
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
fromModuleId: 'my.helpdesk',
eventType: 'BeforeTicketClose',
callback: '\My\Helpdesk\Internals\Integration\My\Helpdesk\EventHandler\BeforeTicketCloseEventHandler::handle',
);
Метод addEventHandler добавляет обработчик только на время текущего запроса и не сохраняет регистрацию в базе данных. Для обработчиков модуля используйте постоянную регистрацию при установке. Для кода конкретного проекта можно регистрировать обработчики в init.php при каждом подключении пролога.
Подключить системное событие в init.php
Добавьте регистрацию в файл /local/php_interface/init.php. Ядро подключает его до события OnPageStart. Подробнее о назначении файла и порядке подключения — в разделе Файл init.php.
В примере обработчик добавляет HTTP-заголовок X-Project к ответу при начале обработки запроса. Событие OnPageStart не передает аргументы обработчику:
<?php
use Bitrix\Main\Context;
use Bitrix\Main\EventManager;
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true)
{
return;
}
EventManager::getInstance()->addEventHandler(
'main',
'OnPageStart',
static function (): void
{
$response = Context::getCurrent()->getResponse();
$response->addHeader('X-Project', 'helpdesk');
},
);
Для событий классического API с аргументами по ссылке используйте метод addEventHandlerCompatible. Например, добавьте в тот же файл обработчик OnBeforeIBlockElementAdd, чтобы удалить пробелы по краям названия элемента перед добавлением:
\Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
'iblock',
'OnBeforeIBlockElementAdd',
static function (array &$fields): bool
{
$fields['NAME'] = trim((string)($fields['NAME'] ?? ''));
if ($fields['NAME'] === '')
{
global $APPLICATION;
$APPLICATION->ThrowException('Укажите название элемента.');
return false;
}
return true;
},
);
Параметр $fields содержит поля нового элемента. Знак & сохраняет изменения в исходном массиве. Если после удаления пробелов название пустое, метод ThrowException задает сообщение об ошибке, а возврат false отменяет добавление. Этот обработчик действует для всех инфоблоков. Чтобы ограничить его одним инфоблоком, проверяйте поле IBLOCK_ID до изменения названия.
Основные системные события
Выбирайте событие по операции и моменту, когда нужно выполнить код. Для событий классического API используйте режим совместимости, а для событий ORM — класс Bitrix\Main\ORM\EventManager и объект Bitrix\Main\ORM\Event.
| Событие | Модуль или источник | Когда срабатывает | Для чего использовать |
|---|---|---|---|
| OnPageStart | main |
При начале выполняемой части пролога после подключения init.php |
Выполнить код в начале обработки запроса |
| OnBeforeIBlockElementAdd | iblock |
При проверке полей перед добавлением элемента через CIBlockElement::Add |
Изменить поля элемента или запретить добавление |
| OnAfterUserAdd | main |
При завершении попытки добавления пользователя через CUser::Add |
Выполнить действие с учетом результата в поле RESULT, событие вызывается и при ошибке |
| OnBeforeAdd, OnBeforeUpdate, OnBeforeDelete | Класс ORM | При подготовке записи к добавлению, обновлению или удалению | Проверить данные или остановить операцию, а перед добавлением и обновлением — изменить поля |
| OnAdd, OnUpdate, OnDelete | Класс ORM | При выполнении операции перед SQL-запросом | Выполнить сопутствующую логику перед записью в базу данных |
| OnAfterAdd, OnAfterUpdate, OnAfterDelete | Класс ORM | При завершении успешной операции с записью | Выполнить связанное действие после изменения данных |
События ORM относятся к конкретному классу и операции с его данными. Они не заменяют события классического API, например OnBeforeIBlockElementAdd. Примеры регистрации и обработки результатов ORM приведены в разделе События.
Старые события и режим совместимости
В продукте есть события в старом формате, например OnBeforeUserAdd. У них нет объекта Bitrix\Main\Event, и в обработчик передается произвольный набор аргументов.
Пример обработчика для старого события:
class OnBeforeUserAddEventHandler
{
public static function handle(array &$fields): mixed
{
// Обработка события
// Результат зависит от конкретного события
return true;
}
}
Для регистрации используйте метод registerEventHandlerCompatible:
\Bitrix\Main\EventManager::getInstance()->registerEventHandlerCompatible(
fromModuleId: 'main',
eventType: 'OnBeforeUserAdd',
toModuleId: 'my.testing',
toClass: My\Testing\Internals\Integration\Main\EventHandler\OnBeforeUserAddEventHandler::class,
toMethod: 'handle',
);
Для работы со старыми событиями используются функции:
-
GetModuleEvents -
AddEventHandler -
ExecuteModuleEvent -
ExecuteModuleEventEx
Если вы видите эти функции в коде, обрабатывайте события в режиме совместимости.