Перейти к содержимому

Архитектура 1С-Битрикс - структура файлов, ядро, жизненный цикл

Прежде чем писать код под 1С-Битрикс, полезно понять, из чего платформа состоит и как проходит запрос. Разберём модульное устройство, файловую структуру, два сосуществующих ядра и жизненный цикл страницы - без этого многие вещи в платформе выглядят необъяснимыми.

Как это работает

Модульный монолит. Система состоит из самостоятельных модулей - контент iblock, каталог catalog, магазин sale, CRM и другие, - но выполняется как единое приложение. Главный модуль main содержит базовые механизмы: загрузку модулей, события, права доступа, кеширование, работу с базой.

Роли распределены по MVC. Модель - это данные и API модулей, и вмешиваться в их код нельзя. Представление - шаблоны сайта, страниц и компонентов, отдающие разметку. Контроллер - это контроллеры D7 и компоненты, принимающие запрос. Дополняют картину расширения JS, агенты, фильтры, события и сервисы.

Ядра два, и они работают одновременно. Старое - это классы с префиксом C и глобальные объекты $APPLICATION, $USER, $DB. Новое ядро, D7, живёт в пространстве имён Bitrix\Main и построено на классах. Новый код пишут на D7, но легаси нужно уметь читать и править: в реальных проектах его много.

Структура сайта хранится в файловой системе, а не в базе. Физическая страница

  • это PHP-файл, который подключает пролог и эпилог, а между ними содержит рабочую область. Динамический контент лежит в инфоблоках, и одна физическая страница с компонентом обслуживает тысячи записей - разделы и детальные страницы виртуальны.

Папка /local/ имеет приоритет над /bitrix/. Это штатный механизм переопределения без правки ядра: одноимённый компонент, шаблон или модуль из /local/ побеждает системный. Именно поэтому весь пользовательский код кладут туда - обновления его не тронут.

Многосайтовость на едином ядре и базе. Несколько сайтов используют общие папки /bitrix/, /upload/ и /local/, а сайт - это запись в таблице с доменами, папкой и языком плюс публичная часть и настройки модулей с привязкой. Идентификатор текущего сайта доступен коду в константе SITE_ID на любом хите. Здесь - только концепция; практическая разводка сайтов по доменам или подпапкам, симлинки на общее ядро и то, что придётся прописывать вручную, разобраны в статье «Мультисайтовость».

Структура каталогов

/ - корень сайта
├── bitrix/ - системные файлы, обновляются системой, править нельзя
│ ├── modules/ - модули платформы
│ ├── components/ - системные компоненты
│ ├── templates/ - системные шаблоны сайтов
│ ├── php_interface/ - init.php, dbconn.php, after_connect.php
│ ├── cache/ managed_cache/ stack_cache/ - файловые кеши
│ ├── .settings.php - настройки ядра D7
│ ├── header.php / footer.php - стандартные пролог и эпилог
│ └── routing_index.php - точка входа роутинга
├── local/ - ВЕСЬ пользовательский код
│ ├── components/ modules/ templates/ activities/ gadgets/
│ ├── php_interface/ - init.php и языковые файлы
│ ├── routes/ - конфигурация роутинга
│ ├── js/ - клиентские расширения
│ └── .settings.php, .settings_extra.php
├── upload/ - загруженные файлы, resize_cache, iblock
└── index.php - главная страница

Жизненный цикл запроса

Понимание порядка шагов объясняет половину «странностей» платформы. Служебная часть пролога выполняется так:

  1. Подключение загрузчика, автозагрузки классов и служебных утилит ядра. 2. Чтение dbconn.php и констант, соединение с базой, затем after_connect.php.
  2. Определение сайта: появляются $APPLICATION и константы SITE_ID, LANGUAGE_ID, SITE_CHARSET, SITE_SERVER_NAME. 4. Подключение init.php - первая точка, где выполняется код самого проекта. 5. Проверка агентов и отправка почтовых событий, если они не переведены на cron. 6. Открытие сессии и событие OnPageStart: до этого шага сессии ещё нет. 7. Авторизация: появляется объект $USER, а с ним и права текущего пользователя. 8. Определение шаблона сайта и событие OnBeforeProlog - точка раннего вмешательства. 9. Проверка прав доступа, старт буферизации вывода и событие OnProlog.

Дальше подключается шапка шаблона, выполняется рабочая область, подключается подвал. В эпилоге срабатывают OnEpilog, завершение буферизации с событием OnEndBufferContent, OnAfterEpilog и завершение приложения.

Из этого порядка следуют два важных факта. Первый: init.php подключается до открытия сессии, поэтому условия по сессии в нём не работают. Второй: именно на буферизации построены отложенные функции - значения задают из тела страницы методами вроде SetTitle() и SetPageProperty(), а выводят в шаблоне методами ShowTitle(), ShowMeta(), ShowProperty(). Реальная подстановка происходит в эпилоге, поэтому «ранние» методы чтения не видят того, что установил компонент ниже по коду.

Страница без шаблона сайта - AJAX-обработчик или контроллер - подключает только служебные части пролога и эпилога, минуя шапку и подвал.

Примеры

1. Правильное место для своего кода

/local/components/mycompany/news.list/ - свой компонент
/local/modules/mycompany.crm/ - свой модуль
/local/templates/main/ - шаблон сайта
/local/templates/main/components/bitrix/news.list/my/ - кастомный шаблон компонента
/local/php_interface/init.php - регистрация обработчиков

Ни один файл в /bitrix/modules/, /bitrix/components/bitrix/ и /bitrix/js/ править нельзя: правки затираются обновлением, а проект теряет право на техподдержку. Поведение ядра меняют событиями, наследованием классов и контейнером зависимостей.

2. Подключение модуля перед использованием

use Bitrix\Main\Loader;
Loader::requireModule('iblock'); // обязательная зависимость
$res = \CIBlockElement::GetList(/* ... */);

Без подключения обращение к классам модуля даёт фатальную ошибку об отсутствии класса. Это, пожалуй, ошибка номер один при переносе кода между файлами.

3. Работа с текущим сайтом

// в публичной части доступна константа
echo SITE_ID;
// в D7-стиле
$siteId = \Bitrix\Main\Context::getCurrent()->getSite();

В административном разделе текущего сайта нет: там идентификатор соответствует языку интерфейса. Поэтому сайтовый файл /local/php_interface/<ID сайта>/init.php в админке не подключается, и общесайтовую логику нужно держать в общем init.php.

Справочник

ЭлементНазначениеОсобенности
/bitrix/системные файлыобновляются платформой, править нельзя
/local/пользовательский кодне перезаписывается, имеет приоритет над /bitrix/
/local/php_interface/init.phpранняя инициализацияподключается на каждом хите, до сессии
/bitrix/.settings.phpнастройки ядра D7сервисы, логгеры, обработка исключений
/local/.settings_extra.phpнастройки, не перезаписываемые из админкис main 24.100.0
dbconn.phpпараметры соединения и константычитается до подключения к базе
SITE_ID, LANGUAGE_ID, SITE_CHARSETконстанты текущего сайтапоявляются после определения сайта
OnPageStart, OnBeforeProlog, OnPrologсобытия прологаточки для раннего вмешательства
OnEpilog, OnEndBufferContent, OnAfterEpilogсобытия эпилогапоследний работает с готовым буфером
Loader::includeModule() / requireModule()подключение модулявторой падает сразу при отсутствии
Application::getInstance()singleton приложенияживёт весь хит
Context::getCurrent()контекст хитазапрос, ответ, сайт, язык

Частые ошибки

Ошибка в init.php кладёт весь сайт. Файл подключается на раннем этапе пролога и на каждом хите - и в публичной части, и в админке. Держите там только регистрацию обработчиков, а логику выносите в модуль.

Условия по сессии в init.php не работают. Сессия открывается позже, а запускать её вручную нельзя.

Скопировали init.php в /local/, и часть логики пропала. При наличии файла в /local/php_interface/ системный файл в /bitrix/php_interface/ перестаёт подключаться. Содержимое нужно переносить целиком, а не копировать частями. При этом общий и сайтовый init.php подключаются оба - сначала общий.

Сайтовый init.php не срабатывает в админке. Там нет текущего сайта. Общесайтовые обработчики - только в общем файле.

Фатальная ошибка об отсутствующем классе. Не подключён модуль перед обращением к его классам.

Правки в /bitrix/ исчезли после обновления. Так и работает: системные файлы перезаписываются, а всё своё держат в /local/.

Частые вопросы

Чем /local/ отличается от /bitrix/ и обязательно ли её создавать?

В /bitrix/ лежат системные файлы, которые платформа перезаписывает при обновлении. Папка /local/ предназначена для всего пользовательского кода: компонентов, модулей, шаблонов, инициализации. При совпадении путей версия из /local/ побеждает, поэтому переопределять системные вещи можно, ничего не ломая. Если папки нет, её просто создают.

Почему в шаблоне нельзя прочитать заголовок страницы?

Из-за порядка выполнения. Шапка шаблона отрабатывает раньше компонентов, а те устанавливают заголовок ниже по коду. Платформа решает это буферизацией: отложенные методы вроде ShowTitle() подставляют значение в самом конце, в эпилоге. Методы чтения такой отсрочки не имеют и вернут то, что известно на момент вызова.

Что такое два ядра и какое использовать?

Старое ядро - это процедурные классы с префиксом C и глобальные объекты, новое (D7) - объектная модель в пространстве Bitrix\Main. Они работают одновременно и дополняют друг друга: часть функциональности до сих пор есть только в старом API. Новый код пишут на D7, а легаси читают и постепенно переводят.

Как несколько сайтов работают на одной установке?

На едином ядре и единой базе: общие папки /bitrix/, /upload/ и /local/, а различаются публичные части, домены, шаблоны и настройки модулей с привязкой к сайту. Текущий сайт доступен в константе SITE_ID. Количество сайтов ограничено редакцией продукта.

Связанные темы

Первоисточники