init.php битрикс - первый файл, в который лезут, когда нужно быстро добавить обработчик события, подключить свою функцию или поменять поведение админки без правки ядра. За несколько лет работы с проектами на 1С-Битрикс я регулярно вижу один и тот же сценарий: файл разрастается до полутора-двух тысяч строк, в нём вперемешку лежат обработчики событий, куски вёрстки, забытые var_dump и открытые API-ключи. Разберу, что там действительно место, а что рано или поздно положит сайт.
Где лежит init.php и когда Битрикс его подключает
Файл живёт по пути /bitrix/php_interface/init.php. По умолчанию его нет, создаётся вручную при первой необходимости. Ядро подключает его на каждом хите, после инициализации модулей, но до обработки конкретной страницы, поэтому здесь удобно вешать глобальные обработчики и объявлять константы, которые должны быть доступны везде.
Если проект мультисайтовый, добавляется ещё один уровень: /bitrix/php_interface/sites/s1/init.php, s2, s3 и так далее подключаются в дополнение к общему файлу, а не вместо него. Это критично для интеграций с эквайрингом или доставкой: если обработчик оплаты через Т‑Банк или расчёта доставки через СДЭК прописан только в общем init.php, он сработает на всех сайтах пропускной сети сразу, и это не всегда нужное поведение.
Что можно и нужно класть в init.php
За что я обычно держусь в этом файле:
- обработчики событий ядра через
AddEventHandler - константы через
define(), которые влияют на поведение сайта целиком (флаги режима, пути, настройки кеша) - автозагрузку собственных классов через
spl_autoload_registerили подключение composer-автозагрузчика одной строкой - небольшие глобальные функции-хелперы, которые действительно нужны в нескольких разных компонентах и модулях
Простой пример обработчика, который вешается на сохранение заказа и передаёт его во внешнюю систему:
<?php
AddEventHandler("sale", "OnSaleOrderSaved", "myOrderSavedHandler");
function myOrderSavedHandler($order)
{
$orderId = $order->getId();
// отправляем заказ во внешнюю систему логистики
}
?>
Всё, что не влияет на весь проект целиком, я стараюсь не тащить сюда, а раскладывать по компонентам и модулям, иначе через полгода в файле уже не разобраться, кто и зачем повесил очередной обработчик.
Бесплатный материал
🎁 Полезный скрипт в подарок
Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.
Без спама. Отписка в 1 клик.
Пример из практики: обработчик для доставки и оплаты
Типичная задача на живом проекте: интеграция с СДЭК для расчёта стоимости доставки и с Т‑Банком для приёма оплаты. Обычно вешаю обработчик на OnSaleStatusOrderChange или OnSaleOrderSaved, внутри которого дергаю внешний API. Важный момент, который часто упускают: если внешний сервис недоступен, обработчик не должен ронять оформление заказа целиком, поэтому оборачиваю вызов в try/catch и пишу ошибку в лог, а не выбрасываю необработанное исключение наружу.
<?php
AddEventHandler("sale", "OnSaleStatusOrderChange", "sendToDeliveryService");
function sendToDeliveryService($orderId, $val)
{
try {
$order = BitrixSaleOrder::load($orderId);
// передача статуса заказа во внешний сервис доставки или оплаты
} catch (Exception $e) {
CEventLog::Add([
"SEVERITY" => "ERROR",
"AUDIT_TYPE_ID" => "DELIVERY_SYNC",
"MODULE_ID" => "sale",
"DESCRIPTION" => $e->getMessage(),
]);
}
}
?>
Такой подход экономит нервы на проде: если СДЭК или платёжный шлюз временно недоступны, заказ всё равно сохранится, а причина сбоя останется в логе событий, а не потеряется в общем потоке ошибок PHP.
Чего в init.php класть нельзя
Список вещей, которые встречал в реальных проектах и которые лучше сразу выносить в другое место:
- echo и вывод html - код в init.php выполняется до рендера шаблона, поэтому вывод здесь либо появится не там, где ожидается, либо ломает заголовки и работу с сессией
- тяжёлые SQL-запросы без кеша - обработчик выполняется на каждый хит или на каждое событие, и без
CPHPCacheтакой запрос кладёт базу на нагруженном проекте - пароли и токены в открытом виде - если ключ эквайринга или CRM лежит прямо в define(), он попадает в git и в бэкапы; храню такие данные в конфиге вне корня сайта или в переменных окружения
- забытый отладочный код - var_dump, print_r без обёртки в условие по IP или окружению, оставшийся после деплоя
- require тяжёлых библиотек без автозагрузки - подключение чего-то вроде полного SDK на каждый хит раздувает память и время ответа
- бизнес-логику конкретного компонента - расчёт цены, генерация markup, работа с конкретной страницей должны жить в компоненте или модуле, а не в глобальном файле
init.php или свой модуль: где проходит граница
Когда обработчиков становится больше десятка и они начинают тянуть за собой свои классы, константы и настройки, я перестаю держать всё в одном файле и выношу код в локальный модуль. Разница на практике такая:
| Критерий | init.php | Локальный модуль |
| Объём кода | комфортно до 200-300 строк | любой, разнесён по файлам и классам |
| Перенос на другой проект | вручную, легко забыть кусок | копируется папкой, ставится через install |
| Автотесты | практически не тестируется | подключается PHPUnit |
| Автозагрузка классов | ручной spl_autoload_register | штатный CModule::IncludeModule |
| Читаемость через год | падает с ростом файла | структура файлов держит порядок |
Если код обрастает собственными сущностями, таблицами и API, а не парой обработчиков, дешевле сразу оформить его как модуль, чем потом распутывать init.php на 1500 строк. Под такие задачи заказывают разработку кастомного модуля под конкретный процесс, и это обычно окупается уже на втором обновлении проекта, когда не приходится вспоминать, что где лежит.
Производительность и типичные ошибки
Каждый обработчик в init.php выполняется на своё событие при каждом обращении к сайту, поэтому лишние или задвоенные обработчики напрямую бьют по скорости. На что смотрю в первую очередь на аудите:
- тяжёлые вычисления внутри обработчика без кеша через модуль main - выношу в
CPHPCacheс разумным сроком жизни - дублирующиеся обработчики при мультисайтовости - проверяю SITE_ID перед выполнением логики, если она нужна не на всех сайтах
- отладку через AddMessage2Log вместо echo или var_dump, чтобы не засорять вывод на проде и видеть ошибки в стандартном логе событий Битрикс
- профилирование через встроенный монитор производительности Битрикс, когда init.php разросся и непонятно, какой конкретно обработчик тормозит
Опечатка в приоритете события или забытый обработчик, повешенный дважды при копировании кода между init.php разных сайтов, встречается чаще, чем кажется, и находится именно через профилирование, а не чтением кода глазами.
Чтобы сайт работал без сбоев
Техподдержка
от 15 000 ₽/мес
Подробнее →Частые вопросы
Где физически находится init.php в Битрикс и нужно ли создавать его вручную?
Файл лежит по пути /bitrix/php_interface/init.php и по умолчанию отсутствует в дистрибутиве, его создают вручную при первой необходимости повесить обработчик или объявить константу. Для мультисайтовых проектов дополнительно используются файлы в /bitrix/php_interface/sites/s1/, s2 и так далее, которые подключаются вместе с общим init.php, а не заменяют его.
Можно ли выводить HTML прямо в init.php?
Технически PHP это не запретит, но результат почти всегда непредсказуем, потому что файл подключается до формирования страницы и до работы с заголовками и сессией. Вывод либо появится не в том месте макета, либо вызовет ошибку headers already sent при попытке модуля отправить cookie или редирект позже по ходу выполнения.
Что делать, если init.php разросся до тысяч строк?
Разбиваю его на смысловые блоки: обработчики событий, константы, автозагрузка, а затем переношу устойчивые куски логики в локальный модуль с собственной структурой файлов. В самом init.php в итоге остаётся короткий список подключений и вызовов, а не вся бизнес-логика.
Как обработчики событий в init.php влияют на скорость сайта?
Каждый обработчик добавляет накладные расходы на то событие, к которому он привязан, и при большом количестве обработчиков или тяжёлых операциях внутри них суммарная задержка становится заметной на нагруженных страницах вроде каталога или оформления заказа. Проверяю это через встроенный монитор производительности и добавляю кеширование там, где обработчик делает запрос к базе или к внешнему API.