2. Руководство разработчика › 2.3 Скрипты › Модульные скрипты

Модульный скрипт — основной строительный блок платформы Melbis. Каждый модуль решает одну изолированную задачу: формирует меню каталога, выводит карточку товара, обрабатывает корзину, выполняет фоновую задачу. Из таких блоков складывается весь сайт.

Модули melbis_* не правят

Модули, идущие с платформой, — с приставкой melbis_ — в своём магазине оставляют как есть. Они должны быть у вас всегда свежими и под рукой: по ним сверяются, из них берут образец, и они же обновляются вместе с движком.

Нужна своя логика — пусть даже одна изменённая строка в расчёте — заводится свой модуль под своей приставкой:

  1. добавьте <компания>_inc_logic.php (или свой обычный модуль — правило то же) со своим пространством имён: namespace <КОМПАНИЯ>_INC_LOGIC;;
  2. скопируйте в него нужные функции из melbis_* и правьте сколько нужно — имена можно оставить, своими их делает строка namespace;
  3. в шаблоне или в вызывающем модуле замените вызов на свой: тег модуля или строку use.

Так работают со всеми модулями поставки: и с демонстрационным магазином, и с готовыми AI-инструментами.

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

Работа с модулями в IDE

Все модули отображаются в дереве файлов в разделе «Скрипты, шаблоны» среды разработки. Модульные скрипты сгруппированы по компании и группе — это делает навигацию удобной даже в больших проектах с десятками модулей.

При открытии модуля в редакторе его интерфейс состоит из трёх частей:

Структура PHP-файла

Типовой модуль — меню каталога melbis_cataloge из демонстрационного магазина:

<?php
namespace MELBIS_CATALOGE;

use MELBIS_INC_PAGE_MENU as PAGE_MENU;

/**
 * Function Main
 **/
function Main($mVars)
{
    // Создать указатель шаблонизатора
    $tpl = MELBIS()->TplCreate();

    // Найти корень каталога
    $command = "SELECT id, tlevel
                  FROM {DBNICK}_topic
                 WHERE skey = 'SHOP'
               ";
    $root = MELBIS()->SqlSelectFlat(__LINE__, $command);

    // Получить разделы меню функцией библиотеки
    $menu = PAGE_MENU\Menu($root['id'], $root['tlevel']);

    // Передать данные в шаблонизатор
    MELBIS()->TplAssign($tpl, 'MENU', $menu);

    // Вернуть результат
    return MELBIS()->TplFinal($tpl, 'main');
}

Модуль начинается с объявления своего пространства имён — это имя файла в верхнем регистре: melbis_cataloge.php → MELBIS_CATALOGE. Строка use под ним даёт подключённой библиотеке короткое имя — об этом ниже, в разделе «Вызов функций библиотеки».

Главная функция называется Main — это единственная обязательная часть модуля. Имя файла она не повторяет: оно уже написано в строке namespace. Парсер вызывает именно её, передавая входные параметры массивом $mVars.

Внутри модуля может быть сколько угодно вспомогательных функций. Их зовут коротким именем, без приставок — Price($id): имя модуля стоит в строке namespace, поэтому Price из карточки товара не столкнётся с Price из корзины. Как называть функции — в разделе «Принятые обозначения».

Обращение к движку пишется как обычно: неквалифицированные имена функций PHP ищет сначала в своём пространстве, а потом в глобальном, поэтому MELBIS(), count() и любая функция плоской библиотеки видны без изменений.

Прежняя форма: модуль без пространства имён

Модуль без строки namespace тоже работает — так написаны модули прошлых лет. Все его функции лежат в общем пространстве PHP, поэтому имена несут приставку: главная функция называется именем файла в верхнем регистре, вспомогательные — тем же именем с Pascal-хвостом:

<?php
function MELBIS_STORE_CARD($mVars)
{
    $price = MELBIS_STORE_CARD_Price($mVars['id']);
    ...
}

function MELBIS_STORE_CARD_Price($mId)
{
    ...
}

Парсер ищет обе формы главной функции — сначала MELBIS_STORE_CARD\Main, затем MELBIS_STORE_CARD, — поэтому тег вызова модуля в шаблоне от формы не зависит. Плоские модули и модули с пространством имён живут в одном магазине и вызывают друг друга. Новый код пишется с пространством имён, а как перевести в него старый модуль — в разделе «Что учесть при переводе модуля».

Вызов функций библиотеки

Функции подключённой библиотеки зовут через строку use под объявлением своего пространства. Так карточка товара melbis_store_card пересчитывает цену в валюту магазина:

namespace MELBIS_STORE_CARD;

use MELBIS_INC_LOGIC_COMMON as LOGIC_COMMON;

...
$store['price_curr'] = LOGIC_COMMON\Price($store['price'], $store['price_curr_id']);

Слово as необязательно. Без него библиотека остаётся под своим полным именем — годится, когда короткое ей не нужно или не объявлено:

use MELBIS_INC_PAGE_MENU;

...
$menu = MELBIS_INC_PAGE_MENU\Menu($id, $level);

Строка use в модуле с пространством обязательна. Без неё имя MELBIS_INC_LOGIC_COMMON\Price() PHP разрешает относительно текущего пространства — ищет MELBIS_STORE_CARD\MELBIS_INC_LOGIC_COMMON\Price — и падает в момент вызова. Обойтись без use можно только ведущим слешем: \MELBIS_INC_LOGIC_COMMON\Price($sum, $curr_id). В плоском модуле, наоборот, use не нужен: там имя со слешем и так глобальное.

Писать строку целиком руками не придётся. Наберите use с пробелом — среда покажет список всех библиотек магазина: полное имя пространства, объявленное библиотекой короткое имя в скобках и её описание. Выбранный пункт вставляется готовой строкой, вместе с as и точкой с запятой, а библиотека при этом отмечается галочкой в панели подключений сама — код первичен, манифест идёт за ним. Уже стоявшую галочку это не трогает, снимать галочки автоматически среда не будет никогда.

Имя в use — псевдоним внутри одного файла, а не глобальное правило: библиотека, подключившая другую библиотеку, объявляет свои use у себя, и вызывающему модулю знать о них не нужно. Наследовать импорты не требуется — загрузка файлов идёт по дереву подключений. Если имя в файле разошлось с тем, что объявила библиотека, при сохранении появится предупреждение Alias differs (см. «Модули-библиотеки»).

Импорт ничего не подключает. use — это только имя; в память библиотека попадает галочкой в манифесте.

Что учесть при переводе модуля

Переезд модуля прежней формы в пространство имён затрагивает не только объявления функций. Три вещи в файле нужно просмотреть глазами:

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

MELBIS_STORE_CARD_Price($id);      // было
MELBIS_STORE_CARD\Price($id);      // стало

Рабочий цикл модуля

Большинство модулей следуют одному и тому же паттерну:

1. Создать указатель шаблонизатора:

$tpl = MELBIS()->TplCreate();

2. Получить данные из базы данных методами MELBIS()->Sql*:

// Вернуть плоский массив одной записи
$topic = MELBIS()->SqlSelectFlat(__LINE__, $command, $params);

// Вернуть массив записей
$menu = MELBIS()->SqlSelect(__LINE__, $command, $params);

// Вернуть массив с индексацией по ключу
$image = MELBIS()->SqlSelectEnumFlat(__LINE__, $command, 'id', $id, $params);

// Вернуть страницу записей и общее число строк
$goods = MELBIS()->SqlSelectLimit(__LINE__, $command, $offset, $limit, $params);

Постраничный вывод, запись данных, транзакции и блокировки таблиц описаны в разделе «Работа с базой данных».

3. Передать данные в шаблонизатор методами MELBIS()->Tpl*:

// Передать одно значение
MELBIS()->TplAssign($tpl, 'TITLE', $topic['name']);

// Передать массив целиком
MELBIS()->TplAssign($tpl, $topic);

// Распарсить шаблон и поместить результат в переменную
MELBIS()->TplParse($tpl, 'CONTENT', 'page_index');

4. Вернуть результат — финальный парсинг главного шаблона:

return MELBIS()->TplFinal($tpl, 'main');

Полный перечень методов Sql* и Tpl* описан в соответствующих разделах документации.

Входные параметры

Параметры поступают в модуль через массив $mVars. Способ формирования массива зависит от того, как был вызван модуль.

Если модуль вызван как входная точка (через MELBIS()->Run()), параметры передаются из корневого скрипта явно:

// В index.php:
$entry_param = [serialize($_GET), serialize($_POST)];
MELBIS()->Run('melbis_base_page', $entry_param);

В модуле объявлено get: serial, post: serial — и внутри функции:

$id = (int) ( $mVars['get']['topic_id'] ?? 0 );

Если модуль вызван из шаблона, параметры передаются прямо в теге вызова — позиционно, через запятую:

{MELBIS:melbis_store_image([ID],kDefault)}
{MELBIS:melbis_store_random(8)}
{MELBIS:melbis_cataloge_sub(,,[ID])}

Значения переменных подставляются в квадратных скобках — [ID], а не {ID}. Аргументы разбираются по запятой и по закрывающей скобке, поэтому любое «грязное» значение — название с запятой, текст с кавычкой, сериализованный массив — при подстановке через {ID} разорвёт список аргументов и модуль получит мусор. Квадратные скобки прогоняют значение через urlencode, а парсер на приёме делает парный urldecode, так что содержимое доезжает целиком и в любом виде. Правило действует и для переменных текущей строки цикла:

{#GOODS}
    {MELBIS:melbis_store_card([VAR:LANG],[ID],[PRIOR])}
{GOODS#}

Литералы (kDefault, 8) пишутся как есть — они не переменные, кодировать нечего.

В модуле должны быть объявлены соответствующие параметры, например id: int, key: str, и внутри функции:

$id  = $mVars['id'];
$key = $mVars['key'];

Типы параметров объявляются в поле над редактором в IDE:

Тип Описание
int Целое число
float Число с плавающей точкой
bool Булево значение
str Строка
fix Фиксированный набор значений; первое — по умолчанию. Пример: mode: fix=list\|grid
serial Сериализованный массив PHP (для передачи $_GET/$_POST). При пустом или некорректном значении вернётся [] — проверять is_array в модуле не нужно

Способы вызова модуля

Из шаблона — основной и безопасный способ. Парсер встречает тег {MELBIS:имя_модуля(...)} в HTML-шаблоне, запускает модуль и подставляет его HTML-результат на место тега. Доступен для любого модуля без ограничений.

Как входная точка — вызов через MELBIS()->Run() в корневом скрипте. URL при этом может быть любым — главный критерий именно вызов через Run. Чтобы разрешить такой вызов, в правой панели IDE необходимо включить опцию «Модуль входной точки». Без этого флага парсер откажется запускать модуль напрямую. Входными точками, как правило, являются модули-роутеры страниц, обработчики форм и cron-задачи.

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

Фрагмент HTML, который вернул модуль, может содержать теги вызова других модулей — и так до любой глубины: парсер обрабатывает всю цепочку сам. Разбор на примере страницы демонстрационного магазина — в разделе «Архитектура витрины».

Библиотеки

В правой панели IDE отображаются все доступные библиотечные модули (inc). Чтобы подключить библиотеку, достаточно поставить галочку — парсер автоматически загрузит её перед запуском текущего модуля, и все её функции станут доступны. Библиотека, которую притянула другая библиотека, показана серой: она уже подключена, отмечать её незачем.

Список таблиц модуля разработчик не ведёт — движок составляет его сам, по запросам, которые модуль выполнил. Вкладка «Таблицы» показывает готовый список, и единственное действие на ней — снять галку с таблицы, от которой кеш зависеть не должен. Как это работает и когда список пополняется — в разделе «Кеширование».

Служебные методы

Помимо работы с шаблонами и базой данных модулю время от времени нужны служебные вызовы платформы — имя текущего модуля, вызов собственной функции, путь к загруженному файлу. Они собраны в разделе «Служебные методы».