Skip to content
  1. Компоненты
  2. pdoTools
  3. Парсер

Парсер pdoTools

Парсер pdoTools представляет из себя отдельный класс, который прописывается в системных настройках MODX и перехватывает обработку тегов на странице.

В старых версиях компонента, включение парсера нужно было подтверждать при установке, но с версии 2.1.1-pl он включается по умолчанию. Если, по каким-то причинам, вас это не устраивает - удалите системные настройки

  • parser_class - имя класса парсера
  • parser_class_path - путь к классу парсера

По умолчанию в MODX нет этих настроек, они нужны только для подключения стороннего парсера, как в нашем случае.

Принцип работы

pdoParser может быть использован в двух случаях:

  • при рендере чанка сниппетом - это происходит всегда и во всех сниппетах, использующих pdoTools, независимо от системных настроек.
  • при рендере страницы - только если парсер включен в настройках системы.

Обработка чанка

В классе pdoTools для этого есть 2 метода, очень похожих на таковые в классе modX:

  • getChunk - полная обработка чанка, может задействовать родной парсер MODX
  • parseChunk - только замена плейсхолдеров на значения, modParser не вызывается

Основной особенностью работы этих методов является то, что для загрузки чанка используется protected метод _loadChunk, который может не только загружать чанк из базы данных, но и превращает в него произвольные строки.

Варианты чанков

Итак, оба метода pdoTools поддерживают следующие виды имён чанков:

@INLINE или @CODE

Один из самых популярных вариантов - указание тела чанка прямо на странице. Например:

modx
[[!pdoResources?
  &parents=`0`
  &tpl=`@INLINE <p>[[+id]] - [[+pagetitle]]</p>`
]]

В таком указании есть особенность, о которой многие люди не задумываются - все плейсхолдеры внутри чанка будут обработаны парсером до вызова сниппета.

То есть, если вызвать сниппет на странице вот так:

modx
[[!pdoResources?
  &parents=`0`
  &tpl=`@INLINE <p>[[+id]] - [[+pagetitle]]</p>`
]]

и в памяти системы при этом выставлены плейсхолдеры [[+id]] или [[+pagetitle]], то в сниппет придёт уже обработанный чанк и вы получите на странице одинаковые строки, типа:

15 - тест
15 - тест
15 - тест

Просто одинаковые значения, которые выставил какой-то другой сниппет раньше. Именно поэтому в примере у нас такие необычные плейсхолдеры - {{+}} вместо [[+]]. Системный парсер их не трогает, а pdoTools заменяет их на нормальные во время работы.

Вы можете использовать фигурные скобочки в качестве обрамления плейсхолдеров во всех чанках pdoTools - он сам превратит их в [[+]] при загрузке.

По этой же причине у вас никогда не будут работать вызовы сниппетов и фильтров в INLINE чанках. Вот так работать не будет:

modx
[[!pdoResources?
  &parents=`0`
  &tpl=`@INLINE <p>[[+id]] - [[+pagetitle:default=`название страницы`]]</p>`
]]

А вот так - без проблем

modx
[[!pdoResources?
  &parents=`0`
  &tpl=`@INLINE <p>{{+id}} - {{+pagetitle:default=`название страницы`}}</p>`
]]

Помните об этом нюансе при использовании INLINE чанков.

@FILE

Многие люди обвиняют MODX в том, что он не умеет хранить чанки в файлах и вынуждает лишний раз работать с базой данных. Это и неудобно для системы контроля версий, и медленнее.

С версии 2.2 MODX предлагает использовать для этих целей статичные элементы, но по ряду причин, этот способ всё равно может быть менее удобен, чем прямая работа с файлами.

pdoTools открывает такую возможность при указании @FILE:

modx
[[!pdoResources?
  &parents=`0`
  &tpl=`@FILE resources/mychunk.tpl`
]]

В целях безопасности, использовать можно только файлы с расширеним html и tpl, и только из определённой, заранее заданной директории в настройке pdotools_elements_path.

Вы можете указать свою собственную директорию для файлов через параметр &elementsPath:

modx
[[!pdoResources?
  &parents=`0`
  &tpl=`@FILE resources/mychunk.tpl`
  &elementsPath=`/core/elements`
]]

Файл будет загружен из файла /core/elements/resources/mychunk.tpl от корня сайта.

@TEMPLATE

Этот тип чанка позволяет использовать шаблоны системы (т.е. объекты modTemplate) для оформления вывода.

modx
[[!pdoResources?
  &parents=`0`
  &tpl=`@TEMPLATE Base Template`
]]

Если указан пустой шаблон и в выбранных записях есть поле template с id или именем шаблона, то запись будет обёрнута в этот шаблон:

modx
[[!pdoResources?
  &parents=`0`
  &tpl=`@TEMPLATE`
]]

Это такой аналог сниппета renderResources.

При выводе шаблона можно указывать и набор параметров (как у сниппетов):

modx
[[!pdoResources?
  &parents=`0`
  &tpl=`@TEMPLATE Base Template@MyPropertySet`
]]

Тогда значения из этого набора будут вставлены в шаблон.

Обычные чанки

Это режим по умолчанию, который загружает чанк из базы данных:

modx
[[!pdoResources?
  &parents=`0`
  &tpl=`MyChunk`
]]

Точно так же поддерживаются и наборы параметров:

modx
[[!pdoResources?
  &parents=`0`
  &tpl=`MyChunk@MyPropertySet`
]]

Эти способы загрузки чанков работают во всех родных сниппетах pdoTools и во всех других, которые используют методы pdoTools getChunk и parseChunk.

Метод getChunk

Объявление этого метода выглядит так:

php
getChunk(string $chunkName, array $properties, bool $fastMode = false)

Метод загружает указанный чанк (следуя указанию @BINDING, если есть) и полностью обрабатывает его, заменяя все плейсхолдеры на переданные значения (параметр $properties).

Третий параметр fastMode вырезает все оставшиеся необработанные плейсхолдеры, чтобы не было лишних тегов на странице. Если этого не сделать, то парсер будет пытаться рекурсивно разобрать эти теги (до 10 итераций по умолчанию), что может привести к замедлению работы.

Рекурсивный парсер - это одно из достоинств MODX и специально оставленные теги очень часто встречаются в логике работы сниппетов системы. Поэтому fastMode отключен по умолчанию и использовать его нужно, только если вы уверены в том, что делаете.

Парсер pdoTools не будет вызывать системный парсер, если смог самостоятельно разобрать все плейсхолдеры. Если же в чанке остались какие-то вызовы фильтров или сниппетов, то работа передаётся в modParser, что требует дополнительное время на обработку.

Метод parseChunk

А этот метод объявлен вот так:

php
parseChunk(string $name, array $properties, string $prefix = '[[+', string $suffix = ']]')

Он также создаёт чанк из указанного имени, разбирая @BINDING, если есть, а потом просто заменяет плейсхолдеры на значения, без особых обработок.

Это самый простой и быстрый способ оформления данных в чанки.

Обработка страницы

Если pdoParser включен в настройках, то он вызывается и для обработки всей страницы при выводе её пользователю.

При использовании этого парсера все чанки и дополнения MODX обрабатываются немного быстрее. Всего лишь "немного" потому, что он не берёт на себя условия и фильтры, обрабатывая только простенькие теги, типа [[+id]] и [[~15]]. Однако, он это делает быстрее modParser, потому что не создаёт лишних объектов.

Помимо возможной прибавки скорости, вы получаете еще и новые возможности по удобному выводу данных из разных ресурсов.

Теги fastField

В конце 2012 года общественности был представлен небольшой плагин с добавлением новых тегов парсеру MODX, который затем вырос в компонент fastField.

Он добавляет в систему обработку дополнительных плейсхолдеров, типа [[#15.pagetitle]]. С разрешения автора, этот функционал уже включен в pdoParser, и даже немного расширен.

Все теги fastField начинаются с # и дальше содержат или id нужного ресурса, или название глобального массива.

Вывод обычных полей ресурсов:

modx
[[#15.pagetitle]]
[[#20.content]]

ТВ параметры ресурсов:

modx
[[#15.date]]
[[#20.some_tv]]

Поля товаров miniShop2:

modx
[[#21.price]]
[[#22.article]]

Массивы ресурсов и товаров:

modx
[[#12.properties.somefield]]
[[#15.size.1]]

Суперглобальные массивы:

modx
[[#POST.key]]
[[#SESSION.another_key]]
[[#GET.key3]]
[[#REQUEST.key]]
[[#SERVER.key]]
[[#FILES.key]]
[[#COOKIE.some_key]]

Можно указывать любые поля в массивах:

modx
[[#15.properties.key1.key2]]

Если вы не знаете, какие значения находятся внутри массива - просто укажите его и он будет распечатан полностью:

modx
[[#GET]]
[[#15.colors]]
[[#12.properties]]

Теги fastField можно сочетать с тегами MODX:

modx
[[#[[++site_start]].pagetitle]]

[[#[[++site_start]]]]

Шаблонизатор Fenom

Поддержка шаблонизатора Fenom появилась в pdoTools с версии 2.0, после чего он стал требовать PHP 5.3+.

Он работает гораздо быстрее, чем родной modParser, и если вы перепишите свой чанк так, что в нём не будет ни одного тега MODX, то modParser и вовсе не будет запускаться. При этом, конечно, одновременная работа и старых тегов, и новых в одном чанке допускается.

На обработку шаблонизатором влияют следующие системные настройки:

  • pdotools_fenom_default - включает обработку через Fenom чанков pdoTools. Включено по умолчанию.
  • pdotools_fenom_parser - включает обработку шаблонизатором всех страниц сайта. То есть, не только чанков, но и шаблонов.
  • pdotools_fenom_php - включает поддержку PHP функций в шаблонизаторе. Очень опасная функция, так как любой менеджер получит доступ к PHP прямо из чанка.
  • pdotools_fenom_modx - добавляет системные переменные {$modx} и {$pdoTools} в шаблоны Fenom. Тоже очень опасно - любой менеджер может управлять объектами MODX из чанков.
  • pdotools_fenom_options - JSON строка с массивом настроек согласно официальной документации. Например: {"auto_escape":true,"force_include":true}
  • pdotools_fenom_cache - кэширование скомпилированных шаблонов. Имеет смысл только для сложных чанков на рабочих сайтах, по умолчанию отключено.

Итак, по умолчанию Fenom включен для работы только в чанках, которые проходят через pdoTools. Это вполне безопасно и менеджеры системы не получают никаких дополнительных возможностей, кроме более удобного синтаксиса и высокой скорости работы.

Включение pdotools_fenom_parser позволяет использовать синтаксис Fenom прямо в контенте документов и шаблонах страниц, но есть один нюанс - шаблонизатор может неверно реагировать на фигурные скобочки, которые в MODX очень любят.

Зачастую, во время первого использования феном на рабочем сайте возникает такая ошибка, как выводится не результат запроса на fenom, а текст кода, это в большинстве случаев связано с тем что в коде javascript также присутствуют фигурные скобки, для решения данной проблемы достаточно обернуть все скрипты между тегами {ignore} ваши скрипты {/ignore}- {ignore}.

Если вы планируете включить Fenom глобально для всего сайта, вам нужно проверить, на всех ли страницах он нормально работает.

Синтаксис

Для начала советую прочитать официальную документацию, а дальше мы рассмотрим синтаксис применительно к MODX.

Все переменные от сниппетов передаются в чанк как есть, поэтому переписывать старые чанки на новый синтаксис - сплошное удовольствие.

MODXFenom
[[+id]]{$id}
[[+id:default=`test`]]{$id ?: 'test'}
[[+id:is=``:then=`test`:else=`[[+pagetitle]]`]]{$id == '' ? 'test' : $pagetitle}

Для использования более сложных сущностей, в pdoParser предусмотрена служебная переменная {$_modx}, которая даёт безопасный доступ к некоторым переменным и методам системы.

MODXFenomКраткая запись
[[*pagetitle]]{$_modx->resource.pagetitle}{'pagetitle' | resource}
[[*tv_name]]{$_modx->resource.tv_name}{'tv.tv_name' | resource}
[[$chunk_name]]{$_modx->getChunk('chunk_name')}{'chunk_name' | chunk} или {include 'chunk_name'}
[[!snippet_name]]{$_modx->runSnippet('!snippet_name')}{'!snippet_name' | snippet}
[[++system_setting]]{$_modx->config.system_setting}{'system_setting' | config} или {'system_setting' | option}
[[%lexicon_name]]{$_modx->lexicon('lexicon_name')}{'lexicon_name' | lexicon}
[[~15]]{$_modx->makeUrl(15)}{15 | url}
[[~[[*id]]]]{$_modx->makeUrl($_modx->resource.id)}{('id' | resource) | url}
[[+placeholder_name]]{$_modx->getPlaceholder('placeholder_name')}{'placeholder_name' | placeholder}

Внимание

Не путайте плейсхолдеры, возвращаемые сниппетом, например: {'page.nav' | placeholder} для pdoPage, и переменные, передающиеся сниппетом в чанк, например: {$pagetitle}. Для MODX-парсера же запись будет через [[+...]] в обоих случаях: [[+page.nav]] и [[+pagetitle]].

Помимо этого вам доступны переменные: {$_modx->config} - системные настройки

fenom
{$_modx->config.site_name}
{$_modx->config.emailsender}
{$_modx->config['site_url']}
{$_modx->config['any_system_setting']}

{$_modx->user} - массив текущего пользователя. Если он авторизован, то добавляются и данные из профиля:

fenom
{if $_modx->user.id > 0}
  Привет, {$_modx->user.fullname}!
{else}
  Вам нужно авторизоваться.
{/if}

{$_modx->context} - массив с текущим контекстом

fenom
Вы находитесь в контексте {$_modx->context.key}

{$_modx->resource} - массив с текущим ресурсом, это вы уже видели в примерах выше

fenom
{$_modx->resource.id}
{$_modx->resource.pagetitle}
{$_modx->makeUrl($_modx->resource.id)}

{$_modx->lexicon} - объект (не массив!) modLexicon, который можно использовать для загрузки произвольных словарей:

fenom
{$_modx->lexicon->load('ms2gallery:default')}
Проверка словарей ms2Gallery: {$_modx->lexicon('ms2gallery_err_gallery_exists')}

За вывод записей отвечает отдельная функция {$_modx->lexicon()}.

fastField Теги

Вывод тегов fastField с помощью Fenom, на примере вывода заголовка:

{27 | resource : 'pagetitle'}

Где 27 id нужного ресурса, pagetitle - необходимое поле для вывода.

Системные переменные

Безымянная системная переменная начинается с $. и предоставляет доступ к глобальным системным переменным и системной информации:

  • $.env — массив $_ENV.
  • $.get — массив $_GET.
  • $.post — массив $_POST.
  • $.files — массив $_FILES.
  • $.cookie — массив $_COOKIE.
  • $.server — массив $_SERVER.
  • $.session — массив $_SESSION.
  • $.globals — массив $GLOBALS.
  • $.request — массив $_REQUEST.
  • $.tpl.name возвращает текущее название шаблона.
  • $.tpl.basename возвращает текущее название шаблона без схемы.
  • $.tpl.scm возвращает схему шаблона.
  • $.tpl.options возвращает параметры шаблона в виде целого числа.
  • $.tpl.depends возвращает массив шаблонов, на которые ссылается текущий шаблон.
  • $.tpl.time возвращает штамп времени, когда шаблон последний раз менялся
  • $.version возвращает версию Fenom.
  • $.const обращение к PHP константе: $.const.PHP_EOL обращение к константе PHP_EOL. Поддерживается пространство имен которое разделяется через точку: $.const.Storage.FS::DIR_SEPARATOR обращение к PHP константе Storage\FS::DIR_SEPARATOR если такой константы нет будет взята константа Storage\FS\DIR_SEPARATOR.
  • $.call обращение к статическомому методу. $.call.Storage.FS::put($filename, $data) обращение к методу Storage\FS::put($filename, $data). Настройка disable_call отключает возможность обращения к $.call. Так же можно ограничить и указать список доступных к вызову классов и функций.
  • $.block проверка на существование блоков которые были определены до момента обращения к аксессору. Например, {$.blocks.BLOCK_NAME}. Так же вы можете добавить свои или удалить существующие системные переменные и функции

Операции

Как и любой другой язык программирования/шаблонизации, Fenom поддерживает множество различных операторов:

  • Арифметические операторы — +, -, *, /, %
  • Логические операторы — ||, &&, !$var, and, or, xor
  • Операторы сравнения — >, >=,<, <=, ==, !=, !==, <>
  • Битовые операторы — |, &, , ~$var, >>,<<
  • Операторы присвоения — =, +=, -=, *=, /=, %=, &=, |=, =, >>=,<<=
  • Строковый оператор — $str1 ~ $str2
  • Тернарные операторы — $a ? $b : $c, $a ! $b : $c, $a ?: $c, $a !: $c
  • Проверяющие операторы — $var?, $var!
  • Оператор тестирование — is, is not
  • Оператор содержания — in, not in

Плейсхолдеры с точкой или тире

Во многих местах MODX используются плейсхолдеры, которые нельзя указывать в Fenom, так как они не соответствуют правилам наименования переменных PHP. Например, плейсхолдеры с точкой (MODX обычно выставляет так данные массивов) или с тире (ТВ параметры).

Для доступа к этим плейсхолдерам нужно использовать вторую служебную переменную - {$_pls}:

fenom
<!-- Вывод сразу -->
{$_pls['tag.subtag']}

<!-- Или через переменную -->
{var $tv_name = $_pls['tv-name']}
{$tv_name}

Если плейсхолдер был выставлен в глобальный массив modX::placeholders, то его нужно получать вот так:

fenom
{var $tag_sub_tag = $_modx->getPlaceholder('tag.sub_tag')}
{$tag_sub_tag}

{$_modx->getPlaceholder('tv_name')}

При указании неправильных переменных ваш шаблон не будет скомпилирован и вы получите запись об этом в системном журнале.

Заполнение плейсхолдеров

Fenom работает за один проход, то есть, не является рекурсивным, как родной парсер MODX.

Выполнение всей шаблонизации за один раз даёт очень высокую скорость, но также следует учитывать, что плейсхолдеры будут доступны только после отработки соответствующего сниппета.

Например: необходимо получить значение плейсхолдера {$mse2_query} (Поисковой запрос) в форме, но форму поиска отобразить над результатами. Для этого необходимо выполнить сниппет mSearch2 и передать результаты плейсхолдеру, например searchResults:

fenom
{'!pdoPage' | snippet : [
  'element' => 'mSearch2',
  'toPlaceholder' => 'searchResults'
]}

Далее вызвать сниппет формы поиска, где парсер Fenom подставит значение плейсхолдера {$mse2_query}:

fenom
{'!mSearchForm' | snippet}

После чего вывести результаты работы сниппета mSearch2:

fenom
{'searchResults' | placeholder}

Если сниппет не умеет сохранять результаты своей работы с плейсхолдер, вы можете присваивать их переменной Fenom:

fenom
{var $date = 'dateAgo' | snippet : ['input' => '2016-09-10 12:55:35']}

...

Ваша дата: {$date}.

Очень похоже на логику работы обычного скрипта.

Вывод сниппетов и чанков

Переменная {$_modx} на самом деле представляет собой простой и безопасный класс microMODX

Поэтому сниппеты и чанки вызываются так:

fenom
{$_modx->runSnippet('!pdoPage@PropertySet', [
  'parents' => 0,
  'element' => 'pdoResources',
  'where' => ['isfolder' => 1],
  'showLog' => 1,
])}
{$_modx->getPlaceholder('page.total')}
{$_modx->getPlaceholder('page.nav')}

Как видите, синтаксис практически полностью повторяет PHP, что открывает новые возможности. Например, можно указывать массивы, вместо JSON строк.

По умолчанию все сниппеты вызываются кэшированными, но вы можете добавить ! перед именем - как в тегах MODX.

Если для вызова сниппета используется родной метод MODX, то для вывода чанков запускается pdoTools, и вы можете использовать все его возможности:

fenom
{$_modx->getChunk('MyChunk@PropertySet')}

{$_modx->parseChunk('MyChunk', [
  'pl1' => 'placeholder1',
  'pl2' => 'placeholder2',
])}

{$_modx->getChunk('@TEMPLATE Base Template')}

{$_modx->getChunk('@INLINE
  Имя сайта: {$_modx->config.site_name}
')}

{$_modx->getChunk(
  '@INLINE Передача переменной в чанк: {$var}',
  ['var' => 'Тест']
)}

{$_modx->getChunk('
  @INLINE Передача переменной в вызов сниппета:
  {$_modx->runSnippet("pdoResources", [
    "parents" => $parents
  ])}
  Всего результатов: {$_modx->getPlaceholder("total")}
  ',
  ['parents' => 0]
)}

Примеры выше немного безумные, но вполне себе работают.

Управление кэшированием

В объекте {$_modx} доступен сервис modX::cacheManager, который позволяет вам устанавливать произвольное время кэширование вызываемых сниппетов:

fenom
{if !$snippet = $_modx->cacheManager->get('cache_key')}
  {set $snippet = $_modx->runSnippet('!pdoResources', [
    'parents' => 0,
    'tpl' => '@INLINE {$id} - {$pagetitle}',
    'showLog' => 1,
  ])}
  {set $null = $_modx->cacheManager->set('cache_key', $snippet, 1800)}
{/if}

{$snippet}

Посмотреть этот кэш можно в /core/cache/default/, в примере он сохраняется на 30 минут.

set $null = ... нужен, чтобы cacheManager->set не вывел 1 (т.е. true) на страницу.

А еще вы можете запускать системные процессоры (если прав хватит):

fenom
{$_modx->runProcessor('resource/update', [
  'id' => 10,
  'alias' => 'test',
  'context_key' => 'web',
])}

Проверка авторизации

Так как объекта с пользователем в {$_modx} нет, методы проверки авторизации и прав доступа вынесены непосредственно в класс:

fenom
{$_modx->isAuthenticated()}
{$_modx->hasSessionContext('web')}
{$_modx->hasPermission('load')}

Вывод информации о скорости работы

С версии 2.1.4 вы можете использовать метод {$_modx->getInfo(string, bool, string)} для получения данных о скорости работы системы в данный момент. У него три параметра:

  • string - вывести определённый ключ из массива данных (пусто, по умолчанию)
  • bool - вывести все данные как строку, а не массив (true, по умолчанию)
  • string - оформить строки в чанк, можно использовать любые типа чанков pdoTools (@INLINE {$key}: ${value}).

Вывод по умолчанию (строка со всеми данными):

fenom
{$_modx->getInfo()}
// queries: 39
// totalTime: 0.1067 s
// queryTime: 0.0069 s
// phpTime: 0.0998 s
// source: database

Вывод конкретного значения:

fenom
{$_modx->getInfo('totalTime')}
// 0.1067 s
{$_modx->getInfo('source')}
// database

Оформление строк в свой чанк:

fenom
{$_modx->getInfo('', true, '@INLINE {$key} => {$value}')}
// queries => 39
// totalTime => 0.1155 s
// queryTime => 0.0091 s
// phpTime => 0.1064 s
// source => database

Вы можете даже добавить строки в лексикон pdoTools (или любой другой) и выводить ключи через него:

fenom
{$_modx->lexicon->load('pdotools:default')}
{$_modx->getInfo('', true, '@INLINE {$_modx->lexicon("pdotools_" ~ $key)} => {$value}')}

Или оформить без лексиконов, произвольно. Просто присваиваем переменной массив данных и выводим его ключи:

fenom
{set $info = $_modx->getInfo('', false)}
Время работы: {$info.totalTime}
Время запросов: {$info.totalTime}
Количество запросов: {$info.queries}
Источник: {$info.source}

Вывод информации только пользователям, которые авторизованы в менеджере:

fenom
{if $_modx->hasSessionContext('mgr')}
  {$_modx->getInfo()}
{/if}

Остальные методы

Эти методы должны быть знакомы всем разработчикам MODX, поэтому просто покажу их на примерах:

fenom
{$_modx->regClientCss('/assets/css/style.css')}
{$_modx->regClientScript('/assets/css/script.js')}

{$_modx->sendForward(10)}
{$_modx->sendRedirect('http://yandex.ru')}

{$_modx->setPlaceholder('key', 'value')}
{$_modx->getPlaceholder('key')}

{if $res = $_modx->findResource('url-to/doc/')}
  {$_modx->sendRedirect( $_modx->makeUrl($res) )}
{/if}

Вы даже можете получать и выводить ресурсы без сниппетов:

fenom
{var $resources = $_modx->getResources(
  ['published' => 1, 'deleted' => 0],
  ['sortby' => 'id', 'sortdir' => 'ASC', 'limit' => 50]
)}
{foreach $resources as $resource}
  {$_modx->getChunk('@INLINE <p>{$id} {$pagetitle}</p>', $resource)}
{/foreach}

Переодически в класс добавляются новые методы, так что смотрите их в самом файле

Модификаторы

Fenom умеет использовать сниппеты MODX в качестве модификаторов. Например, установив компонент dateAgo, вы сразу можете использовать его для вывода дат:

modx
[[!pdoResources?
  &parents=`0`
  &tpl=`@INLINE <p>{$id} - {$pagetitle} {$createdon | dateago}</p>`
]]

Также работает и Jevix, и любые другие сниппеты-фильтры MODX, которые принимают параметры $input и $options, согласно документации.

modx
[[!pdoResources?
  &parents=`0`
  &includeContent=`1`
  &tpl=`@INLINE <p>{$id} - {$pagetitle} {$createdon | dateago} {$content | jevix}</p>`
]]

Модификаторы могут работать последовательно:

modx
[[!pdoResources?
  &parents=`0`
  &includeContent=`1`
  &tpl=`@INLINE <p>{$id} - {$pagetitle} {$createdon | date_format:"%Y-%m-%d %H:%M:%S" | dateago}</p>`
]]

Если нужный модификатор не будет найден, то переменная останется как есть, необработанной, и вы получите запись об этом в логе ошибок.

Объединение массивов

fenom
{set $arr1 = [2,15,55]}
{set $arr2 = [44,88,11]}
{set $arr = $arr1 | array_merge : $arr2}
<pre>
  {$arr | print_r}
</pre>

Встроенные модификаторы

На данный момент вы можете использовать следующие модификаторы в Fenom (в скобках указаны равноценные синонимы):

  • date_format - форматирование даты через функцию strftime. Если переменная пуста, будет получено текущее время.

    fenom
    {'2015-01-10 12:45' | date_format : '%d.%m.%Y'} // 10.01.2015
    {'' | date_format : '%Y'} // текущий год
  • date - форматирование даты через функцию date. Если переменная пуста, будет получено текущее время.

    fenom
    {'2015-01-10 12:45' | date : 'd.m.Y'} // 10.01.2015
    {'' | date : 'Y'} // текущий год

    для получения даты +1 день используется формат согласно strftime()

    fenom
    {'+1 days' | date : 'Y-m-d'} // завтра
  • escape (e) - экранирование переменной. Первым параметром принимает режим работы, вторым - кодировку.

    fenom
    {'<p>value</p>' | escape : 'html' : 'utf-8'} // &lt;p&gt;value&lt;/p&gt;
    {'http://site.com/?key=value' | escape : 'url'} // http%3A%2F%2Fsite.com%2F%3Fkey%3Dvalue
    {['key' => 'value'] | escape : 'js'}
  • unescape - декодирование переменной

    fenom
    {'&lt;p&gt;value&lt;/p&gt;' | unescape : 'html' : 'utf-8'} // <p>value</p>
    {'http%3A%2F%2Fsite.com%2F%3Fkey%3Dvalue' | unescape : 'url'} // http://site.com/?key=value
    {'{"key":"value"}' | unescape : 'js'}
  • truncate (ellipsis) - Обрезает переменную до определенной длинны, по умолчанию - 80 символов. В качестве необязательного второго параметра, вы можете передать строку текста, которая будет отображаться в конце обрезанной переменной. Символы этой строки не включаются в общую длину обрезаемой строки. По умолчанию, truncate попытается обрезать строку в промежутке между словами. Если вы хотите обрезать строку строго на указаной длине, передайте в третий необязательный параметр значение true.

    fenom
    {var $str = 'very very long string'}
    {$str | truncate : 10 : ' read more...'} // very very read more...
    {$str | truncate : 5 : ' ... ' : true : true} // very ... string
  • strip - убирает пробелы в начале и конце текста. Если указан дополнительный параметр, то убираются и все повторяющиеся пробелы в тексте.

    fenom
    {'text     with      spaces' | strip : true} // text with spaces
  • length (len, strlen) - выводит длину переменной. Может принимать строку или массив.

    fenom
    {'var' | length} // 3
    {['key' => 'value'] | len} // 1
  • in - оператор проверки наличия подстроки в строке или значения в массиве

    fenom
    {var $key = '10'}
    // строка
    {if $key | in : 'У меня 10 яблок'}
      Ключ найден
    {/if}
    // массив + тернарный оператор
    {$key | in : [1, 3, 42] ? 'ключ найден' : 'не найден'}
  • iterable - проверка возможности перебора переменной

    fenom
    {var $array = ['key' => 'value']}
    {if $array | iterable}
      {foreach $array as $key => $value}
        <p>{$key} is {$value}</p>
      {/foreach}
    {/if}
  • replace - заменяет все вхождения строки поиска на строку замены

    fenom
    {$fruits | replace : "pear" : "orange"} // все pear будут заменены на orange
  • ereplace - выполняет поиск и замену по регулярному выражению.

    fenom
    {'April 15, 2014' | ereplace : '/(\w+) (\d+), (\d+)/i' : '${1}1, $3'} // April1, 2014
  • match - проверят совпадение строки с шаблоном, через функцию fnmatch. Можно использовать простые маски:

    fenom
    {if $color | match : '*gr[ae]y'}
    какой-то оттенок серого
    {/if}
  • ematch - выполняет проверку на соответствие регулярному выражению

    fenom
    {if $color | ematch : '/^(.*?)gr[ae]y$/i'}
    какой-то оттенок серого
    {/if}
  • split - разбивает строку и возвращает массив, используя первый параметр в качестве разделителя (по умолчанию ,).

    fenom
    {var $fruits = 'banana,apple,pear' | split} // ["banana", "apple", "pear"]
    {var $fruits = 'banana,apple,pear' | split : ',apple,'} // ["banana", "pear"]
  • esplit разбивает строку по регулярному выражению в первом параметре (по умолчанию /,\s*/).

    fenom
    {var $fruits = 'banana, apple, pear' | esplit} // ["banana", "apple", "pear"]
    {var $fruits = 'banana; apple; pear' | esplit : '/;\s/'} // ["banana", "apple", "pear"]
  • join - объединяет элементы массива в строку, используя первый параметр в качестве соединителя (по умолчанию ,).

    fenom
    {var $fruits1 = ["banana", "apple", "pear"]}
    {$fruits1 | join} // banana, apple, pear
    {$fruits1 | join : 'is not'} // banana is not apple is not pear
  • number (number_format) - форматирование числа php функцией number_format()

    fenom
    {var $number = 10000}
    {$number | number : 0 : '.' : ' '} // выведет 10 000
  • rand - генерация случайного числа через php функцию rand()

    fenom
    {rand(1, 10)} // выведет случайное число от 1 до 10
  • md5, sha1, crc32 - подсчёт контрольной суммы строки разными алгоритмами

    fenom
    {'text' | md5} // 1cb251ec0d568de6a929b520c4aed8d1
    {'text' | sha1} // 372ea08cab33e71c02c651dbc83a474d32c676ea
    {'text' | crc32} // 999008199
  • urldecode, urlencode, rawurldecode - обработка строки соответствующей PHP функцией.

    fenom
    {'http://site.com/?key=value' | urlencode} // http%3A%2F%2Fsite.com%2F%3Fkey%3Dvalue
    {'http%3A%2F%2Fsite.com%2F%3Fkey%3Dvalue' | urldecode} // http://site.com/?key=value
  • base64_decode, base64_encode - кодирование и декодирование строки алгоритмом base 64

    fenom
    {'text' | base64_encode} // dGV4dA==
    {'dGV4dA==' | base64_decode} // text
  • json_encode (toJSON), json_decode (fromJSON)

    fenom
    {'{"key":"value"}' | fromJSON} // ["key" => "value"]
    {["key" => "value"] | toJSON} // {"key":"value"}
  • http_build_query - генерирация URL-кодированной строки из массива

    fenom
    {['foo'=>'bar','baz'=>'boom'] | http_build_query} // foo=bar&baz=boom
  • print_r - распечатка переменной через PHP функцию. Если передан параметр, то можно сохранить результат.

    fenom
    {var $printed = (['key' => 'value'] | print_r : true)} // Array ( [key] => value )
  • var_dump (dump) - распечатка переменной вместе с типом

  • nl2br - вставляет HTML-код разрыва строки перед каждым переводом строки

  • lower (low, lcase, lowercase, strtolower) - перевод строки в нижний регистр

    fenom
    {'Текст для проверки' | lower} // текст для проверки
  • upper (up, ucase, uppercase, strtoupper) - перевод строки в верхний регистр

    fenom
    {'Текст для проверки' | upper} // ТЕКСТ ДЛЯ ПРОВЕРКИ
  • ucwords - преобразует в верхний регистр первый символ каждого слова в строке, а остальные символы в нижний

    fenom
    {'теКст Для провЕрки' | ucwords} // Текст Для Проверки
  • ucfirst - преобразует в верхний регистр первый символ первого слова в строке, а остальные символы в нижний

    fenom
    {'теКст Для провЕрки' | ucfirst} // Текст для проверки
  • htmlent (htmlentities) - преобразует все возможные символы в соответствующие HTML-сущности

    fenom
    {'<b>Текст для проверки</b>' | htmlent} // &lt;b&gt;Текст для проверки&lt;/b&gt;gt;
  • limit - обрезка строки до указанного количества символов

    fenom
    {'Привет, как дела' | limit : 6} // Привет
  • esc (tag) - экранирование HTML и MODX тегов

    fenom
    {'Привет, [[+username]]' | esc} // тег username не будет обработан
  • notags (striptags, stripTags, strip_tags) - удаление HTML тегов из строки

    fenom
    {'<b>Привет!</b>' | notags} // Привет!
  • stripmodxtags - удаление тегов MODX из строки

    fenom
    {'Привет, [[+username]]' | stripmodxtags} // Привет,
  • cdata - оборачивает вывод тегами CDATA. Обычно это нужно для экранирования значений в XML разметке.

  • reverse (strrev) - переворачивает строку символ за символом

    fenom
    {'mirrortext' | reverse} // txetrorrim
  • wordwrap - вставляет перенос строки после каждого n-ого символа (слова не разбиваются)

    fenom
    {'very very long text' | wordwrap : 3} // very<br />very<br />long<br />text
  • wordwrapcut - вставляет перенос строки после каждого n-ого символа, даже если этот символ будет внутри слова

    fenom
    {'very very long text' | wordwrapcut : 3} // ver<br />y<br />ver<br />y<br />lon<br />g<br />tex<br />t
  • fuzzydate - выводит дату относительно сегодня и вчера, используя словари системы. Если прошло уже больше 2х дней, то форматирует дату через strftime.

    fenom
    {'2016-03-20 13:15' | fuzzydate : '%d.%m.%Y'} // 20.03.2016
    {'' | date : 'Y-m-d 13:15' | fuzzydate} // сегодня в 01:15 PM
  • declension (decl) - склоняет слово, следующее за числом по правилам русского языка. Например: 1 яблоко, 2 яблока, 10 яблок. Вторым параметром указывается выводить ли само число, по умолчанию выводится только подходящий вариант слова. Разделитель вариантов можно задавать третьим параметром, по умолчанию |.

    fenom
    {6 | declension : 'яблоко|яблока|яблок'} // яблок
    {3 | declension : 'яблоко|яблока|яблок' : true} // 3 яблока
    {101 | decl : 'яблоко,яблока,яблок' : false : ','} // яблоко
  • ismember (memberof, mo) - проверка принадлежности пользователя к группе или группам пользователей MODX. Если переменная пуста, то проверка проводится для текущего пользователя.

    fenom
    {1 | ismember : 'Administrator'} // true
    {0 | ismember : ['Administrator', 'Manager']} // false
  • isloggedin, isnotloggedin - проверка авторизации текущего пользователя в контексте. Если контекст не указан, то проверяется текущий.

    fenom
    {'' | isloggedin : 'web'} // true
    {'' | isnotloggedin : 'web'} // false
  • url - генерация ссылки на документ системы через modX::makeUrl.

    fenom
    {10 | url}
    {10 | url : ['scheme' => 'full'] : ['param' => 'value']}
  • lexicon - вывод записи из словарей системы через modX::lexicon. Некоторые словари предварительно нужно загрузить.

    fenom
    {$_modx->lexicon->load('en:core:default')}
    {'action_err_nfs' | lexicon : ['id' => 25] : 'en'} // No action with 25 found.
  • user (userinfo) - вывод поля пользователя системы

    fenom
    {1 | user : 'fullname'} // Administrator
  • resource - вывод поля документа системы. Можно выводить и ТВ параметры

    fenom
    {1 | resource : 'longtitle'} // Home page
    {1 | resource : 'my_tv'} // My_tv value
  • print - функция распечатки и экранирования любых переменных. Очень удобно использовать для отладки, когда нужно узнать, что находится внутри. Единственный параметр указывает, нужно ли обернуть данные в тег <pre> (по умолчанию - да).

    fenom
    {1 | resource | print} // распечатает массив с данными всего ресурса
    {0 | user : 'extended' | print : false} // распечатает массив поля `extended` текущего пользователя в одну строку
  • setOption - выставление значения в массив modX::config. Обязательно нужно указать ключ значения в массиве.

    fenom
    {'Новое имя сайта' | setOption : 'site_name'}
  • option (getOption) - получение значения из массива modX::config

    fenom
    {'site_name' | option} // Новое имя сайта
  • setPlaceholder (toPlaceholder) - выставление значения в массив modX::placeholders. Обязательно нужно указать ключ значения в массиве.

    fenom
    {'My text' | setPlaceholder : 'my_text'}
  • placeholder (fromPlaceholder) - получение значения из массива modX::placeholders.

    fenom
    {'my_text' | placeholder} // My text
  • cssToHead - регистрация CSS кода в шапке страницы

  • htmlToHead - регистрация произвольного HTML в шапке страницы

  • htmlToBottom - регистрация произвольного HTML в подвале страницы

  • jsToHead - регистрация javascript файла в шапке страницы. Если передать параметром true, то можно регистрировать сразу код.

  • jsToBottom - регистрация javascript в подвале страницы. Если передать параметром true, то можно регистрировать сразу код.

    fenom
    {'<script>alert();</script>' | jsToBottom : true} // При загрузке страницы будет javascript alert

Внимание

Для работы функций регистрации на странице должны быть теги head и body

PCRE модификаторы

  • preg_quote - добавляет обратный слэш перед каждым служебным символом. Это бывает полезно, если в составлении шаблона участвуют строковые переменные, значение которых в процессе работы скрипта может меняться в регулярных выражениях служебными считаются следующие символы:

    php
    . \ + * ? [ ^ ] $ ( ) { } = ! < > | : -
  • preg_match - выполняет проверку на соответствие регулярному выражению

    Например, выведем email если соответствует регулярному выражению:

    fenom
    {if 'email@mail.ru'|preg_match : '/^.+@.+\\.\\w+$/' }
    email
    {/if}
  • preg_get - выполняет поиск по регулярному выражению, результат содержит первый набор вхождений

    Например, получим первое изображение из поля content:

    fenom
    {set $image = $_modx->resource.content|preg_get : '!http://.+\.(?:jpe?g|png|gif)!Ui}
  • preg_get_all - выполняет поиск по регулярному выражению, результат содержит набор всех вхождений

  • preg_grep - возвращает массив вхождений, которые соответствуют шаблону

    Например, получим только даты:

    fenom
    {["26-04-1974", "Сергей", "27-11-1977", "Юля"] | preg_grep : "/(\d{2})-(\d{2})-(\d{4})/" | print_r : true}
  • preg_replace - выполняет поиск и замену по регулярному выражению

    Например, выведем название сайта без http:

    fenom
    Website: {'http://site.name'|preg_replace : '~^https?://~': ''}
  • preg_filter - выполняет поиск и замену по регулярному выражению идентична preg_replace за исключением того, что возвращает только те значения, в которых найдено совпадение

  • preg_split - разбивает строку по регулярному выражению

    Например:

    fenom
    {'I love MODX' | preg_split : '/ /' | print_r : true}// Array ( [0] => I [1] => love [2] => MODX )
  • Простая конкатенация массивов (Можно использовать теги HTML)

    Пример:

    fenom
    {set $array = [1,2,3,4]}
    
    {set $output}
      {foreach $array as $item}{$item}{/foreach}
    {/set}
    
    {$output} //Вывод 1234

Собственные модификаторы

Сниппет-фильтр должен принимать 2 параметра: обязательны $input и необязательный $options. Возвращать он может только строки.

В качестве примера давайте создадим сниппет randomize, который будет брать полученную строку и прибавлять к ней случайный набор символов. В параметре мы можем передать количество добавляемых символов:

php
<?php
if (empty($options)) {
  $options = 10;
}
$rand = md5(rand());

return $input . ' ' . substr($rand, 0, $options);

Теперь можно смело использовать новый модификатор как в Fenom

fenom
{'any text' | randomize : 5}

так и в MODX

modx
[[!*pagetitle:randomize=`5`]]

Собственные модификаторы Fenom

Начиная с версии 2.6.0, для добавления модификаторов можно использовать системное событие pdoToolsOnFenomInit:

php
<?php
/** @var modX $modx */
switch ($modx->event->name) {
  case 'pdoToolsOnFenomInit':
    /** @var Fenom $fenom
      Мы получаем переменную $fenom при его первой инициализации, так что можем добавить
      модификатор "website" для вывода имени домена из произвольной ссылки.
    */
    $fenom->addModifier('website', function ($input) {
      if (!$url = parse_url($input)) {
        return $input;
      }
      $output = str_replace('www.', '', $url['host']);

      return strtolower($output);
    });
    break;
}

Теперь модификатор website можно использовать в любых чанках и шаблонах Fenom:

fenom
<a href="{$link}" target="blank">{$link | website}</a>

Такие модификаторы являются внутренними методами Fenom и работают быстрее сниппетов, потому что не нужно делать запрос в БД для их загрузки, а потом кэшировать код в файл для запуска.

Примеры полезных собственных модификаторов Fenom

Время чтения

Модификатор для расчета времени чтения содержимого на странице, может быть удобно для блогов и разделов с текстовыми статьями.

php
$fenom->addModifier('readingtime', static function (string $input, int $threshold = 200) {
  $words = count(preg_split('~[^\p{L}\p{N}\']+~u', strip_tags($input)));
  return ceil($words / $threshold) ?: 0;
});

Использовать можно, например, вот так: {$_modx->resource.content | readingtime : 150 | decl : 'минута|минуты|минут' : true}

Собственные теги Fenom

В fenom можно создавать собственные теги

php
<?php
/** @var modX $modx */
switch ($modx->event->name) {
  case 'pdoToolsOnFenomInit':
    $fenom->addCompiler('exit', function () {
      return "return;";
    });
    break;
}

Теперь спокойно можете перервать работу шаблонизатора используя {exit} Пример:

fenom
тут текст
{exit}
тут текст после exit
вывод "тут текст"

Расширение шаблонов

Использование шаблонизатора Fenom позволяет включать одни чанки (шаблоны в другие) и даже расширять их.

Например, вы можете просто подгрузить содержимое чанка:

fenom
Обычный чанк {include 'имя чанка'}
Шаблон modTemplate {include 'template:имя шаблона'}
Чанк с набором параметров
{include 'chunk@propertySet'}
{include 'template:Name@propertySet'}

Подробнее про {include} читайте в официальной документации.

Гораздо более интересная функция - {extends} шаблонов, она требует включенной системной настройки pdotools_fenom_parser.

Пишем базовый шаблон "Fenom Base":

fenom
<!DOCTYPE html>
<html lang="en">
  <head>
    {include 'head'}
  </head>
  <body>
    {block 'navbar'}
      {include 'navbar'}
    {/block}
    <div class="container">
      <div class="row">
        <div class="col-md-10">
          {block 'content'}
            {$_modx->resource.content}
          {/block}
        </div>
        <div class="col-md-2">
          {block 'sidebar'}
            Sidebar
          {/block}
        </div>
      </div>
      {block 'footer'}
        {include 'footer'}
      {/block}
    </div>
  </body>
</html>

Он включает обычные чанки (в которых, кстати, обычные плейсхолдеры MODX от компонента Theme.Bootstrap) и определяет несколько блоков {block}, которые можно расширить в другом шаблоне.

Теперь пишем "Fenom Extended":

fenom
{extends 'template:Fenom Base'}

{block 'content'}
  <h3>{$_modx->resource.pagetitle}</h3>
  <div class="jumbotron">
    {parent}
  </div>
{/block}

Так вы можете написать один базовый шаблон и расширить его дочерними.

Точно также можно писать и расширять чанки, только обратите внимание, что для работы с modTemplate нужно указывать префикс template:, а для чанков нет — они работают по умолчанию во всех {include} и {extends}.

Внимание

Важно! При наследовании Fenom требует обязательного объявления хотя бы одного блока в шаблоне. Т.е. просто унаследовать базовый шаблон строкой {extends 'template:Fenom Base'} нельзя, будет 502 ошибка на уровне PHP. Достаточно объявить любой блок либо переопределить из базового:

fenom
{extends 'template:Fenom Base'}
{block 'empty'}{/block}

Тестирование производительности

Создаём новый сайт и добавляем в него 1000 ресурсов вот таким консольным скриптом:

php
<?php
define('MODX_API_MODE', true);
require 'index.php';

$modx->getService('error','error.modError');
$modx->setLogLevel(modX::LOG_LEVEL_FATAL);
$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');

for ($i = 1; $i <= 1000; $i++) {
  $modx->runProcessor('resource/create', array(
    'parent' => 1,
    'pagetitle' => 'page_' . rand(),
    'template' => 1,
    'published' => 1,
  ));
}

Затем создаём 2 чанка: modx и fenom со следующим содержимым, соответственно:

modx
<p>[[+id]] - [[+pagetitle]]</p>
fenom
<p>{$id} - {$pagetitle}</p>

И добавляем два консольных скрипта тестирования. Для родного парсера MODX

php
<?php
define('MODX_API_MODE', true);
require 'index.php';

$modx->getService('error','error.modError');
$modx->setLogLevel(modX::LOG_LEVEL_FATAL);
$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');

$res = array();
$c = $modx->newQuery('modResource');
$c->select($modx->getSelectColumns('modResource'));
$c->limit(10);
if ($c->prepare() && $c->stmt->execute()) {
  while ($row = $c->stmt->fetch(PDO::FETCH_ASSOC)) {
    $res .= $modx->getChunk('modx', $row);
  }
}
echo number_format(microtime(true) - $modx->startTime, 4), 's<br>';
echo number_format(memory_get_usage() / 1048576, 4), 'mb<br>';
echo $res;

И для pdoTools:

php
<?php
define('MODX_API_MODE', true);
require 'index.php';

$modx->getService('error','error.modError');
$modx->setLogLevel(modX::LOG_LEVEL_FATAL);
$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');
$pdoTools = $modx->getService('pdoTools');

$res = array();
$c = $modx->newQuery('modResource');
$c->select($modx->getSelectColumns('modResource'));
$c->limit(10);
if ($c->prepare() && $c->stmt->execute()) {
  while ($row = $c->stmt->fetch(PDO::FETCH_ASSOC)) {
    $res .= $pdoTools->getChunk('fenom', $row);
    //$res .= $pdoTools->getChunk('modx', $row);
  }
}
echo number_format(microtime(true) - $modx->startTime, 4), 's<br>';
echo number_format(memory_get_usage() / 1048576, 4), 'mb<br>';
echo $res;

Так как pdoTools понимает оба синтаксиса, для него 2 теста - в режиме тегов MODX, и в режиме Fenom. В скриптах есть указание limit = 10, дальше в таблице я привожу цифры с его возрастанием:

LimitMODXpdoTools (MODX)pdoTools (Fenom)
100.0369s 8.1973mb0.0136s 7.6760mb0.0343s 8.6503mb
1000.0805s 8.1996mb0.0501s 7.6783mb0.0489s 8.6525mb
5000.2498s 8.2101mb0.0852s 7.6888mb0.0573s 8.6630mb
10000.4961s 8.2232mb0.1583s 7.7019mb0.0953s 8.6761mb

А теперь, давайте немного усложним чанки - добавим в них генерацию ссылки для ресурса и вывод menutitle:

modx
<p><a href="[[~[[+id]]]]">[[+id]] - [[+menutitle:default=`[[+pagetitle]]`]]</a></p>
fenom
<p><a href="{$_modx->makeUrl($id)}">{$id} - {$menutitle ?: $pagetitle}</a></p>
LimitMODXpdoTools (MODX)pdoTools (Fenom)
100.0592s 8.2010mb0.0165s 7.8505mb0.0346s 8.6539mb
1000.1936s 8.2058mb0.0793s 7.8553mb0.0483s 8.6588mb
5000.3313s 8.2281mb0.2465s 7.8776mb0.0686s 8.6811mb
10000.6073s 8.2560mb0.4733s 7.9055mb0.1047s 8.7090mb

Как видите, обработка чанков через pdoTools во всех случаях быстрее. При этом заметно, что у чанков Fenom есть некоторый минимум для старта, который обусловлен необходимостью компиляции шаблона.

Отключение

Парсер pdoTools хорошо протестирован и не замечен в каких-либо ошибках, но если вы в чём-то его подозреваете - с версии 2.8.1 можно легко включить родной парсер MODX.

Для этого вам нужно изменить изменить ровно одну системную настройку: parser_class - укажите в неё modParser.

Для включения pdoParser нужно указать его имя обратно в этой настройке.

Проблемы и решения

При работе с pdoParser, при использовании fenom шаблонизатора, разработчики часто встречаются с выводом белого экрана вместо страниц сайта. Сергей Шлоков в своей статье — Почему не работает тег ignore, подробно расписал механику работы pdo парсера и объяснил, что именно вызывает ошибку.

Если front-end вашего сайта лег (белая страница или ошибка 500), а в логах вы встречаете подобные ошибки:

php
(ERROR @ /public_html/core/components/pdotools/model/pdotools/pdotools.class.php : 582) Unexpected token 'm' in expression in file:chunks/metrika.tpl line 3, near '{(m' <- there

это значит, что парсер fenom пытается обработать тег в фигурных скобках и не находит его у себя. Чтобы такого не происходило можно использовать одно из решений, представленных ниже.

Пробелы вокруг фигурных скобок

Необходимо поставить пробелы вокруг фигурных скобок, в таком случае никаких дополнительных манипуляций не требуется.

js
function(){ //code }

Тег

Часто не удобно проставлять пробелы вокруг скобок, так как кода может быть много, он может добавляться постоянно, очень просто пропустить какую-то скобку. В таких случаях удобно использовать тег

fenom
{ignore}
  function(){ //code }
{/ignore}

Но надо помнить, что внутри этого тега fenom не работает. Тег {ignore} должен присутствовать в итерации, после которой больше не будет тегов Fenom. К сожалению, добиться этого не всегда получается. В новой версии pdoTools эта ошибка исправлена, но она всё равно возникает при использовании файловых элементов.

От автора сайта modzone

Если {ignore} не помог, то в статье по ссылке выше, есть два варианта решения.

  1. Указать в шаблоне кэшируемый тег [[*content]] вместо {$_modx->resource.content}.
  2. Перенести блок с тегом {ignore} в ресурс (зачастую такие теги размещают в чанках).

Встроенные модификаторы

Это удобно когда вы подключаете JS-код. Например можно воспользоваться модификатором jsToBottom. @iWatchYouFromAfar приводит в пример следующий код:

fenom
{'
<script type="text/javascript">
  {Скрипт Google аналитики или Яндекс.Метрики}
</script>
' | jsToBottom : true}

Свой модификатор ignore

Можно так же написать свой модификатор ignore с помощью события pdoToolsOnFenomInit. Это намного лучше, чем делать ради этого отдельно сниппет. Этот вариант удобен, когда вы используете {} вне JS кода, например в разметке элемента.

php
<?php
/** @var modX $modx */
switch ($modx->event->name) {
  case 'pdoToolsOnFenomInit':
    $fenom->addModifier('ignore', function ($input) {
      $output = '{ignore}' . $input . '{/ignore}';
      return $output;
    });
    break;
}

Использование:

fenom
<div data-attr='{'{"0":{"items":1,"nav":"false"},"100":{"items":2}}' | ignore}'>

Примеры

Запуск сниппетов в Fenom

1 способ:

fenom
{'!pdoPage' | snippet : [
  'parents' => $_modx->resource.id,
  'limit' => 7,
  'toPlaceholder' => 'result'
]}
{$_modx->getPlaceholder('result')}
{$_modx->getPlaceholder('page.total')}
{$_modx->getPlaceholder('page.nav')}

2 способ:

fenom
{$_modx->runSnippet('!pdoPage', [
  'parents' => $_modx->resource.id,
  'limit' => 7,
  'toPlaceholder' => 'result'
])}


{$_modx->getPlaceholder('result')}
{$_modx->getPlaceholder('page.total')}
{$_modx->getPlaceholder('page.nav')}

Если необходимо вызвать pdopage без параметров:

1 способ:

fenom
{'!pdoPage' | snippet}

2 способ:

fenom
{$_modx->runSnippet('!pdoPage')}

Условие IF

Устанавливаем id текущей страницы в переменную

fenom
{set $id = $_modx->resource.id}

Условие:

fenom
{if $id == '1'}
<p>Главная страница</p>
{else}
<p>Не главная</p>
{/if}>

Условие IF с использованием тренарного оператора

Устанавливаем id текущей страницы в переменную

fenom
{set $id = $_modx->resource.id}

Условие:

fenom
{$id == '1' ? '<p>Главная страница</p>' : '<p>Не главная</p>'}

Вывод чанка

fenom
{include 'myChunk'}

Получение значения TV у произвольного ресурса:

fenom
{7 | resource: 'tv_name'}

7 — id ресурса

Информация о прозводителе в miniShop2

fenom
{$_modx->makeUrl($_pls['vendor.resource'])}
{$_pls['vendor.name']})
{$_modx->getPlaceholder('vendor.name')}

Получение изображения через переменную с точкой ($_pls)

fenom
{$_pls["tv.img"] | phpthumbon : "w=300&h=200&zc=1"}

// Без префикса

{$img | phpthumbon : "w=300&h=200&zc=1"}

Получение превью изображений в корзине

fenom
{if $product['120x90']?}
  <img src="{$product['120x90']}" alt="{$product.pagetitle}" title="{$product.pagetitle}"/>
{else}
  <img src="{'assets_url' | option}components/minishop2//web/ms2_small.png"
  srcset="{'assets_url' | option}components/minishop2/img/web/ms2_small@2x.png 2x"
  alt="{$product.pagetitle}" title="{$product.pagetitle}"/>
{/if}

Вывод текущего года

fenom
{'' | date : 'Y'}

Вывод полного URL адреса текущей страницы вместе с протоколом + canonical

fenom
{$_modx->makeUrl($_modx->resource.id, '', '', 'full')}
<link rel="canonical" href="{$_modx->makeUrl($_modx->resource.id, '', '', 'full')}">

Вывод базового URL

fenom
<base href="{$_modx->config.site_url}">

meta robots

fenom
{if $_modx->resource.searchable}
  <meta name="robots" content="index, follow">
{else}
  <meta name="robots" content="noindex, nofollow">
{/if}

За index/noindex отвечает галка в ресурсе: Доступен для поиска.

title

Выводим расширенный заголовок в title, если он не заполнен то выводим просто заголовок

fenom
<title>{$_modx->resource.longtitle ?: $_modx->resource.pagetitle} | {$_modx->config.site_name}</title>

Выводим имя автора контента

fenom
<meta name="author" content="{$_modx->user.fullname}">

Убираем циклическую ссылку с главной страницы

Многие SEO специалисты, считают к примеру что логотип на главной странице не должен вести на нее же, поэтому можно написать такое условие.

fenom
{if $_modx->resource.id != 1}
  <a class="logo" href="{1 | url}" title="Перейти на главную страницу" >
  <img src="images/logotip.png" alt="{'site_name' | config}, перейти на главную"></a>
{else}
  <div class="logo"><img src="images/logotip.png" alt="{$_modx->config.site_name}"></div>
{/if}

Вывод чанка на определенном ресурсе

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

fenom
{if $_modx->resource.id = 1}{include 'slider'}{/if}

Вывод чанков по ID шаблонов

fenom
{include ('tpl.' ~ $_modx->resource.template)}

Вывод даты

fenom
{$_modx->resource.publishedon | date_format:"%d-%m-%Y %H:%M:%S"}

Вырезаем пробелы из телефона

Допустим у нас стоит ClientConfig и мы создали системную настройку phone со значение 8 800 7000 700 и нам нужно убрать пробелы из значения tel, тогда вызов будет следующим:

fenom
<a href="tel:{$_modx->config.phone  | preg_replace : '/[^0-9+]/' : ''}">  {$_modx->config.phone}</a>

Вывод MIGX через fenom

Вывод migx с помощью fenom для текущего ресурса

fenom
{set $rows = $_modx->resource.elements | fromJSON}
{foreach $rows as $idx => $row}
  {$row.image}
{/foreach}

где elements имя migx тв.

Вывод migx с помощью fenom для произвольного ресурса

fenom
{set $rows = ID | resource : 'elements' | fromJSON}
{foreach $rows as $idx => $row}
  {$row.image}
{/foreach}

где ID id ресурса TV которого нужно вывести, elements имя migx TV.

Ресайз картинок с помощью phpthumbon через fenom

fenom
{$img | phpthumbon : "w=220&h=150&zc=1"}

Заключение

Давайте подытожим возможности парсера pdoTools:

  • Быстрая работа
  • Поддержка тегов fastField
  • Поддержка шаблонизатора Fenom
  • Наследование шаблонов
  • Расширение шаблонов
  • Безопасный доступ к продвинутым функциям MODX

На данный момент pdoTools скачан более 114 000 раз из официального репозитория и более 25 000 из репозитория modstore.pro, что позволяет надеяться на широкое распространение новых технологий шаблонизации в MODX.