Розробка PHP-хуків для подій 1С-Bitrix
Подійна модель Bitrix — основний механізм розширення системи без модифікації ядра. Коли потрібно виконати дію при створенні замовлення, зміні елемента каталогу, авторизації користувача або обміні з 1С — пишеться обробник подій. Проблема в тому, що при неграмотному використанні обробники перетворюються на вузьке місце: сповільнюють хіти, створюють циклічні виклики, ломають штатну логіку. Нижче — розбір архітектури подій та типових помилок.
Як працюють подій в Bitrix
Ядро Bitrix генерує подій у ключових точках: до операції (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 тижні |
Подійна модель Bitrix потужна, але потребує дисципліни. Кожен обробник — це код, який виконується на кожному хіті (якщо подія частота). Один неоптимальний обробник на OnBeforeProlog додає 50 мс до всіх сторінок — а це 50 мс × мільйон хітів на день.







