Архитектура 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 - главная страницаЖизненный цикл запроса
Понимание порядка шагов объясняет половину «странностей» платформы. Служебная часть пролога выполняется так:
- Подключение загрузчика, автозагрузки классов и служебных утилит ядра. 2.
Чтение
dbconn.phpи констант, соединение с базой, затемafter_connect.php. - Определение сайта: появляются
$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. Количество сайтов ограничено редакцией продукта.
Связанные темы
- Ядро D7 - современное API платформы
- Старое ядро - как читать и править легаси
- Шаблоны сайта - пролог, эпилог и рабочая область
- Мультисайтовость - практическая разводка сайтов вместо общей концепции
- Раздел Основы