Архитектура витрины

Витрина Melbis — это несколько строк в корневом скрипте и дерево модулей, которое разворачивается из одной точки входа. Здесь — общая картина: как устроен путь запроса, из чего состоит проект и как модули складываются в страницу. Подробности каждого звена — в последующих разделах.

Путь запроса

index.php
  │
  ├─ require 'units/melbis.php'     инициализация окружения: чтение config.json
  │                                 в PHP-константы, обработчики ошибок,
  │                                 автозагрузчик классов, функция MELBIS()
  │
  ├─ MELBIS()->DefineSession(...)   сессия посетителя, CSRF-токен,
  │                                 проверка кода включения отладчика
  │
  ├─ MELBIS()->Run($entry_point)    запуск модуля входной точки
  │     │
  │     └─ Parser ─ выполняет PHP-код модуля
  │               ─ рендерит его .htm-шаблоны
  │               ─ раскрывает вложенные вызовы {MELBIS:...}
  │               ─ на каждом уровне проверяет и сохраняет кеш
  │
  ├─ MELBIS()->Publish()            вывод готового HTML
  │
  └─ MELBIS()->Report()             панель отладчика, если она включена

Всё, что происходит между Run и Publish, — работа парсера. Бизнес-логики в корневом скрипте нет: он только решает, какой модуль запустить первым, и отдаёт результат.

Из чего состоит проект

Что Где лежит Раздел
Корневые скрипты корень сайта: index.php, cron.php, .htaccess «Корневые скрипты»
Модульные скрипты units/ — файл имя.php на каждый модуль «Модульные скрипты»
Шаблоны templates/{группа}/units/{модуль}/*.htm «Синтаксис шаблонов»
Настройки config.json в корне сайта «Конфигурация»
Движок core/ — классы платформы и скрипты обмена с программой

Ручное подключение файла в проекте ровно одно — require 'units/melbis.php' в корневом скрипте. Все остальные зависимости платформа подгружает сама: библиотеки разработчик отмечает в параметрах модуля, а вложенные модули парсер находит по тегам в шаблонах.

Вложенность модулей

Каждый модуль формирует фрагмент HTML-кода на основе своих шаблонов. Этот фрагмент может содержать теги вызова других модулей — и так до любой глубины. Парсер обрабатывает всю цепочку автоматически.

Посмотрим на реальный пример из демонстрационного магазина. Модуль-роутер melbis_base_page запускается как входная точка и формирует полную страницу. Его главный шаблон main.htm вызывает три вложенных модуля:

<!doctype html>
<html>
    {MELBIS:melbis_base_head()}

    <body>
        {MELBIS:melbis_base_header()}

        <main>
            {CONTENT}
        </main>

        {MELBIS:melbis_base_footer()}
    </body>
</html>

Модуль melbis_base_header в своём шаблоне, в свою очередь, вызывает модуль каталога:

<nav>
    {MELBIS:melbis_cataloge()}
</nav>

А шаблон melbis_cataloge при переборе пунктов меню вызывает ещё один модуль для подменю, передавая ему [ID] текущего элемента цикла:

{#MENU}
    <li class="nav-item dropdown">
        {MELBIS:melbis_cataloge_sub(,,[ID])}
    </li>
{MENU#}

Параллельно шаблон страницы товаров вызывает модуль списка товаров, тот — карточку каждого товара, а карточка в своём шаблоне вызывает ещё два модуля — изображение и характеристики:

<!-- Шаблон melbis_store_card/main.htm -->
<div class="card">
    <h4>{NAME|html}</h4>
    {MELBIS:melbis_store_image([ID],kDefault)}
    {MELBIS:melbis_store_features([ID])}
    ...
</div>

Таким образом, одна страница магазина может состоять из десятка вложенных модулей — и каждый из них разрабатывается, тестируется и кешируется независимо.

Что считается на каждом запросе

Результат модуля кешируется вместе с его локальными данными — при следующем вызове с теми же параметрами модуль может не выполняться вовсе. Поэтому всё, что зависит от конкретного посетителя, вынесено в отдельный механизм: глобальный массив разворачивается вторым проходом, который не кешируется никогда.

Здесь чаще всего и ошибаются: имя пользователя, содержимое корзины и текущий язык нельзя выводить как обычную переменную модуля — иначе первый же посетитель запечёт своё значение в кеш, и его увидят остальные. Как делать правильно — в разделе «Глобальный массив»; сами уровни кеша — в разделе «Кеширование».