1С Битрикс · 7 мин чтения

CModule::IncludeModule: подключение модулей Битрикса и типичные ошибки

CModule::IncludeModule - первая строчка почти любого обработчика, компонента, агента или консольного скрипта на 1С-Битрикс, если код выходит за пределы модуля main. Ядро Битрикса модульное: сам движок и часть системных классов поднимаются автоматически через prolog, а iblock, catalog, sale, highloadblock, currency и десятки других модулей нужно подключать явно перед тем, как использовать их классы. По моей практике большая часть фатальных ошибок вида «Class not found» на боевых проектах связана именно с этим методом: его либо забыли вызвать, либо вызвали не в том месте, либо не проверили результат. Разбираю, как метод работает на практике, чем отличается от современного Bitrix\Main\Loader::includeModule и какие ошибки подключения модулей встречаю чаще всего у клиентов.

Что делает CModule::IncludeModule и зачем он нужен

Каждый модуль Битрикса лежит в своей папке /bitrix/modules// и подключается по требованию, а не автоматически при старте страницы. Метод CModule::IncludeModule($moduleId) делает две вещи: проверяет, что модуль с таким ID зарегистрирован в базе (таблица b_module) и физически установлен, и если да, подключает файл include.php этого модуля, где объявлены классы, константы и обработчики. Возвращает метод булево значение: true, если подключение прошло успешно, false, если модуль не найден, не установлен или отключён в панели управления.

Ключевой момент, который часто упускают: сам по себе провал 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 типичных ошибок при подключении модулей

Собрал по своим проектам и по правкам чужого кода пять ошибок, которые встречаю чаще всего.

  1. Не проверять результат вызова. Пишут CModule::IncludeModule(‘iblock’) отдельной строкой и сразу используют CIBlockElement, не оборачивая вызов в if. Если модуль почему-то не установлен (тестовый сервер без части лицензии, восстановленный бэкап), скрипт падает фатальной ошибкой вместо аккуратного сообщения.
  2. Подключать модуль после того, как его классы уже понадобились. Например, класс компонента наследуется от чего-то из sale, а IncludeModule вызывается ниже по коду, уже после объявления этого класса.
  3. Путать программный ID модуля с названием в панели управления. В админке модуль называется «Торговый каталог», а ID для IncludeModule - ‘catalog’; «Информационные блоки» - это ‘iblock’; «Высоконагруженные блоки» - ‘highloadblock’. Написать CModule::IncludeModule(‘catalog_module’) и получить false без внятного объяснения - обычное дело.
  4. Забыть подключить модуль в агенте или консольном скрипте. Ядро main поднимается автоматически, а iblock или sale внутри тела функции-агента нужно подключать так же явно, как в обычном компоненте.
  5. Не закладываться на то, что модуль могут отключить. Через год клиент меняет тариф лицензии и отключает часть модулей, а старый код без проверки 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 указан в карточке модуля на странице установки.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно