Разработка обработчиков событий 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 мс к каждому хиту — значит добавить часы потерянного времени пользователей в день. Событийная модель мощная, но требует дисциплины в архитектуре и постоянного контроля за производительностью.







