Development of 1C-Bitrix event handlers

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Development of Event Handlers for 1C-Bitrix

The event model in Bitrix is not just "attach a hook and forget". It's an architectural layer that permeates all core operations: saving an order, updating an iblock element, user authorization, sending mail. An incorrectly written handler slows down every hit, creates circular calls, or silently breaks adjacent logic. Let's understand how the system works and how to do it right.

Anatomy of Events in Bitrix

The core generates events at key points via the \Bitrix\Main\EventManager class. Each event is tied to a module and has a name. A handler is registered with:

use Bitrix\Main\EventManager;

EventManager::getInstance()->addEventHandler(
    'sale',                          // source module
    'OnSaleOrderBeforeSaved',        // event name
    [\MyProject\Sale\OrderHandler::class, 'onBeforeSave'], // callable
    100                              // sort — priority
);

Registration happens in /local/php_interface/init.php — this file is included on every hit before page formation. For portable logic, handlers register in the module's installEvents() method and uninstall in uninstallEvents().

Before-events (OnBefore*) — allow you to modify data before save or abort the operation. The handler returns an \Bitrix\Main\EventResult object:

return new EventResult(EventResult::ERROR, 'Invalid field value', 'my_module');

After-events (OnAfter*) — for reacting to an action already performed: send notification, write log, update related entities. Return value doesn't affect the operation result.

Key Events by Module

Module Event Trigger Type
iblock OnBeforeIBlockElementAdd Before adding iblock element Before
iblock OnAfterIBlockElementUpdate After element update After
iblock OnBeforeIBlockElementDelete Before element deletion Before
sale OnSaleOrderBeforeSaved Before order save Before
sale OnSalePayOrder On order payment After
sale OnSaleStatusOrder On order status change After
sale OnSaleBasketItemRefreshData On basket item recalc Before
catalog OnBeforePriceUpdate Before price update Before
catalog OnAfterCatalogImport1C After 1C exchange completes After
main OnAfterUserAuthorize After authorization After
main OnBeforeProlog Before page generation Before
main OnEpilog After page sent After

Full event catalog for each module is in /bitrix/modules/{module}/lib/events.php or /bitrix/modules/{module}/include.php.

Code Structure: How to Organize Dozens of Handlers

A real project accumulates 20–50 handlers. Without structure, init.php becomes an unmanageable dump. Working scheme:

/local/php_interface/
├── init.php                   → only file inclusion
├── handlers/
│   ├── SaleHandlers.php       → sale module event registration
│   ├── IblockHandlers.php     → iblock module event registration
│   └── MainHandlers.php       → main module event registration
└── classes/
    ├── OrderEventHandler.php  → order handler logic
    ├── CatalogEventHandler.php
    └── UserEventHandler.php

init.php contains only require_once — no logic. Registration file (SaleHandlers.php) — only addEventHandler calls. Handler classes — static methods with single responsibility.

Critical Errors

Circular calls. Handler OnAfterIBlockElementUpdate internally updates an iblock element → event fires again → infinite recursion. Protection via static flag:

class CatalogEventHandler
{
    private static bool $inProgress = false;

    public static function onAfterElementUpdate(array $arFields): void
    {
        if (self::$inProgress) {
            return;
        }
        self::$inProgress = true;
        try {
            // update logic
        } finally {
            self::$inProgress = false;
        }
    }
}

Heavy operations in Before-events. OnSaleOrderBeforeSaved fires several times during single checkout (each recalc). HTTP request to external API inside it — guaranteed slowdown. Heavy operations (external requests, mass UPDATE) go to After-events or agents.

No exception handling. Unhandled exception in handler can crash the page. Minimum — try/catch with logging via \Bitrix\Main\Diag\Debug::writeToFile().

Execution order. Multiple handlers for one event execute in ascending sort order. If your handler depends on another's result — set sort explicitly. Default — 100.

Debugging and Profiling

Use the perfmon module (performance panel in admin area) for diagnostics. Shows which handlers are registered for each event and how much time each takes.

Temporary debug:

\Bitrix\Main\Diag\Debug::writeToFile(
    ['event' => 'OnSaleOrderBeforeSaved', 'data' => $arFields],
    'order_handler',
    '/local/logs/sale.log'
);

Timeline

Task Duration
1–3 simple handlers (notifications, logging, field filling) 2–3 days
Complex logic (external service integration, order validation, price recalc) 5–10 days
Refactoring existing handlers in init.php (audit, reorganization, conflict resolution) 1–2 weeks

Each handler on OnBeforeProlog executes on every site hit. Adding 50 ms to each hit means adding hours of lost user time per day. The event model is powerful, but requires discipline in architecture and constant performance monitoring.