Розробка обробників подій для 1С-Бітрікс
Подієва модель Бітрікс — це не просто «повісити гак і забути». Це архітектурний шар, який пронизує всі операції ядра: збереження замовлення, оновлення елемента інфоблоку, авторизацію користувача, відправку пошти. Неправильно написаний обробник уповільнює кожне попадання, створює циклічні виклики або непомітно ламає суміжну логіку. Розберемо, як влаштована система зсередини і як робити правильно.
Анатомія подій в Бітрікс
Ядро генерує подій у ключових точках через клас \Bitrix\Main\EventManager. Кожна подія прив'язана до модуля і має ім'я. Обробник реєструється викликом:
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler(
'sale', // модуль-джерело
'OnSaleOrderBeforeSaved', // ім'я подій
[\MyProject\Sale\OrderHandler::class, 'onBeforeSave'], // callable
100 // sort — пріоритет
);
Реєстрація відбувається в /local/php_interface/init.php — цей файл підключається на кожному попаданні до формування сторінки. Для переносимої логіки обробники реєструються в методі installEvents() власного модуля і видаляються в uninstallEvents().
Before-подій (OnBefore*) — дозволяють змінити дані до збереження або перервати операцію. Обробник повертає об'єкт \Bitrix\Main\EventResult:
return new EventResult(EventResult::ERROR, 'Недопустиме значення поля', 'my_module');
After-подій (OnAfter*) — для реакції на вже виконану дію: відправити сповіщення, написати лог, оновити пов'язані сутності. Повернення значення не впливає на результат операції.
Ключові подій за модулями
| Модуль | Подія | Триггер | Тип |
|---|---|---|---|
iblock |
OnBeforeIBlockElementAdd |
Перед додаванням елемента інфоблоку | Before |
iblock |
OnAfterIBlockElementUpdate |
Після оновлення елемента | After |
iblock |
OnBeforeIBlockElementDelete |
Перед видаленням елемента | Before |
sale |
OnSaleOrderBeforeSaved |
Перед збереженням замовлення | Before |
sale |
OnSalePayOrder |
При оплаті замовлення | After |
sale |
OnSaleStatusOrder |
При зміні статусу замовлення | After |
sale |
OnSaleBasketItemRefreshData |
При перерахунку позиції корзини | Before |
catalog |
OnBeforePriceUpdate |
Перед оновленням ціни | Before |
catalog |
OnAfterCatalogImport1C |
Після завершення обміну з 1С | After |
main |
OnAfterUserAuthorize |
Після авторизації | After |
main |
OnBeforeProlog |
До формування сторінки | Before |
main |
OnEpilog |
Після відправки сторінки | After |
Повний каталог подій кожного модуля — у файлах /bitrix/modules/{module}/lib/events.php або в /bitrix/modules/{module}/include.php.
Структура коду: як організувати десятки обробників
На реальному проекті обробників набирається 20–50. Без структури init.php перетворюється на неуправляєму сваління. Робоча схема:
/local/php_interface/
├── init.php → тільки підключення файлів
├── handlers/
│ ├── SaleHandlers.php → реєстрація подій модуля sale
│ ├── IblockHandlers.php → реєстрація подій модуля iblock
│ └── MainHandlers.php → реєстрація подій модуля main
└── classes/
├── OrderEventHandler.php → логіка обробників замовлень
├── CatalogEventHandler.php
└── UserEventHandler.php
init.php містить лише require_once — ніякої логіки. Файл реєстрації (SaleHandlers.php) — тільки виклики addEventHandler. Класи-обробники — статичні методи з єдиною відповідальністю.
Критичні помилки
Циклічний виклик. Обробник OnAfterIBlockElementUpdate всередину оновлює елемент інфоблоку → подія спрацьовує повторно → нескінченна рекурсія. Захист через статичний прапор:
class CatalogEventHandler
{
private static bool $inProgress = false;
public static function onAfterElementUpdate(array $arFields): void
{
if (self::$inProgress) {
return;
}
self::$inProgress = true;
try {
// логіка оновлення
} finally {
self::$inProgress = false;
}
}
}
Важкі операції в Before-подіях. OnSaleOrderBeforeSaved викликається кілька разів при одному оформленні замовлення (кожен перерахунок). HTTP-запит до зовнішнього API всередину нього — це гарантоване уповільнення. Важкі операції (зовнішні запити, масові UPDATE) виносяться в After-подій або в агентів.
Немає обробки винятків. Необроблене виключення в обробнику може упасти сторінку. Мінімум — try/catch з логуванням через \Bitrix\Main\Diag\Debug::writeToFile().
Порядок виконання. Кілька обробників на одну подію виконуються у порядку зростання sort. Якщо ваш обробник залежить від результату іншого — явно встановлюйте sort. За замовчуванням — 100.
Відладка та профілювання
Для діагностики — модуль perfmon (панель продуктивності в адміністративній частині). Показує, які обробники зареєстровані на кожну подію і скільки часу займає кожен.
Тимчасовий дебаг:
\Bitrix\Main\Diag\Debug::writeToFile(
['event' => 'OnSaleOrderBeforeSaved', 'data' => $arFields],
'order_handler',
'/local/logs/sale.log'
);
Терміни
| Завдання | Тривалість |
|---|---|
| 1–3 простих обробники (сповіщення, логування, заповнення поля) | 2–3 дні |
| Складна логіка (інтеграція з зовнішнім сервісом, валідація замовлення, перерахунок цін) | 5–10 днів |
| Рефакторинг існуючих обробників в init.php (аудит, реорганізація, усунення конфліктів) | 1–2 тижні |
Кожен обробник на OnBeforeProlog виконується при кожному попаданні на сайт. Додати 50 мс до кожного попадання — означає додати години втраченого часу користувачів на день. Подієва модель потужна, але вимагає дисципліни в архітектурі та постійного контролю за продуктивністю.







