CBitrixComponent - абстрактный класс из /bitrix/modules/main/classes/general/component.php, от которого наследуются все компоненты в 1С-Битрикс: и системные из битрикс.каталог, и кастомные, которые пишет разработчик под конкретный проект. Когда беру в работу нетиповой компонент - форма заявки с интеграцией в CRM, блок с расчётом доставки через СДЭК или вывод каталога с нестандартной фильтрацией - первое, что проверяю в чужом коде: правильно ли выстроен порядок вызова методов. Ошибка в последовательности initComponent, onPrepareComponentParams и executeComponent на практике даёт три типовых симптома - параметры компонента приходят пустыми, кеш отдаёт чужие данные другому пользователю, шаблон получает не тот arResult. Разберу, что и в каком порядке выполняет движок, и покажу рабочий скелет компонента.
Из чего состоит CBitrixComponent и что от него наследуется
Класс объявлен в модуле main и реализует интерфейс IBitrixComponent. От него наследуется CBitrixComponentTemplate для шаблонов и, что важнее для разработчика, любой class.php компонента вида:
class MyCompanyDeliveryCalcComponent extends CBitrixComponent
{
public function onPrepareComponentParams($arParams) { ... }
public function executeComponent() { ... }
}
Внутри объекта компонента после инициализации доступны служебные свойства: $this->arParams - нормализованные входные параметры, $this->arResult - данные для шаблона, $this->__component_name, $this->__path, $this->__template - метаданные о самом компоненте и подключённом шаблоне. Их не нужно объявлять руками - движок раскладывает их на этапе инициализации, до вызова executeComponent. Именно эта инициализация и задаёт тот самый порядок методов, который часто путают.
Порядок вызова методов: жизненный цикл компонента от запроса до HTML
Когда компонент подключается через $APPLICATION->IncludeComponent() или CBitrixComponent::includeComponentClass(), движок вызывает методы строго в таком порядке:
| Метод | Когда вызывается | Что делает |
|---|---|---|
| __construct() | при создании объекта компонента | переопределяю редко, только если нужна ранняя инициализация зависимостей |
| initComponent() | сразу после создания объекта | устанавливает пути компонента, регистрирует автозагрузку классов из /lib, подключает include.php при наличии |
| onIncludeComponentLang() | после initComponent, до параметров | подключает файл языковых сообщений lang/ru/component.php под текущий LANGUAGE_ID |
| onPrepareComponentParams($arParams) | до executeComponent | нормализует и валидирует arParams, задаёт значения по умолчанию |
| executeComponent() | основной вызов | единственный обязательный к переопределению метод, точка входа в бизнес-логику |
| includeComponentTemplate() | вызывается изнутри executeComponent | подключает template.php выбранного шаблона и component_epilog.php |
Важный нюанс: onPrepareComponentParams вызывается системой отдельно от executeComponent, и если внутри него не вернуть массив $arParams, компонент получит null вместо параметров - частая причина, почему «параметры не доходят до шаблона», хотя на деле проблема на шаг раньше.
onPrepareComponentParams и initComponent: что происходит до executeComponent
initComponent переопределяю редко - там обычно только регистрация автозагрузки для собственных классов из папки /lib компонента:
public function initComponent()
{
BitrixMainLoader::registerAutoLoadClasses('mycompany.delivery', [
'MyCompany\Delivery\Calculator' => 'lib/calculator.php',
]);
}
A onPrepareComponentParams использую почти всегда - здесь провожу нормализацию входных данных, до которых ещё не добрался executeComponent:
public function onPrepareComponentParams($arParams)
{
$arParams['CACHE_TIME'] = isset($arParams['CACHE_TIME']) ? (int)$arParams['CACHE_TIME'] : 3600;
$arParams['DELIVERY_CITY_CODE'] = trim((string)($arParams['DELIVERY_CITY_CODE'] ?? ''));
$arParams['USE_CACHE'] = ($arParams['USE_CACHE'] ?? 'Y') === 'Y';
return $arParams;
}
Если пропустить приведение типов на этом шаге, каждая проверка вроде if ($arParams['CACHE_TIME'] > 0) внутри executeComponent превращается в источник багов - параметр может прийти строкой из .php-настроек компонента на странице, и сравнение отработает не так, как ожидается.
Бесплатный материал
🎁 Полезный скрипт в подарок
Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.
Без спама. Отписка в 1 клик.
executeComponent, кеширование и получение arResult
executeComponent - единственный метод, который обязателен. Внутри него обычно три шага: попытка достать данные из кеша, получение данных, если кеша нет, и подключение шаблона. Стандартная связка выглядит так:
public function executeComponent()
{
if ($this->startResultCache($this->arParams['CACHE_TIME'], $this->arParams['DELIVERY_CITY_CODE'])) {
try {
$this->arResult = $this->getDeliveryData($this->arParams['DELIVERY_CITY_CODE']);
} catch (Exception $e) {
$this->abortResultCache();
$this->arResult = ['ERROR' => $e->getMessage()];
}
$this->includeComponentTemplate();
}
}
На практике здесь и живёт большинство ошибок кеширования. Если внутри блока кеша обращаться к $_SESSION, персональным скидкам или данным авторизованного пользователя, не вызвав abortResultCache(), первый посетитель зафиксирует свой персональный результат в кеше - и следующие пользователи увидят чужие данные. Я это ловил на компоненте расчёта доставки через СДЭК: калькулятор кешировал ответ API по городу, но забыл исключить из ключа кеша тип тарифа, и часть посетителей получала неверную стоимость до истечения TTL. Похожая история с интеграциями T‑Bank и WooCommerce-подобными витринами - если статус оплаты попадает в закешированный arResult, кеш начинает врать про статус заказа. Когда логика компонента завязана на внешний API с нестабильным откликом, разработку такого узла обычно закладываю отдельным этапом, и заказчикам, которым проще делегировать это целиком, предлагаю разработку кастомного компонента под задачу вместо доработки чужого нетипового кода.
includeComponentTemplate и порядок подключения шаблона
Метод includeComponentTemplate() выбирает шаблон по трём источникам - явно переданному имени, значению из SIGNED_PARAMETERS в GET-запросе (для AJAX-компонентов) и, если ничего не задано, шаблону .default. Дальше порядок такой: подключается header.php темы сайта (если компонент не в буфере), затем сам template.php с доступом к $arResult и $arParams в области видимости шаблона, затем component_epilog.php, если он есть в папке шаблона - там обычно добавляют CSS/JS через CJSCore или задают заголовок страницы через SetTitle. При компонентном AJAX-запросе (COMPONENT_TEMPLATE или AJAX_MODE=Y) footer/header сайта не подключаются вовсе, выводится только тело шаблона - это нужно держать в голове, если в template.php завязана логика на глобальные переменные шаблона сайта.
Каскад для составных и AJAX-компонентов
У композитных страниц и компонентов с CBitrixComponent::includeComponentTemplate в режиме AJAX порядок методов не меняется, меняется только то, что часть вывода буферизуется и возвращается через json. executeComponent вызывается так же, просто результат includeComponentTemplate() не пишется напрямую в буфер ответа, а перехватывается ядром для сборки JSON-ответа.
Частые ошибки при переопределении методов CBitrixComponent
За несколько лет работы с чужими и своими компонентами регулярно встречаю один и тот же набор багов:
- Логика выборки данных из БД засунута в onPrepareComponentParams вместо executeComponent - метод вызывается системой раньше, чем ожидает разработчик, и падает на отсутствующих зависимостях модуля
- Прямое обращение к $_REQUEST внутри executeComponent вместо $this->arParams - ломает переиспользование компонента на разных страницах с разными настройками
- Отсутствие приведения типов CACHE_TIME и других числовых параметров - при передаче параметра строкой из визуального редактора кеш либо не работает вовсе, либо висит бесконечно
- Персональные данные пользователя (скидки, статус заказа, регион) попадают в закешированный arResult без abortResultCache() - самая частая причина «чужих данных у другого пользователя»
- Забытый .parameters.php или неверная структура папки .default/template.php - компонент не находит шаблон и падает с пустым выводом без внятной ошибки
Большинство этих ошибок ловятся на этапе код-ревью за пять минут, если знать, в каком порядке движок реально вызывает методы, а не полагаться на то, «как обычно бывает».
Чтобы сайт работал без сбоев
Техподдержка
от 15 000 ₽/мес
Подробнее →Частые вопросы
В каком порядке вызываются методы компонента в 1С-Битрикс?
Сначала __construct(), затем initComponent(), потом onIncludeComponentLang() для подключения языковых файлов, следом onPrepareComponentParams() для нормализации входных параметров, и только после этого executeComponent() - единственный обязательный метод, внутри которого обычно вызывается includeComponentTemplate() для подключения шаблона.
Чем отличается onPrepareComponentParams от initComponent?
initComponent вызывается раньше и обычно используется для регистрации автозагрузки классов компонента и подключения include.php. onPrepareComponentParams вызывается позже, принимает на вход массив $arParams и должен вернуть его же после нормализации - приведения типов, значений по умолчанию, валидации. Бизнес-логику получения данных в оба метода выносить не стоит, для этого есть executeComponent.
Как кеширование влияет на порядок выполнения executeComponent?
StartResultCache() проверяет наличие валидного кеша по переданному ключу и времени жизни. Если кеш есть, метод возвращает false, и код внутри блока if вообще не выполняется - арResult и шаблон достаются из закешированного файла. Если кеша нет или он истёк, метод возвращает true, выполняется получение данных, и в конце вызывается includeComponentTemplate(), результат которого автоматически попадает в кеш при штатном завершении без abortResultCache().
Нужно ли переопределять конструктор CBitrixComponent?
В подавляющем большинстве компонентов - нет, initComponent() полностью закрывает задачи ранней инициализации, включая регистрацию автозагрузки классов. Переопределять __construct() имеет смысл только при внедрении сторонних зависимостей до вызова родительских методов инициализации, и в этом случае обязательно нужно вызвать parent::__construct() первой строкой, иначе сломается вся дальнейшая цепочка initComponent → onPrepareComponentParams → executeComponent.