События

Событие — это действие или изменение состояния системы, например, нажатие кнопки пользователем или завершение загрузки данных. События уведомляют части приложения об изменениях, что позволяет системе реагировать на них.

Базовое использование

Чтобы создать и отправить событие, используйте класс 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(
            fromModule: 'my.helpdesk',
            eventType: 'BeforeTicketClose',
            toModuleId: 'my.helpdesk',
            toClass: My\Helpdesk\Internals\Integration\My\Helpdesk\EventHandler\TicketClosedEventHandler::class,
            toMethod: 'handle',
        );
        

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

При удалении модуля удаляйте обработчики с помощью метода unRegisterEventHandler:

\Bitrix\Main\EventManager::getInstance()->unRegisterEventHandler(
            fromModule: 'my.helpdesk',
            eventType: 'BeforeTicketClose',
            toModuleId: 'my.helpdesk',
            toClass: My\Helpdesk\Internals\Integration\My\Helpdesk\EventHandler\TicketClosedEventHandler::class,
            toMethod: 'handle',
        );
        

Есть также способ добавить обработчик динамически через метод addEventHandler:

\Bitrix\Main\EventManager::getInstance()->addEventHandler(
            fromModule: 'my.helpdesk',
            eventType: 'BeforeTicketClose',
            callback: '\My\Helpdesk\Internals\Integration\My\Helpdesk\EventHandler\TicketClosedEventHandler::handle',
        );
        

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

Старые события и режим совместимости

В продукте есть события в старом формате, например OnBeforeUserAdd. У них нет объекта Bitrix\Main\Event, и в обработчик передается произвольный набор аргументов.

Пример обработчика для старого события:

class OnBeforeUserAddEventHandler
        {
            public static function handle(array &$fields): mixed
            {
                // Обработка события
        
                // Результат зависит от конкретного события
                return true;
            }
        }
        

Для регистрации используйте метод registerEventHandlerCompatible:

\Bitrix\Main\EventManager::getInstance()->registerEventHandlerCompatible(
            fromModule: 'main',
            eventType: 'OnBeforeUserAdd',
            toModuleId: 'my.testing',
            toClass: My\Testing\Internals\Integration\Main\EventHandler\OnBeforeUserAddEventHandler::class,
            toMethod: 'handle',
        );
        

Для работы со старыми событиями используются функции:

  • GetModuleEvents

  • AddEventHandler

  • ExecuteModuleEvent

  • ExecuteModuleEventEx

Если вы видите эти функции в коде, обрабатывайте события в режиме совместимости.