CModule::IncludeModule - первая строчка почти любого обработчика, компонента, агента или консольного скрипта на 1С-Битрикс, если код выходит за пределы модуля main. Ядро Битрикса модульное: сам движок и часть системных классов поднимаются автоматически через prolog, а iblock, catalog, sale, highloadblock, currency и десятки других модулей нужно подключать явно перед тем, как использовать их классы. По моей практике большая часть фатальных ошибок вида «Class not found» на боевых проектах связана именно с этим методом: его либо забыли вызвать, либо вызвали не в том месте, либо не проверили результат. Разбираю, как метод работает на практике, чем отличается от современного Bitrix\Main\Loader::includeModule и какие ошибки подключения модулей встречаю чаще всего у клиентов.
Что делает CModule::IncludeModule и зачем он нужен
Каждый модуль Битрикса лежит в своей папке /bitrix/modules/
Ключевой момент, который часто упускают: сам по себе провал IncludeModule не роняет скрипт фатальной ошибкой сразу. Код падает чуть позже, в момент, когда PHP пытается создать объект незадекларированного класса, например CIBlockElement или Bitrix\Sale\Order, и вот тогда на экране полноценный fatal error без внятного объяснения причины.
if (!CModule::IncludeModule('iblock')) {
ShowError('Модуль "Информационные блоки" не установлен');
return;
}
$el = new CIBlockElement();
$el->Add([
'IBLOCK_ID' => 12,
'NAME' => 'Тестовый элемент',
'ACTIVE' => 'Y',
]);
Как правильно подключать модуль Битрикса в коде
Правило простое: вызывать IncludeModule до первого обращения к классу модуля в конкретном PHP-процессе, а не «где-то в начале файла вообще». PHP компилирует файл целиком перед выполнением, поэтому даже если строка с IncludeModule физически стоит выше по файлу, порядок легко перепутать при рефакторинге, если между вызовом и использованием класса появляется условие или подключение другого файла.
В компонентах и шаблонах
В component.php подключаю нужные модули в самом начале файла, до любых обращений к API модуля. В шаблоне компонента (template.php) модуль формально уже должен быть подключён компонентом, но если шаблон дополнительно обращается к API модуля напрямую, добавляю проверку и там же, не полагаясь на то, что кто-то подключил его раньше по цепочке вызовов.
В агентах и консольных скриптах
У агента, зарегистрированного через CAgent::AddAgent, отдельный жизненный цикл: по умолчанию агенты выполняются на хитах посетителей, система при загрузке страницы сама проверяет, есть ли агент, которому пора запускаться, и исполняет его. Отдельной точки входа вида agent.php у агентов нет: чтобы перенести их на cron, выполнение на хите отключают опциями agents_use_crontab и check_agents, а рядом заводят свой скрипт с вызовом CAgent::CheckAgents() и ставят его в cron. В любом из этих случаев ядро main поднимается, а остальные модули нет. Подключаю нужный модуль прямо внутри тела функции-агента, а не полагаюсь на то, что он был подключён где-то при регистрации агента - тот код мог выполниться совсем в другом запросе.
Для консольных скриптов и вебхуков, которые не идут через обычный prolog (например, обработчик, который дёргает СДЭК по расписанию вне админки), сначала подключаю ядро, потом сам модуль:
require_once($_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/main/include/prolog_before.php');
if (!CModule::IncludeModule('highloadblock')) {
die('Модуль highloadblock не установлен');
}
$hlblock = HighloadBlockTable::getById(5)->fetch();
CModule::IncludeModule и Bitrix\Main\Loader::includeModule: в чём разница
В модулях, построенных по D7-архитектуре (это большинство модулей начиная примерно с 14‑й версии ядра), классы подключаются через автозагрузчик по namespace, а не через ручной require в include.php. Для такого стиля кода в Битриксе есть современный аналог, Bitrix\Main\Loader::includeModule(). По сути оба метода делают одно и то же: проверяют регистрацию модуля и подключают его классы, оба возвращают bool и оба безопасно вызывать повторно в одном запросе.
| Способ | Что делает | Когда использую |
|---|---|---|
| CModule::IncludeModule(‘iblock’) | Подключает файлы модуля и объявляет его классы через legacy API ядра | Любой код, который трогает iblock, catalog, sale, highloadblock и другие модули из ядра Битрикса |
| Bitrix\Main\Loader::includeModule(‘iblock’) | Тот же результат через D7-автозагрузчик классов | Новый код, где уже есть use-namespace вверху файла и хочется единообразия |
| Bitrix\Main\ModuleManager::isModuleInstalled(‘iblock’) | Только проверяет регистрацию модуля в b_module, классы не подключает | Когда нужен быстрый флаг «модуль вообще есть», без реальной работы с его классами |
Для встроенных модулей ядра (iblock, catalog, sale, highloadblock, currency) оба варианта работают одинаково надёжно. В новом коде использую Loader::includeModule просто потому, что он лучше сочетается с use-namespace вверху файла и не тянет за собой старый стиль с префиксом C.
Отдельно уточню: подключение одного модуля не подключает автоматически модули, от которых он логически зависит. Catalog работает поверх iblock, но если нужны классы обоих, подключаю iblock и catalog отдельными вызовами IncludeModule, а не рассчитываю, что один вызов сам подтянет второй.
Бесплатный материал
🎁 Полезный скрипт в подарок
Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.
Без спама. Отписка в 1 клик.
5 типичных ошибок при подключении модулей
Собрал по своим проектам и по правкам чужого кода пять ошибок, которые встречаю чаще всего.
- Не проверять результат вызова. Пишут CModule::IncludeModule(‘iblock’) отдельной строкой и сразу используют CIBlockElement, не оборачивая вызов в if. Если модуль почему-то не установлен (тестовый сервер без части лицензии, восстановленный бэкап), скрипт падает фатальной ошибкой вместо аккуратного сообщения.
- Подключать модуль после того, как его классы уже понадобились. Например, класс компонента наследуется от чего-то из sale, а IncludeModule вызывается ниже по коду, уже после объявления этого класса.
- Путать программный ID модуля с названием в панели управления. В админке модуль называется «Торговый каталог», а ID для IncludeModule - ‘catalog’; «Информационные блоки» - это ‘iblock’; «Высоконагруженные блоки» - ‘highloadblock’. Написать CModule::IncludeModule(‘catalog_module’) и получить false без внятного объяснения - обычное дело.
- Забыть подключить модуль в агенте или консольном скрипте. Ядро main поднимается автоматически, а iblock или sale внутри тела функции-агента нужно подключать так же явно, как в обычном компоненте.
- Не закладываться на то, что модуль могут отключить. Через год клиент меняет тариф лицензии и отключает часть модулей, а старый код без проверки IncludeModule продолжает дёргать его классы. Сайт падает не сразу, а через несколько месяцев, когда никто уже не помнит, что там было завязано.
Как проверить, что модуль подключён, и не сломать сайт при его отключении
Если нужно просто узнать, зарегистрирован ли модуль, без реальной загрузки его классов, быстрее вызвать Bitrix\Main\ModuleManager::isModuleInstalled(‘iblock’) - метод смотрит в таблицу b_module и ничего не подключает. Удобно для условной логики в настройках, где важен сам факт наличия модуля, а не его классы.
Отдельная история - модули, которые клиент может отключить сам через панель управления при пересмотре лицензии. Код, который жёстко рассчитывает на iblock или catalog без проверки IncludeModule, продолжит работать до первого запроса к отключённому модулю, а потом упадёт фатально в самом неожиданном месте. Оборачиваю такие блоки в if (Loader::includeModule(‘catalog’)) и в ветке else показываю понятное сообщение вместо белого экрана.
Если достаётся проект без документации и непонятно, какие модули вообще использовались предыдущим разработчиком, проще не гадать по логам ошибок, а сразу заказать доработку и поддержку 1С-Битрикс с ревизией кода, чем чинить одну и ту же фатальную ошибку раз в месяц на новом месте.
Производительность: сколько раз можно вызывать IncludeModule
Ядро Битрикса хранит список уже подключённых модулей внутри запроса, поэтому повторный вызов CModule::IncludeModule для одного и того же модуля в одном хите не подключает файлы заново, а фактически сразу возвращает true. Фатальной ошибки о повторном объявлении класса из-за двойного вызова не будет, это не проблема.
Из практики: не стоит специально прятать вызов IncludeModule внутри цикла foreach на тысячу итераций ради «оптимизации» - выигрыш нулевой, а читать код становится неудобнее. Вызываю метод один раз в начале функции или обработчика, до первого использования классов модуля, и на этом останавливаюсь.
1С-Битрикс регулярно обновляется, поэтому перед внедрением я сверяюсь с актуальной документацией.
Частые вопросы
Чем CModule::IncludeModule отличается от Bitrix\Main\Loader::includeModule?
CModule::IncludeModule - метод старого ядра, который подключает файл include.php модуля и объявляет его классы напрямую. Bitrix\Main\Loader::includeModule делает то же самое, но рассчитан на модули, построенные по D7-архитектуре с автозагрузкой классов по namespace. Функционально для встроенных модулей вроде iblock или sale разницы почти нет, оба возвращают bool и оба нужно проверять условием перед использованием классов модуля.
Что делать, если CModule::IncludeModule возвращает false?
Значит модуль либо не установлен физически (нет папки в /bitrix/modules/), либо не зарегистрирован в базе, либо отключён в панели управления в разделе с модулями. Сначала проверяю ID модуля на опечатку, потом смотрю список установленных модулей в админке, и если модуля там нет - устанавливаю его через маркетплейс или разбираюсь, почему он пропал у клиента (частая причина - откат бэкапа без части модулей).
Нужно ли подключать модуль в каждом файле или один раз на весь запрос?
Ядро само кеширует факт подключения в одном запросе, так что повторный вызов в другом файле того же хита не создаёт накладных расходов. Но экономить на проверках не стоит: лучше вызвать IncludeModule в каждом месте, где реально используешь классы модуля, чем полагаться на то, что его подключили где-то выше по цепочке вызовов.
Как узнать правильный ID модуля для IncludeModule?
Точный технический ID модуля смотрю в списке «Модули» в панели управления Битрикса, там рядом с человекочитаемым названием указан код в скобках, либо прямо в названии папки внутри /bitrix/modules/ - имя папки и есть ID для IncludeModule. Для решений из маркетплейса ID указан в карточке модуля на странице установки.