Разработка PHP-хуков для событий 1С-Битрикс
Событийная модель Битрикс — основной механизм расширения системы без модификации ядра. Когда нужно выполнить действие при создании заказа, изменении элемента каталога, авторизации пользователя или обмене с 1С — пишется обработчик события. Проблема в том, что при неграмотном использовании обработчики превращаются в узкое место: замедляют хиты, создают циклические вызовы, ломают штатную логику. Ниже — разбор архитектуры событий и типовых ошибок.
Как работают события в Битрикс
Ядро Битрикс генерирует события в ключевых точках: до операции (Before-события) и после (After-события). Обработчик регистрируется через EventManager:
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler(
'sale', // модуль
'OnSaleOrderBeforeSaved', // событие
['MyHandler', 'onBeforeOrderSave'] // callback
);
Регистрация обработчиков — в файле init.php (/local/php_interface/init.php или /bitrix/php_interface/init.php). Для модулей — в методе installEvents().
Before-события позволяют модифицировать данные до сохранения или отменить операцию. Возвращаете EventResult с типом ERROR — операция прерывается. After-события — для реакции на уже совершённое действие: отправить уведомление, записать лог, обновить связанные данные.
Каталог ключевых событий
| Модуль | Событие | Когда срабатывает | Тип |
|---|---|---|---|
iblock |
OnBeforeIBlockElementAdd |
Перед добавлением элемента инфоблока | Before |
iblock |
OnAfterIBlockElementUpdate |
После обновления элемента | After |
sale |
OnSaleOrderBeforeSaved |
Перед сохранением заказа | Before |
sale |
OnSalePayOrder |
При оплате заказа | After |
sale |
OnSaleStatusOrder |
При смене статуса заказа | After |
sale |
OnSaleBasketItemRefreshData |
При пересчёте корзины | Before |
catalog |
OnBeforePriceUpdate |
Перед обновлением цены | Before |
main |
OnAfterUserAuthorize |
После авторизации пользователя | After |
main |
OnBeforeProlog |
До формирования страницы | Before |
Полный список — в файлах модулей: /bitrix/modules/{module}/lib/events.php или в документации dev.1c-bitrix.ru.
Архитектура обработчиков: как не запутаться
На реальном проекте обработчиков — десятки. Без организации init.php превращается в свалку. Рекомендуемая структура:
/local/php_interface/
├── init.php → только require файлов регистрации
├── handlers/
│ ├── sale.php → регистрация обработчиков модуля sale
│ ├── iblock.php → регистрация обработчиков модуля iblock
│ └── main.php → регистрация обработчиков модуля main
├── classes/
│ ├── SaleHandler.php → классы с логикой обработчиков sale
│ ├── IblockHandler.php
│ └── MainHandler.php
Каждый класс-обработчик — статические методы. Один метод — одно событие. Внутри метода — минимум логики: валидация входных данных, вызов сервисного класса, возврат результата.
Типовые ошибки и как их избежать
Циклические вызовы. Обработчик OnAfterIBlockElementUpdate внутри себя обновляет элемент инфоблока → снова срабатывает OnAfterIBlockElementUpdate → бесконечная рекурсия. Решение — статический флаг:
class IblockHandler
{
private static bool $isProcessing = false;
public static function onAfterUpdate($arFields): void
{
if (self::$isProcessing) return;
self::$isProcessing = true;
// ... логика
self::$isProcessing = false;
}
}
Тяжёлые операции в Before-событиях. OnSaleOrderBeforeSaved вызывается при каждом пересчёте заказа — а это происходит несколько раз за оформление. Если внутри — HTTP-запрос к внешнему API (проверка адреса, расчёт доставки) — оформление заказа тормозит. Решение: тяжёлые операции выносить в After-события или в очередь (агенты, \Bitrix\Main\Event с отложенной обработкой).
Отсутствие обработки ошибок. Обработчик Before-события возвращает EventResult::ERROR, но не задаёт сообщение об ошибке. Пользователь видит пустую ошибку. Всегда передавайте внятный errorMessage в конструктор EventResult.
Зависимость от порядка обработчиков. Если два модуля вешают обработчики на одно событие, порядок вызова зависит от параметра sort при регистрации (по умолчанию — 100). Если ваш обработчик должен выполниться первым — задайте sort=50.
Отладка обработчиков
Стандартный способ — Bitrix\Main\Diag\Debug::writeToFile(). Пишет в файл /local/php_interface/debug.log (или в путь, заданный в .settings.php).
Более системный подход — модуль perfmon. Показывает, какие обработчики зарегистрированы на каждое событие и сколько времени каждый потребляет. Включается в Настройки → Производительность → Панель производительности.
Для Before-событий модуля sale — особенность: цепочка обработчиков прерывается при первом ERROR. Если ваш обработчик не вызывается — проверьте, не возвращает ли ERROR другой обработчик, зарегистрированный с меньшим sort.
Модуль vs init.php
Для одноразовых обработчиков (специфика конкретного проекта) — init.php + файлы в /local/. Для переносимой логики (интеграция с внешним сервисом, используемая на нескольких проектах) — собственный модуль.
Модуль регистрирует обработчики в installEvents() и удаляет в uninstallEvents(). При деактивации модуля обработчики автоматически отключаются — чего не происходит с кодом в init.php.
Сроки
| Задача | Объём | Срок |
|---|---|---|
| 1–3 простых обработчика (уведомления, логирование) | init.php + классы | 2–3 дня |
| Комплексная логика (интеграция, валидация заказов, пересчёт цен) | Модуль + тесты + документация | 1–1.5 недели |
| Рефакторинг существующих обработчиков (вынос из init.php, устранение конфликтов) | Аудит + реорганизация | 1–2 недели |
Событийная модель Битрикс мощная, но требует дисциплины. Каждый обработчик — это код, который выполняется на каждом хите (если событие частое). Один неоптимальный обработчик на OnBeforeProlog добавляет 50 мс ко всем страницам сайта — а это 50 мс × миллион хитов в день.







