Developing PHP hooks for 1C-Bitrix events

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
    1183
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    813
  • 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
    657
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Development of PHP hooks for 1C-Bitrix events

The event model of Bitrix is the main mechanism for extending the system without modifying the core. When you need to execute an action on order creation, catalog element change, user authorization, or 1C exchange — you write an event handler. The problem is that improper use turns handlers into a bottleneck: they slow down hits, create cyclic calls, break standard logic. Below — analysis of event architecture and typical mistakes.

How events work in Bitrix

The Bitrix core generates events at key points: before operation (Before events) and after (After events). A handler is registered via EventManager:

use Bitrix\Main\EventManager;

EventManager::getInstance()->addEventHandler(
    'sale',                          // module
    'OnSaleOrderBeforeSaved',        // event
    ['MyHandler', 'onBeforeOrderSave'] // callback
);

Handler registration — in init.php file (/local/php_interface/init.php or /bitrix/php_interface/init.php). For modules — in installEvents() method.

Before events allow modifying data before saving or cancel operation. Return EventResult with type ERROR — operation is interrupted. After events — for reacting to completed action: send notification, write log, update related data.

Catalog of key events

Module Event When triggered Type
iblock OnBeforeIBlockElementAdd Before adding infoblock element Before
iblock OnAfterIBlockElementUpdate After updating element After
sale OnSaleOrderBeforeSaved Before saving order Before
sale OnSalePayOrder On order payment After
sale OnSaleStatusOrder On order status change After
sale OnSaleBasketItemRefreshData On basket recalculation Before
catalog OnBeforePriceUpdate Before price update Before
main OnAfterUserAuthorize After user authorization After
main OnBeforeProlog Before page generation Before

Full list — in module files: /bitrix/modules/{module}/lib/events.php or in dev.1c-bitrix.ru documentation.

Handler architecture: how to stay organized

On a real project there are dozens of handlers. Without organization, init.php becomes a dump. Recommended structure:

/local/php_interface/
├── init.php              → only require registration files
├── handlers/
│   ├── sale.php          → sale module handler registration
│   ├── iblock.php        → iblock module handler registration
│   └── main.php          → main module handler registration
├── classes/
│   ├── SaleHandler.php   → classes with sale handler logic
│   ├── IblockHandler.php
│   └── MainHandler.php

Each handler class — static methods. One method — one event. Inside method — minimal logic: input validation, service class call, result return.

Typical mistakes and how to avoid them

Cyclic calls. OnAfterIBlockElementUpdate handler inside itself updates infoblock element → OnAfterIBlockElementUpdate triggers again → infinite recursion. Solution — static flag:

class IblockHandler
{
    private static bool $isProcessing = false;

    public static function onAfterUpdate($arFields): void
    {
        if (self::$isProcessing) return;
        self::$isProcessing = true;
        // ... logic
        self::$isProcessing = false;
    }
}

Heavy operations in Before events. OnSaleOrderBeforeSaved is called at every order recalculation — which happens several times during checkout. If inside is an HTTP request to external API (address check, delivery calculation) — checkout slows down. Solution: move heavy operations to After events or to queue (agents, \Bitrix\Main\Event with deferred handling).

Missing error handling. Before event handler returns EventResult::ERROR but doesn't set error message. User sees empty error. Always pass meaningful errorMessage to EventResult constructor.

Dependency on handler order. If two modules hook to one event, call order depends on sort parameter when registering (default — 100). If your handler must execute first — set sort=50.

Debugging handlers

Standard way — Bitrix\Main\Diag\Debug::writeToFile(). Writes to /local/php_interface/debug.log (or path set in .settings.php).

More systematic approach — perfmon module. Shows which handlers are registered on each event and how much time each consumes. Enabled in Settings → Performance → Performance Panel.

For Before events of sale module — special note: handler chain breaks on first ERROR. If your handler doesn't execute — check if another handler registered with smaller sort returns ERROR.

Module vs init.php

For one-off handlers (specific to project) — init.php + files in /local/. For reusable logic (external service integration, used on multiple projects) — own module.

Module registers handlers in installEvents() and removes in uninstallEvents(). On module deactivation handlers automatically turn off — which doesn't happen with code in init.php.

Timeline

Task Scope Duration
1–3 simple handlers (notifications, logging) init.php + classes 2–3 days
Complex logic (integration, order validation, price recalculation) Module + tests + documentation 1–1.5 weeks
Refactoring existing handlers (remove from init.php, eliminate conflicts) Audit + reorganization 1–2 weeks

Bitrix's event model is powerful but requires discipline. Each handler is code that executes on every hit (if event is frequent). One inefficient handler on OnBeforeProlog adds 50 ms to all pages — multiply by a million hits per day.