---
name: Bitrix Events
slug: bitrix-events
category: DevOps
description: "Comprehensive guide to the Bitrix event system covering new OOP model (Event, EventResult) and legacy hooks. Use for integrating modules, handling entity lifecycle events, publishing custom events, and subscribing to other modules' events."
github: "https://github.com/bxmaximum/bitrix-framework-skills/tree/main/skills/bitrix-events"
stars: 19
forks: 3
install: "npx degit https://github.com/bxmaximum/bitrix-framework-skills/tree/main/skills/bitrix-events ~/.claude/skills/bitrix-events"
installs_to: ~/.claude/skills/bitrix-events
source_path: skills/bitrix-events/SKILL.md
collection_size: 25
category_size: 798
collection_url: "https://dirskills.com/collections/bxmaximum/bitrix-framework-skills"
added: 2026-08-11T07:22:30.816Z
last_synced: 2026-08-11T07:22:30.816Z
canonical_url: "https://dirskills.com/skills/bitrix-events"
---

# Bitrix Events

Comprehensive guide to the Bitrix event system covering new OOP model (Event, EventResult) and legacy hooks. Use for integrating modules, handling entity lifecycle events, publishing custom events, and subscribing to other modules' events.

**Install:**

```bash
npx degit https://github.com/bxmaximum/bitrix-framework-skills/tree/main/skills/bitrix-events ~/.claude/skills/bitrix-events
```

## README

# 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

```bash
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
<?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

```php
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

```bash
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
<?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);
        }

        /** @var Notifier $notifier */
        $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`

```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:

```php
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):

```php
EventManager::getInstance()->addEventHandler(
    'main',
    'OnAfterUserAdd',
    fn (array $fields) => /* ... */,
);
```

Such registration lives until the end of the request.

## Checklist

- [ ] Public event files — in `/lib/Public/Event/<Aggregate>/`.
- [ ] Handlers of other modules' events — in `/lib/Internals/Integration/<OtherModule>/EventHandler/`.
- [ ] Registration and unregistration of handlers as a pair in `DoInstall`/`DoUninstall`.
- [ ] Custom events use `Bitrix\Main\Event` + `EventResult` instead of returning arrays.
- [ ] Handler has no constructor DI — resolve services inside the method via `ServiceLocator::get()`.
- [ ] Handler is idempotent and does not crash — wrap everything in `try/catch` with logging.
- [ ] Heavy logic is moved to a queue (`Messenger` via `$message->send()`), handler only dispatches a task.
