Claude Skill

bitrix-events

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

LLM Mart · 0 points · 9 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download bxmaximum-bitrix-framework-skills-skills_bitrix-events-66c40e0.zip · 2 KB
Part of bxmaximum/bitrix-framework-skills — 38 skills

Install

skills CLI npx skills add https://github.com/bxmaximum/bitrix-framework-skills/tree/main/skills/bitrix-events
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install bxmaximum-bitrix-framework-skills@llmmart
Git git clone https://github.com/bxmaximum/bitrix-framework-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole bxmaximum/bitrix-framework-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

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);
        }

        /** @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

\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

  • 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.
Files (bitrix-framework-skills)
  • SKILL.md 6 KB
    ---
    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
    
    ```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.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related