Здрасте!
В статье рассматривается альтернативный способ загрузки страниц т. н. «Единая точка входа» (иначе FrontController). Его сравнение с текущим способом загрузки ну и конечно реализация данного подхода.
INTRO
Все что описано ниже, это мое личное мнение и виденье, если оно вас не устраивает, если вы считаете меня не компетентным, или недостаточно опытным — это сугубо ваше личное мнение. Если говорить про объективную критику и советы — то милости прошу, это очень даже полезные штуки, тем более что именно для них и написана статья. Ну, а теперь поехали
AFTER_INTRO
Так уж случилось, что по каким-то причинам Битрикс не имеет единую точку входа.
Не то, чтобы это было прям необходимо, но ДА - это прям необходимо!
Почему же единая точка прям таки нужна (прям таки для Битрикс):
Рассмотрим порядок загрузки страницы Битрикс (:
2-3. а зачем подключаться сразу? Если мы хотим вернуть статику (кеш) это действие будет лишним;
7. проверка, то есть запуск? Вообще немного странно, но про агенты будет далее;
8. опять лишнее действие если данные в сессии не хранятся (хотя по факту Битрикс ее юзает в любом случае, но по идее стартовать должен модуль ее использующий, и это не должно быть заложено априори);
16-21. самое непонятное что может только быть. То есть если нам нужно вывести при обработке страницы, что-либо в шаблоне (title, breadcrumbs, установка заголовков, ...) мы прекращаем текущую буферизацию, запоминаем данные, а затем начинаем буферизацию по новой и по сути загрузка страницы может выглядит примерно так:
22. почему отдельно, а не в пункте 23?
Собственно опущены очевидные вещи и все то что касается многосайтовости.
ЗАГРУЗКА СТРАНИЦЫ (by Juggernaut)
Для начала нужно составить порядок загрузки страницы:
Прежде чем я перейду к профитам данного порядка загрузки, представлю псевдокод (концепт) который собственно данный порядок формализует и добавляет некоторые возможности:
Реализация классов Request, Response и др. опущена, потому что не имеет особо значения (на данный момент по крайней мере).
Какие плюсы вытекают из «нового» варианта загрузки:
Вот такой небольшой вариант альтернативной загрузки, ваши вопросы, предложения, пожелания и гневные комменты жду ниже
P.S. небольшой опросик напоследок, который меня дико волнует
В статье рассматривается альтернативный способ загрузки страниц т. н. «Единая точка входа» (иначе FrontController). Его сравнение с текущим способом загрузки ну и конечно реализация данного подхода.
INTRO
Все что описано ниже, это мое личное мнение и виденье, если оно вас не устраивает, если вы считаете меня не компетентным, или недостаточно опытным — это сугубо ваше личное мнение. Если говорить про объективную критику и советы — то милости прошу, это очень даже полезные штуки, тем более что именно для них и написана статья. Ну, а теперь поехали

AFTER_INTRO
Так уж случилось, что по каким-то причинам Битрикс не имеет единую точку входа.
Не то, чтобы это было прям необходимо, но ДА - это прям необходимо!
Почему же единая точка прям таки нужна (прям таки для Битрикс):
- облегчает управление приложение
- делает прозрачным порядок обработки запроса
- избавляет от include вообще (да-да)
- позволяет много чего интересного (об этом ниже)
Рассмотрим порядок загрузки страницы Битрикс (:
- Подключение /bitrix/php_interface/dbconn.php (различные константы)
- Соединение с базой данных
- Подключение /bitrix/php_interface/after_connect.php
- Определение текущего сайта (определяются все классы и функции Главного модуля с учетом выбранного сайта)
- Подключение /bitrix/php_interface/init.php
- Подключение /bitrix/php_interface/SITE_ID/init.php
- Проверка агентов
- Открытие сессии
- Событие OnPageStart
- Определение пользователя, авторизация пользователя, завершение сеанса, регистрация (в зависимости от параметров в запросе)
- Определение текущего шаблона сайта
- Событие OnBeforeProlog
- Проверка прав доступа уровня 1
- Начало буфферизации вывода
- Событие OnProlog
- Подключение /bitrix/templates/ID шаблона сайта/header.php
- Обработка страницы
- Подключение /bitrix/templates/ID шаблона сайта/footer.php
- Вызов функции Cmain::ShowSpreadCookieHTML (данная функция выводит набор невидимых IFRAME'ов используемых в Технология переноса посетителей)
- Событие OnEpilog
- Завершение буферизации страницы
- Отправка E-Mail писем
- Событие OnAfterEpilog
- Завершение соединения с базой данных
2-3. а зачем подключаться сразу? Если мы хотим вернуть статику (кеш) это действие будет лишним;
7. проверка, то есть запуск? Вообще немного странно, но про агенты будет далее;
8. опять лишнее действие если данные в сессии не хранятся (хотя по факту Битрикс ее юзает в любом случае, но по идее стартовать должен модуль ее использующий, и это не должно быть заложено априори);
16-21. самое непонятное что может только быть. То есть если нам нужно вывести при обработке страницы, что-либо в шаблоне (title, breadcrumbs, установка заголовков, ...) мы прекращаем текущую буферизацию, запоминаем данные, а затем начинаем буферизацию по новой и по сути загрузка страницы может выглядит примерно так:
// блок 1 ob_start(); $APPLICATION→AddBufferContent(…); // записываем в переменную то, что вывели в блоке 1 ob_end_clean(); // блок 2 ob_start(); $APPLICATION→AddBufferContent(…); // записываем в переменную то, что вывели в блоке 2 ob_end_clean(); // … // записывает в переменную то, что вывели в блоке 100500 // находится компонент который возвращает JSON пакет (для AJAX запроса) // собственно чистим буфер, т. к. то что вывелось выше нам не нужно $APPLICATION→RestartBuffer(); |
Собственно опущены очевидные вещи и все то что касается многосайтовости.
ЗАГРУЗКА СТРАНИЦЫ (by Juggernaut)
Для начала нужно составить порядок загрузки страницы:
- Событие OnInit (конструктор)
- Событие OnBeforeRequest
- Обработка запроса
- Событие OnAfterRequest
- Отправка ответа (вместе с заголовками, куками и данными)
- OnInit – в данном событии определяются константы, обработчики событий и какой сайт запрашивается (собственно сначала нужно делать это, а потом уже все остальное).
- OnBeforeRequest – сюда можно посадить например выполнение агентов.
- Обработка запроса — определение нужно действия, обработка ошибок и прочие полезности.
- OnAfterRequest – сюда можно посадить например логирование, выполнение агентов (хотя лучше не стоит, ниже подробнее).
- Отправка ответа — собственно вот

Прежде чем я перейду к профитам данного порядка загрузки, представлю псевдокод (концепт) который собственно данный порядок формализует и добавляет некоторые возможности:
class Main
{
const EVENT_INIT = 'mainOnInit';
const EVENT_BEFORE_REQUEST = 'mainOnBeforeRequest';
const EVENT_AFTER_REQUEST = 'mainOnAfterRequest';
const EVENT_ERROR = 'mainOnError';
/**
* Функция обработки ошибки
* @var callable
*/
protected $errorHandler;
/**
* Инициализация предварительных данных
*/
public function __construct() {
$this->initAutoloader();
$this->initListeners();
$this->initErrorHandler();
//
Event::trigger(self::EVENT_INIT);
}
/**
* инициализация автозагрузчика,
* для возможности неявно подключать классы модулей, без ручного подключения,
* для возможности вызова класса компонентов напрямую (не через APPLICATION)
*/
public function initAutoloader() {}
/**
* загружает файлы инициализации всех активных модулей.
* вместо того чтобы вносить какие-либо изменения в файл 'php_interface/init.php'
* нужно в каждом модуле, если это требуется,
* создать файл 'init.php', который будет подгружаться в данном событии.
* в файле 'init.php' можно вешать слушателей на любые события,
* а также производить любые необходимые действия с учетом текущего этапа загрузки
*/
public function initListeners() {}
/**
* Точка входа
*/
public function run() {
try {
$this->onBeforeRequest();
$response = $this->handleRequest();
$this->onAfterRequest();
$response->send();
return $response->status;
}
catch (Exception $error) {
call_user_func($this->errorHandler, $error);
return $error->getCode();
}
}
/**
* событие ПЕРЕД обработкой запроса,
* сюда можно повешать выполнение агентов
*/
public function onBeforeRequest() {
$this->initSite();
$this->initUrlRewrite();
//
Event::trigger(self::EVENT_BEFORE_REQUEST);
}
/**
* инициализируются параметры запрошеного сайта:
* - id
* - локализация (по-умолчанию)
* - шаблон (по-умолчанию)
* - ...
*/
public function initSite() {}
/**
* инициализация URL менеджера
*/
public function initUrlRewrite() {}
/**
* непосредственная обработка запроса,
* определяет запрошеную страницу,
* формирует объект ответа,
* обработка запрошенной страницы,
* подключает запрошенную страницу на основе полученного маршрута,
* инициализирует контроллер страницы которые отвечает:
* - за вывод данных
* - за тип данных
* - за подключение шаблона
* - за атрибуты страницы
* - др.
*/
public function handleRequest() {
list($page, $params) = $this->parseRequest();
$pageController = new PageController($page);
$pageController->params = $params;
$pageController->run();
return $pageController->response;
}
/**
* событие ПОСЛЕ обработки запроса,
* сюда можно повешать различные логгирования
*/
public function onAfterRequest() {
Event::trigger(self::EVENT_AFTER_REQUEST);
}
/**
* вещает обработчика ошибок,
* если не был установлен никакой кастомный
*/
public function initErrorHandler() {
if ($this->errorHandler === null) {
/**
* подключает страницы с соответствующими страницами
*/
$this->errorHandler = function(Exception $error) {
if ($error instanceof NotFoundHttpException) {
include '404.php';
}
elseif ($error instanceof BadRequestHttpException) {
include '400.php';
}
else {
include '500.php';
}
};
}
}
/**
* выполняет разбор запроса, на основе URL менеджера и текущего сайта
*/
public function parseRequest() {}
} |
Реализация классов Request, Response и др. опущена, потому что не имеет особо значения (на данный момент по крайней мере).
Какие плюсы вытекают из «нового» варианта загрузки:
- это проще и прозрачнее
- избавляет от глобальных переменных типа APPLICATION, USER, …
- упрощает подключение файлов и модулей
- все страницы, можно вынести из публичной части
- избавляет от AddBufferContent и ее логики коллбэков
- нет include (вообще)
- …
Вот такой небольшой вариант альтернативной загрузки, ваши вопросы, предложения, пожелания и гневные комменты жду ниже
P.S. небольшой опросик напоследок, который меня дико волнует