Корневые скрипты — это точки входа вашего сайта. Как правило, это
index.php и cron.php. Их задача
минималистична: инициализировать платформу, определить, какой модуль
нужно запустить, и опубликовать результат. Никакой бизнес-логики в
корневых скриптах нет — она вся находится в модулях.
Единственное ручное подключение файла во всём проекте —
units/melbis.phpпервой строкой. Что именно он инициализирует, описано в разделе «melbis.php».
Разберём структуру типового корневого скрипта построчно:
// Melbis start
require 'units/melbis.php';Подключение units/melbis.php инициализирует всё
окружение платформы: загружает config.json, настраивает
обработчики ошибок, регистрирует автозагрузчик классов и создаёт
соединение с базой данных. После этой строки доступна функция
MELBIS(), возвращающая единственный экземпляр парсера.
Подробнее о том, что делает melbis.php — в конце этого
раздела.
// Define session
MELBIS()->DefineSession('MELBIS_SHOP');Инициализация PHP-сессии. Параметр — уникальное имя сессии для этого
проекта. Помимо сессии, этот вызов автоматически активирует CSRF-токен и
проверяет наличие в URL параметра отладчика: если передан
?debug_on_KEY и KEY совпадает с
MELBIS_DEBUG_CODE из конфигурации — включается режим
отладки. Полная сигнатура и работа с сессией — в разделе «Сессии и
CSRF».
// Define self constants
MELBIS()->DefineSelfConst();Загружает пользовательские константы из таблицы
{DBNICK}_self_key_value в пространство PHP-констант. Вызов
необязателен, если в проекте нет пользовательских констант. Подробнее —
в разделе «Конфигурация».
// Entry point
if ( isset($_GET['lazy']) )
{
$entry_point = $_POST['mod'];
$entry_param = $_POST['params'];
}
else
{
$entry_point = $_GET['mod'] ?? 'melbis_base_page';
$entry_param = [serialize($_GET), serialize($_POST)];
}Определение точки входа — имени модуля, который будет запущен первым. Логика проста:
mod. Если параметр не передан, запускается модуль по
умолчанию (как правило, роутер страниц).?lazy (асинхронная загрузка)
имя модуля и параметры берутся из POST-данных. Этот механизм
задействуется системой отложенной загрузки — подробнее в разделе
«Отложенная загрузка».// Entry point check
$entry_exists = MELBIS()->UnitExists($entry_point, true);
if ( !$entry_exists )
{
header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
$entry_point = 'melbis_base_404';
}Имя модуля пришло из адреса, то есть его выбрал не разработчик, а
посетитель — и совпадать с реальным модулем оно не обязано.
UnitExists($mName, $mCheckEntry = false)
проверяет, что модуль с таким именем в проекте есть, и возвращает
true или false.
Второй аргумент добавляет к проверке ещё одно условие: модуль должен
быть разрешён как точка входа — отмечен галочкой «Точка
входа» в IDE либо включён в отложенную загрузку. Правило то же, по
которому парсер решает, запускать ли модуль из корневого скрипта,
поэтому вердикт UnitExists и поведение Run
всегда совпадают.
Без этой проверки Run с несуществующим именем
останавливает разбор ошибкой парсера: посетитель увидит служебную
страницу, а поисковый робот получит её с кодом 200 и
проиндексирует. Проверка стоит одного обращения к файловой системе и
превращает это в нормальный 404.
Имя обеззараживается внутри метода ровно так же, как перед запуском
модуля, — подделать путь через ../ не выйдет.
Та же проверка нужна везде, где имя модуля приходит снаружи: в обработчике cron-запросов и в скрипте отложенной загрузки.
// Run
MELBIS()->Run($entry_point, $entry_param);
// Publish
MELBIS()->Publish();
// Possible report
MELBIS()->Report();Финальная тройка вызовов присутствует в каждом корневом скрипте:
Run() — запускает указанный модуль,
получает от него HTML-результат и рекурсивно обрабатывает все вложенные
вызовы модулей из шаблонов.Publish() — отправляет заголовок
Content-Type (если он ещё не был отправлен) и выводит
готовый HTML-код страницы. Также добавляет заголовок
Server-Timing с данными о времени компиляции — удобно при
профилировании через DevTools. Парный Fetch() возвращает
тот же HTML строкой, ничего не печатая, — он нужен, когда страницу
подменяют из кода модуля (см. «Служебные методы»).Report() — если активирован режим
отладки, выводит после страницы панель отладчика со статистикой по
модулям, SQL-запросам и кешированию. Если отладчик не включён, метод
ничего не делает.<?php
// Melbis start
require 'units/melbis.php';
// Define session
MELBIS()->DefineSession('MELBIS_SHOP');
// Define self constants
MELBIS()->DefineSelfConst();
// Entry point
if ( isset($_GET['lazy']) )
{
$entry_point = $_POST['mod'];
$entry_param = $_POST['params'];
}
else
{
$entry_point = $_GET['mod'] ?? 'melbis_base_page';
$entry_param = [serialize($_GET), serialize($_POST)];
}
// Entry point check
$entry_exists = MELBIS()->UnitExists($entry_point, true);
if ( !$entry_exists )
{
header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
$entry_point = 'melbis_base_404';
}
// Run
MELBIS()->Run($entry_point, $entry_param);
// Publish
MELBIS()->Publish();
// Possible report
MELBIS()->Report();Обычно библиотеки подключаются галочками в параметрах модуля через
IDE — так парсер знает не только сами файлы, но и таблицы, от которых
они зависят. Корневой скрипт модулем не является, и такой галочки у него
нет: если функция библиотеки нужна прямо здесь, до Run, её
подключают вызовом UnitInc:
MELBIS()->UnitInc('melbis_inc_logic');Имя передаётся без расширения, как и всем остальным методам движка. Зависимости самой библиотеки — те, что отмечены галочками в её параметрах, — подтягиваются рекурсивно, повторное подключение того же модуля игнорируется.
Только в корневом скрипте.
UnitIncподключает код, но не регистрирует таблицы библиотеки в системе отслеживания изменений. Для кешируемого модуля это тихая поломка: библиотека читает свои таблицы, кеш модуля о них не знает и при их изменении не сбрасывается — витрина продолжает показывать старые данные, пока кеш не истечёт по времени. В некешируемом модуле вызов отработает, но и там правильный способ — галочка в IDE. Отсюда правило: в модулях — только IDE, в корневом скрипте —UnitInc.
Тем же UnitInc удобно разбирать на части сам корневой
скрипт. Определение языка, выбор точки входа, запись в журнал — каждый
шаг живёт в своём inc-модуле, а index.php остаётся
оглавлением:
// Define lang
MELBIS()->UnitInc('melbis_inc_index_lang');
// Run
MELBIS()->UnitInc('melbis_inc_index_run');
// Log
MELBIS()->UnitInc('melbis_inc_index_log');Такой inc-модуль — обычный модуль-библиотека, созданный в среде разработки и видимый в общем дереве. Отличие только в том, что он не объявляет функции, а выполняет код сразу при подключении.
Переменные из подключённого файла наружу не выходят. Подключение выполняется внутри метода парсера, поэтому код файла работает в его области видимости, а не в области видимости
index.php. Всё, что должно пережить подключение, объявляется в самом файле черезglobal:global $gLang, $gLangPath;Без этого переменная тихо останется пустой — ошибки не будет, поломка обнаружится позже и в другом месте.
Если проект использует периодические задачи — рассылки, очистку
данных, автоматические импорты, — рядом с index.php
появляется второй корневой скрипт, cron.php. Он так же
начинается с require 'units/melbis.php', но вместо
Run объявляет расписание методом CronAdd и
запускает подошедшие задачи вызовом CronRun. Прямой доступ
к нему из браузера закрыт правилом .htaccess.
Формат расписания, устройство cron-модуля, защита от вызова извне, журналы задач и обработка ошибок — в разделе «Планировщик задач».
Файл .htaccess решает несколько задач одновременно.
Минимальный набор правил, который должен присутствовать в каждом
проекте:
AddDefaultCharset UTF-8
DirectoryIndex index.html index.htm index.php
Options -Indexes
# Запрет прямого доступа к конфигурации
RewriteRule ^config.json$ - [F]
# Запрет прямого доступа к cron
RewriteRule ^cron.php$ - [F]
# Запрет прямого доступа к внутренним папкам движка
RewriteRule ^core/(cache|class|init|tmp|trick|log)/ - [F]
# Запрет прямого доступа к журналам в любом месте проекта
RewriteRule \.log$ - [F]
# Маршрут для отложенной загрузки модулей (Lazy Loading)
RewriteRule ^lazy/$ index.php?lazy [L,QSA]Правило ^core/(…)/ закрывает всё, что движок держит для
себя: классы (class), скомпилированный кеш
(cache), данные аналитики (trick), временные
файлы (tmp), SQL-скрипты установки (init) и
журналы (log). Снаружи там не нужно ничего и никогда. Точки
входа core/*.php, через которые с движком общается
программа Melbis Shop, правило не затрагивает — журналы читаются в
дереве скриптов программы, а не по адресу в браузере.
Правило \.log$ добирает всё остальное: дампы
TplDumpVar внутри шаблонов, журналы прежних версий движка,
оставшиеся в корне, и любой лог, который положит рядом с проектом
разработчик.
Правило lazy/ обязательно, если в проекте используется
отложенная загрузка модулей — оно перенаправляет все AJAX-запросы
платформы на index.php с признаком ?lazy.
При необходимости здесь же размещают ЧПУ-маршруты, HTTPS-редиректы и правила кеширования статики.