Псевдо-теги
Псевдо-тег — функция, которая вызывается как тег шаблона и возвращает готовый кусок HTML:
{icon 'edit' class='icon-sm'}Удобно для повторяющихся фрагментов — иконок, кнопок, бэйджей, — которые не хочется заводить отдельным чанком.
Скобок нет, и в выражение тег не встаёт
Это тег, а не функция, и синтаксис у него теговый: параметры через пробел, без скобок и запятых. Всё остальное — ошибка компиляции Function icon not found, то есть белая страница и 500:
{icon 'edit' class='icon-sm'} {* так *}
{icon('edit')} {* 500 *}
{set $i = icon('edit')} {* 500 *}
{if icon('edit')} {* 500 *}Нужно значение в переменной или в условии — заводите модификатор: он работает и фильтром, и функцией, и внутри выражения.
📁 Где объявлять
core/App/Helpers/fenom/inline_tags.phpФайл возвращает массив «имя → функция». Первым аргументом приходит массив параметров: безымянные лежат под числовыми ключами, именованные — под своими.
return [
'icon' => function ($params) {
// {icon 'edit' class='icon-sm'} → ['edit', 'class' => 'icon-sm']
$name = array_shift($params);
$attrs = [];
foreach ($params as $key => $value) {
$attrs[] = htmlspecialchars($key) . '="' . htmlspecialchars($value) . '"';
}
$attrStr = $attrs ? ' ' . implode(' ', $attrs) : '';
$path = MODX_BASE_PATH . "assets/icons/{$name}.svg";
if (!file_exists($path)) {
return "<!-- icon '{$name}' not found -->";
}
$svg = file_get_contents($path);
return preg_replace('/<svg\b(.*?)>/', '<svg$1' . $attrStr . '>', $svg, 1);
},
];{icon 'edit' class='icon-sm text-muted'}
{icon 'user' class='icon-lg' aria-hidden='true'}Как и у модификаторов, внутри доступен $this->modx — файл подключается изнутри View.
🔧 Готовые псевдо-теги
Компонент регистрирует свои — их не нужно объявлять:
| Тег | Что возвращает |
|---|---|
{csrf} | <input type="hidden" name="_token" …> |
{csrf_token} | только значение токена |
{meta_csrf} | <meta name="csrf-token" …> для fetch-запросов |
{spam} | скрытое поле-приманка honeypot |
{recaptcha 'login'} | поле reCAPTCHA v3 с действием и публичным ключом |
{route 'home'} | адрес зарегистрированного маршрута |
{lang 'auth.title'} | строка перевода |
{pb_filters table='Products'} | готовая форма фильтров таблицы |
{heroicon $block.icon style='solid'} | инлайновая Heroicon |
{pbList parent=$id tpl='item' limit=10} | листер ресурсов с AJAX-пагинацией |
route и lang есть и модификаторами — там они умеют больше (подстановки, язык) и работают в выражениях: {$item.uri|lang_url}, {route('order', ['id' => $id])}.
Контракт колбэка
pbFenom сохраняет исторический контракт addFunction(): колбэк получает ($params, $tpl, $var), где $params — разобранный массив параметров тега. Именно так подключаются inline_tags.php.
Если хочется, чтобы API тега задавала сама сигнатура функции, регистрируйте её через addFunctionSmart(). Готового файла-хука под это нет: View::init() кладёт экземпляр движка в modx()->fenom, оттуда его и берут — например из плагина на OnLoadWebDocument или из сниппета:
modx()->fenom->addFunctionSmart('excerpt', function (string $text, int $length = 100) {
return mb_substr(strip_tags($text), 0, $length);
});{excerpt text=$page.introtext length=200}В форке addFunctionSmart() принимает любой callable — замыкание, массив-колбэк, объект с __invoke; в upstream работали только строки. Пропущенный обязательный аргумент сообщается на компиляции («Function excerpt requires the 'text' argument»), а не взрывается посреди рендера.