| name | bitrix-events |
| description | Covers Bitrix event system โ new model (Bitrix\Main\Event, EventResult, EventManager::addEventHandler, make:event, make:eventhandler) and old model (OnBefore*/OnAfter* hooks in CIBlock, CUser, CSale and other classic APIs). Applied when integrating modules, entity lifecycle hooks, publishing custom events and subscribing to events of other modules. Key terms โ Event, EventManager, EventResult, OnBefore, OnAfter, handler, subscriber, addEventHandler. |
Bitrix Events
There are two event models: new (OOP, Event + EventResult) and old (string code + handler returning bool/array). For new code โ use the new model. The old model is used for compatibility with the kernel (OnBeforeUserAdd, OnPageStart, ...).
New Model โ Publishing Your Event
1. Create Event Class
php bitrix/bitrix.php make:event PostCreated -m vendor.blog
Since main 25.900 for make:*. On older versions, scaffold the class manually.
File: /local/modules/vendor.blog/lib/Public/Event/Post/PostCreatedEvent.php.
<?php declare(strict_types=1);
namespace Vendor\Blog\Public\Event\Post;
use Bitrix\Main\Event;
final class PostCreatedEvent extends Event
{
public function __construct(
public readonly int $postId,
public readonly int $authorId,
public readonly string $title,
) {
parent::__construct('vendor.blog', self::class, [
'postId' => $this->postId,
'authorId' => $this->authorId,
'title' => $this->title,
]);
}
}
2. Dispatch Event from Service
use Vendor\Blog\Public\Event\Post\PostCreatedEvent;
$event = new PostCreatedEvent($post->getId(), $post->getAuthorId(), $post->getTitle());
$event->send();
foreach ($event->getResults() as $result)
{
if ($result->getType() === \Bitrix\Main\EventResult::ERROR)
{
$this->logger->warning('Subscriber failed', ['errors' => $result->getParameters()]);
}
}
3. Write Handler
php bitrix/bitrix.php make:eventhandler NotifyAuthor \
--event-module=vendor.blog --handler-module=vendor.notify
EventManager invokes handlers via call_user_func_array (class + method). Handlers are not created by ServiceLocator โ do not rely on constructor DI. Resolve services inside the handler method.
<?php declare(strict_types=1);
namespace Vendor\Notify\Internals\Integration\Blog\EventHandler;
use Bitrix\Main\DI\ServiceLocator;
use Bitrix\Main\Event;
use Bitrix\Main\EventResult;
use Vendor\Blog\Public\Event\Post\PostCreatedEvent;
use Vendor\Notify\Application\Service\Notifier;
final class NotifyAuthorHandler
{
public static function handle(Event $event): EventResult
{
if (!$event instanceof PostCreatedEvent)
{
return new EventResult(EventResult::UNDEFINED);
}
$notifier = ServiceLocator::getInstance()->get(Notifier::class);
$result = $notifier->notifyAuthor($event->authorId, $event->title);
return new EventResult(
$result->isSuccess() ? EventResult::SUCCESS : EventResult::ERROR,
$result->getErrorMessages(),
);
}
}
4. Register Handler in install/index.php
\Bitrix\Main\EventManager::getInstance()->registerEventHandler(
fromModule: 'vendor.blog',
eventType: \Vendor\Blog\Public\Event\Post\PostCreatedEvent::class,
toModuleId: 'vendor.notify',
toClass: \Vendor\Notify\Internals\Integration\Blog\EventHandler\NotifyAuthorHandler::class,
toMethod: 'handle',
);
In DoUninstall() โ mandatory unRegisterEventHandler with the same parameters.
Old Model (Compatibility)
Old events have string names: OnBeforeUserAdd, OnAfterUserAdd, OnEpilog, OnPageStart. They pass an array/object of parameters and return:
true/nothing โ continue;
false + $APPLICATION->ThrowException(...) โ cancel action;
- array with
'FIELDS' => [...] โ modify fields (for OnBefore*).
Registering handlers accepting the old signature:
EventManager::getInstance()->registerEventHandlerCompatible(
'main',
'OnAfterUserAdd',
'vendor.blog',
\Vendor\Blog\Internals\Integration\Main\EventHandler\OnAfterUserAddHandler::class,
'handle',
);
The new registerEventHandler also works with old events but adapts them to the Event $event signature โ parameters are retrieved via $event->getParameter('fields'), modification via EventResult.
Order and Chain of Handlers
- Handlers run in ascending
$sort order (default 100).
addEventHandler($fromModuleId, $eventType, $callback, $includeFile = false, $sort = 100) โ $sort is the 5th parameter.
registerEventHandler($fromModuleId, $eventType, $toModuleId, $toClass = '', $toMethod = '', $sort = 100, ...) โ $sort is the 6th parameter.
- The new API (
Event::send()) collects results from all handlers โ the chain is not interrupted even if one returns ERROR.
- In the old API, a single
false can interrupt the action (depends on the calling code in the kernel).
Dynamic Subscription in One Process
For hooks that don't need to be stored in the DB (tests, one-time wrappers):
EventManager::getInstance()->addEventHandler(
'main',
'OnAfterUserAdd',
fn (array $fields) => ,
);
Such registration lives until the end of the request.
Checklist