Блог/Изоляция работы модификаторов

Про парсер MODX Revolution и изоляцию обработки модификаторов и кода внутри от парсера

Автор: Кудашев Сергей

Как многие знают, парсер MODX Revolution при работе с модификаторами, имеет одну неприятную особенность, которая иногда сводит на нет их применение. Проблема в том, что при использовании условных модификаторов типа команда находящаяся в условии будет обработана парсером, вне зависимости от того, истинно оно или ложно. В очередной раз намучавшись с данной проблемой, решил попробовать ее решить. Дисклеймер, будет много букв.

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

<a href="/[[~[[*parent]]]]" data-doc="1">test</a>

Для чистоты эксперимента создаем два новых документа, один родительский в корне, другой его вложенный/дочерний (это условие обязательно). Создаем пустой шаблон и пропишем туда следующий модификатор:

[[*isfolder:isnot=`1`:then=`[[$testChunk]]`]]

, очистим кеш и для начала в браузере обратимся к родительской странице. Как мы можем видеть, в логи MODX Revolution сразу же упало сообщение об ошибке.

[2018-03-04 14:50:18] (ERROR @ modx.class.php : 990) `0` is not a valid integer and may not be passed to makeUrl()
[2018-03-04 14:50:18] (ERROR in resource 15 @ modparser.class.php : 1372) Bad link tag `[[~0]]` encountered

Действительно, содержимое чанка обработалось, хотя контента, который он должен был отдать на странице, мы не увидели. Отработался у нас чанк на родительском документе (он в нашем случае является и папкой), так как именно у него родитель равен 0, а метод makeUrl() не смог этот ноль обработать. И это несмотря на то, что модификатору было указано сработать только в случае, если запрашиваемый документ папкой не будет. Повторим обращение, и если документ у нас помечен как кешируемый, то больше никаких ошибок в логах мы не увидим. Это нам говорит о том, что модификаторы кешируются и условия их выполнения запоминаются. Если обратиться к дочернему документу, то все отработает как мы и ожидали, и мы увидим ссылку с анкором test ведующую на родительский документ.

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

Покопавшись в интернетах можно найти рекомендации использовать хитрые конструкции, при которых код чанка, сниппета или что мы там хотим вызвать, будет формироваться на лету, только при условии из модификатора: https://modx.com/blog/2012/09/14/tags-as-the-result-or-how-conditionals-are-like-mosquitoes/. Давайте проверим этот способ. Нам предлагается поступить следующим образом:

[[[[*field:is=`0`:then=`!SomeScript`:else=`$SomeChunk`]]]]

Сразу обратим внимание, что в данном примере задействованы оба условия. Давайте адаптируем к нашему примеру:

[[[[*isfolder:isnot=`1`:then=`$testChunk`]]]]

Выполнив данный код мы получим в лог сообщение:

[2018-03-04 15:11:34] (ERROR @ modparser.class.php : 540) Could not find snippet with name .

, а ведь действительно, если условие не выполняется, то в парсер передаются пустые скобки, которые воспринимаются как сниппет (так работает парсер, все непонятные тэги он пытается обработать как сниппет core/model/modx/modparser.class.php в районе 544 строки), который система, конечно, не может найти. Но давайте проведем еще один эксперимент, попробовав вынести часть чанка так, чтобы не было вызова несуществующего сниппета. Именно такое решение рекомендуется еще частью разработчиков. Давайте попробуем оба возможных/рекомендуемых варианта:

[[$test[[*isfolder:isnot=`1`:then=`Chunk`]]]]
[[$[[*isfolder:isnot=`1`:then=`testChunk`]]]]

Они оба работают, но есть одно но. Если мы будем смотреть на отладочную информацию выводимую MODX Revolution с количеством обращений к БД, то сразу заметим, что данные конструкции дают дополнительные запросы к БД как в некешируемымом, так и в кешируемом варианте. Это связано с тем, что парсер пытается найти несуществующий чанк и не находит его. А для его поисков он дергает БД, и если с некешированной версией еще можно смириться, то 2 дополнительных запроса к БД на некешируемой версии страницы на ровном месте лично меня не устраивают.

Давайте попробуем как-то решить эту проблему другим путем. Например, не сразу очевидным, но возможным решением, был бы вывод тегов комментариев [[- в случае, когда условие не выполняется. Сразу скажу, что эта замечательная идея при подходе в лоб работать не будет, так как конструкция [[*isfolder:isnot=`1`:then=`[[-`]] не обработается правильно. Парсер MODX устроен таки образом, что не позволяет указывать пары квадратных скобок, которые не закрывают друг друга. То есть обернуть наш код в конструкцию:

[[*isfolder:isnot=`1`:then=`[[-`]]
Код
[[*isfolder:isnot=`1`:then=`]]`]]

у меня не получилось. В общем, подумав немного, приходит в голову следующая идея, опять таки в лоб, это добавление тэгов комментариев к существующим модификаторам, то есть, например:

[[+comstart]]
Код
[[+comstop]]

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

Пробежимся по основным этапам. При обращении к URL MODX вызывает $modx ->handleRequest(), который фактически вызывает $modx->request->handleRequest(), который в свою очередь вызывает prepareResponse метод класса modRequest, который будет готовить наш контент к отправке (в нем же к слову выбрасывается событие ‘OnLoadWebDocument’). Пока нам это ничего не дает, идем дальше. Метод prepareResponse вызывает метод outputContent класса modResponse, и вот уже можно оглядеться. Метод определяет тип документы и если он не бинарный, то отправляет его на парсинг, используя метод processElementTags объекта modParser. Посмотрев работу последнего методы мы видим, что перед тем, как начать парсить документ, выбрасывается событие OnParseDocument. То есть это единственное событие, в котором будет достаточно данных для обработки контента и на которое мы можем повесить какое-то свое действие, которое обработает контент раньше основного парсера. Замечательно, создаем плагин, назвав его PreConditions, который будет обрабатывать отслеживаемые тэги и будет подменять их на какие-то данные до обработки основным парсером.

Давайте продумаем логику работы плагина. Нам необходимо найти в контенте специфические тэги, которые должны быть обработаны раньше основного парсера. Для этого мы будем использовать OnParseDocument событие, чтобы сделать это прямо перед обработкой. При этом мы должны придумать какой-то механизм, чтобы эта обработка не исполнялась каждый раз, чтобы не воздействовать на производительность. Мы планируем использовать данный плагин для подстановки MODX комментариев для исключения блоков контента из обработки по определенному условию, хотя можно придумать и другие способы применения данного решения. Единственный недостаток такого подхода с добавлением комментариев до этапа обработки основным парсером, что для каждого случая придется использовать два модификатора вместо одного. Чуть позже мы замерим, как это скажется на производительности.

Для правильной обработки нам необходимо выбрать токен для тэгов, то есть символ, который будет обрабатываться только нашим плагином и еще не используется MODX (то етсь не +,%,~,$,* и не #, если подключены fastField). Я выбрал символ ‘^’ так как в регулярных выражениях он означает начало строки, что по смыслу нам подходит. Все остальное будет в точности походить на работу с условными модификаторами (Conditional output modifiers). В принципе должны работать и строчные модификаторы, однако строки можно обрабатывать и обычными модификаторами, поэтому смысла их использования на этом этапе нет. Конечный внешний вид использования будет следующий:

[[^isfolder:is=`1`:then=`[[-`]]
Какой-то код
[[^isfolder:is=`1`:then=`]]`]]

Отмечу ряд особенностей работы компонента, во-первых для обработки тэгов плагин используем два класса, это modPreConditions и modPreConditionTag, который наследуется от modTag, во-вторых для обработки тэгов он использует собственный метод collectElementTags, который по своей логике напоминает родной collectElementTags, однако несколько проще и работает на регулярных выражениях. В-третьих, плагин обрабатывает контент не привычной для нас переменной $modx->resource->_output, ее в этот момент еще нет, а переменной $modx->documentOutput, в которой находится нужный нам контент. В-четвертых, плагин устанавливает собственную «глобальную» переменную $modx->preparsed, чтобы не обрабатывать контент каждый раз в рамках одного запроса к сайту. Все, далее я не буду подробно расписывать, как работает плагин и классы, так как они фактически эмулируют работу основного парсера, только по более простой схеме и с меньшим функционалом. Для интересующихся весь код можно посмотреть код на github.

Я предлагаю сразу перейти к тестам на производительность и насколько наш компонент будет сильно на нее влиять. Соберем чистую установку MODX 2.6.1 в которую добавим вышеупомянутые два документа родитель и потомок. Далее добавим в нее новый шаблон:

[[*content]]

[[$testChunk]] <-- то, что будем менять.

[[!timeStop]]

<div style="text-align:center;">sql: [^qt^] ([^q^]), php: [^p^], mem: [^m^], time: [^t^] from [^s^].</div>

, и создадим пустой чанк testChunk, и сниппет timeStop:

<?php
return "\n<br />" . (microtime(true) - $modx->startTime)."s<br />\n" . round(memory_get_peak_usage(true)/1048576,2)."Mb\n";

, как мы знаем, перед вызовом метода handleRequest() в index.php MODX Revolution сохраняет время перед его вызовом в специальной переменной, поэтому мы можем получить точное время выполнения от начала обработки запроса до выполнения нашего сниппета, который замерит время. Ну и возьмем пиковое потребление памяти, просто посмотреть. Тут еще один дисклеймер, что я понимаю и знаю, что замерять время вообще не очень корректно, так как на него влияют множество факторов и оно сильно зависит, как от состояния железа, так от состояния веб-сервера и процесса PHP. Но здесь мы будем его замерять, просто ориентируясь работает быстрее, медленнее, и большая ли разница, т.е. нас интересует примерная картина, а не математическая точность.

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

Для генерации HTML и кода мы будем использовать Emmet, который позволяет генерировать код в нужном количестве с разными подстановками. Например: {<a href="/[[~[[*parent]]]]" data-doc="$">test $</a>${newline}}*1000 сгенерирует 1000 ссылок с порядковыми номерами в тексте ссылки. Это очень упрощает проведение всяких опытов и исследований.

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

Чанк [[$testChunk]] без каких-либо внутренних проверок

Внутри чанка [[$testChunk]] создаем 1000 ссылок с кодом:

<a href="/[[~[[*parent]]]]" data-doc="1">test</a>

, и делаем замер по заранее определенной схеме и снимаем показатели:

Родительский документ не кешированный
Количество обращений к БД7
Время отработки PHP0,93954515457153s
Пиковое потребление памяти4Mb
Родительский документ закешированный
Количество обращений к БД0
Время отработки PHP0,08843207359314s
Пиковое потребление памяти2Mb
Дочерний документ не кешированный
Количество обращений к БД7
Время отработки PHP0,24113202095032s
Пиковое потребление памяти4Mb
Дочерний документ закешированный
Количество обращений к БД0
Время отработки PHP0,075303077697754s
Пиковое потребление памяти2Mb
В логах MODX предупреждение makeUrl() на родительском документе

Чанк [[$testChunk]] с одной внутренней проверкой

Внутри чанка [[$testChunk]] оставляем 1000 ссылок и оборачиваем их в проверку:

[[*isfolder:isnot=`1`:then=`
<a href="/[[~[[*parent]]]]" data-doc="1">test</a>
…
<a href="/[[~[[*parent]]]]" data-doc="1000">test</a>
`]]

, и делаем замер по заранее определенной схеме и снимаем показатели:

Родительский документ не кешированный
Количество обращений к БД7
Время отработки PHP0,88471412658691s
Пиковое потребление памяти4Mb
Родительский документ закешированный
Количество обращений к БД0
Время отработки PHP0,093286037445068s
Пиковое потребление памяти2Mb
Дочерний документ не кешированный
Количество обращений к БД7
Время отработки PHP0,2331531047821s
Пиковое потребление памяти4Mb
Дочерний документ закешированный
Количество обращений к БД0
Время отработки PHP0,088649988174438s
Пиковое потребление памяти4Mb
В логах MODX предупреждение makeUrl() на родительском документе

Чанк [[$testChunk]] с множеством внутренних проверок

Внутри чанка [[$testChunk]] все 1000 ссылок оборачиваем в проверку:

[[*isfolder:isnot=`1`:then=`<a href="/[[~[[*parent]]]]" data-doc="1">test</a>`]]

, и делаем замер по заранее определенной схеме и снимаем показатели:

Родительский документ не кешированный
Количество обращений к БД7
Время отработки PHP1,2959039211273s
Пиковое потребление памяти4Mb
Родительский документ закешированный
Количество обращений к БД0
Время отработки PHP0,092460155487061s
Пиковое потребление памяти2Mb
Дочерний документ не кешированный
Количество обращений к БД7
Время отработки PHP0,52802681922913s
Пиковое потребление памяти6Mb
Дочерний документ закешированный
Количество обращений к БД0
Время отработки PHP0,075139999389648s
Пиковое потребление памяти4Mb
В логах MODX предупреждение makeUrl() на родительском документе

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

Единичная проверка в шаблоне с чанком [[$testChunk]]

В чанке осталяем наши сформированные 1000 ссылок без каких либо проверок. Внутрь шаблона помещаем конструкцию [[*isfolder:isnot=`1`:then=`[[$testChunk]]`]], и делаем замер по заранее определенной схеме и снимаем показатели:

Родительский документ не кешированный
Количество обращений к БД7
Время отработки PHP0,92086791992188s
Пиковое потребление памяти2Mb
Родительский документ закешированный
Количество обращений к БД0
Время отработки PHP0,10577201843262s
Пиковое потребление памяти2Mb
Дочерний документ не кешированный
Количество обращений к БД7
Время отработки PHP0,24568891525269s
Пиковое потребление памяти4Mb
Дочерний документ закешированный
Количество обращений к БД0
Время отработки PHP0,08309006690979s
Пиковое потребление памяти4Mb
В логах MODX предупреждение makeUrl() на родительском документе

Единичная проверка в шаблоне с чанком [[$testChunk]] с выполнением условия

В чанке осталяем наши сформированные 1000 ссылок без каких либо проверок. Внутрь шаблона помещаем конструкцию [[$[[*isfolder:isnot=`1`:then=`testChunk`]]]] , как нам рекомендовали, для того, чтобы условия не обрабатывались и делаем замер по заранее определенной схеме и снимаем показатели:

Родительский документ не кешированный
Количество обращений к БД11
Время отработки PHP0,13047409057617s
Пиковое потребление памяти4Mb
Родительский документ закешированный
Количество обращений к БД3
Время отработки PHP0,078697204589844s
Пиковое потребление памяти2Mb
Дочерний документ не кешированный
Количество обращений к БД7
Время отработки PHP0,1879620552063s
Пиковое потребление памяти4Mb
Дочерний документ закешированный
Количество обращений к БД0
Время отработки PHP0,097842931747437s
Пиковое потребление памяти2Mb
В логах MODX какие-либо предупреждения отсутствуют

Множественная проверка кода прямо в шаблоне

Внутрь шаблона помещаем 1000 вызовов конструкции [[*isfolder:isnot=`1`:then=`<a href="/[[~[[*parent]]]]">test</a>`]] и делаем замер по заранее определенной схеме и снимаем показатели:

Родительский документ не кешированный
Количество обращений к БД6
Время отработки PHP1,2022387981415s
Пиковое потребление памяти2Mb
Родительский документ закешированный
Количество обращений к БД0
Время отработки PHP0,073862075805664s
Пиковое потребление памяти2Mb
Дочерний документ не кешированный
Количество обращений к БД6
Время отработки PHP0,62936997413635s
Пиковое потребление памяти4Mb
Дочерний документ закешированный
Количество обращений к БД0
Время отработки PHP0,088212966918945s
Пиковое потребление памяти2Mb
В логах MODX предупреждение makeUrl() на родительском документе

Собрав все эти данные мы можем перейти к замерам работы нашего компонента, который так же будет состоять из трех блоков. Устанавливаем компонент из ветки master, в данной реализации используется плагин, который срабатывает на событие onParseDocument. Еще раз напомню, что компонент может быть применен исключительно к контенту размещенному в шаблонах.

Единичная проверка в шаблоне с выполнением условия

Перетаскиеваем сформированные 1000 ссылок в шаблон и оборачиваем в проверку:

[[^isfolder:is=`1`:then=`[[-`]]
<a href="/[[~[[*parent]]]]" data-doc="1">test</a>
…
<a href="/[[~[[*parent]]]]" data-doc="1000">test</a>
[[^isfolder:is=`1`:then=`]]`]]

, и делаем замер по заранее определенной схеме и снимаем показатели:

Родительский документ не кешированный
Количество обращений к БД6
Время отработки PHP0,12065505981445s
Пиковое потребление памяти2Mb
Родительский документ закешированный
Количество обращений к БД0
Время отработки PHP0,074366092681885s
Пиковое потребление памяти2Mb
Дочерний документ не кешированный
Количество обращений к БД6
Время отработки PHP3,3769309520721s
Пиковое потребление памяти4Mb
Дочерний документ закешированный
Количество обращений к БД0
Время отработки PHP0,1069130897522s
Пиковое потребление памяти4Mb
В логах MODX какие-либо предупреждения отсутствуют

Множественные проверки в шаблоне с выполнением условия

Вместо 1000 сформированных ссылок в шаблон помещаем 1000 конструкций, в которой ссылка проверяется по условию:

[[^isfolder:is=`1`:then=`[[-`]]
<a href="/[[~[[*parent]]]]" data-doc="1">test</a>
[[^isfolder:is=`1`:then=`]]`]]

, и делаем замер по заранее определенной схеме и снимаем показатели:

Родительский документ не кешированный
Количество обращений к БД6
Время отработки PHP0,68638801574707s
Пиковое потребление памяти6Mb
Родительский документ закешированный
Количество обращений к БД0
Время отработки PHP0,080582857131958s
Пиковое потребление памяти4Mb
Дочерний документ не кешированный
Количество обращений к БД6
Время отработки PHP3,8289330005646s
Пиковое потребление памяти4Mb
Дочерний документ закешированный
Количество обращений к БД0
Время отработки PHP0,084268093109131s
Пиковое потребление памяти2Mb
В логах MODX какие-либо предупреждения отсутствуют

Единичная проверка в шаблоне с чанком [[$testChunk]] с выполнением условия

Вместо 1000 конструкций возвращаемся к использованию нашего чанка, в котором у нас присутсвует 1000 сформированных ссылок. Используем в шаблоне один раз такую конструкцию:

[[^isfolder:is=`1`:then=`[[-`]]
[[$testChunk]]
[[^isfolder:is=`1`:then=`]]`]]

, и делаем замер по заранее определенной схеме и снимаем показатели:

Родительский документ не кешированный
Количество обращений к БД6
Время отработки PHP0,13211798667908s
Пиковое потребление памяти4Mb
Родительский документ закешированный
Количество обращений к БД0
Время отработки PHP0,078866004943848s
Пиковое потребление памяти4Mb
Дочерний документ не кешированный
Количество обращений к БД7
Время отработки PHP3,3375608921051s
Пиковое потребление памяти4Mb
Дочерний документ закешированный
Количество обращений к БД0
Время отработки PHP0,08203387260437s
Пиковое потребление памяти4Mb
В логах MODX какие-либо предупреждения отсутствуют

Желающие могут сравнить все данные в целом, я лишь обращу внимание, на несколько интересных моментов. Обратите внимание, что в первой таблице при использовании чанков запросов к БД было одинаковое количество, как у родителя, так и у потом. В нашей последней таблице это не так, так как на родительском документе часть кода, которая не подходит по условию, реально не обрабатывается, а следовательно не дергается чанк и запросов у родителя на один меньше. Что хочется еще отметить, что при единичном использовании разницы во времени обработки практически нет. При множественной проверке компонент создает ощутимое влияние на систему, но если копнуть глубже, то создает данное влияние не сам компонент, а постоянный вызов события onParseDocument, который дергается при обработке любого типа контента. При этом, даже если сделать наш плагин пустым, то событие в любом случае будет дергаться и будет существенная задержка при генерации страницы. Можем ли мы с этим что-то сделать?

Да, но это потребует добавление нового события, которое будет выбрасываться в момент загрузки шаблона. К сожалению данный способ потребует внесения изменений в код самой MODX Revolution и, следовательно, правки при каждом ее обновлении. Но другие варианты, как например переписанный собственный парсер, у меня не получились. Давайте ради интереса повесим компонент на событие и замерим производительность.

Для того, чтобы повесить компонент на событие или устанавливаем пакет из ветки onParseTemplate, который сам пропишет собственное событие. Или создаем его вручную.

$event = $modx->newObject('modEvent');
$event->set('name', 'OnParseTemplate');
$event->set('service', 5);
$event->set('groupname', 'System');
$event->save();

В любом случае после установки компонента или создания события, как ранее упоминалось, придется внести изменения в файл core/model/modx/modtemplate.class.php, добавив код вызова события в районе 114 строки:

if (is_string($this-&gt;_output) && !empty($this->_output)) {

	/* turn the processed properties into placeholders */

	$this->xpdo->toPlaceholders($this->_properties, '', '.', true);

меняем на:

if (is_string($this->_output) && !empty($this->_output)) {
	/* invoke OnParseTemplate event */
	$this->xpdo->invokeEvent('OnParseTemplate', array('content' => &$this->_output));


	/* turn the processed properties into placeholders */
	$this->xpdo->toPlaceholders($this->_properties, '', '.', true);

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

Единичная проверка в шаблоне с выполнением условия

Перетаскиеваем сформированные 1000 ссылок в шаблон и оборачиваем в проверку:

[[^isfolder:is=`1`:then=`[[-`]]
<a href="/[[~[[*parent]]]]" data-doc="1">test</a>
…
<a href="/[[~[[*parent]]]]" data-doc="1000">test</a>
[[^isfolder:is=`1`:then=`]]`]]

, и делаем замер по заранее определенной схеме и снимаем показатели:

Родительский документ не кешированный
Количество обращений к БД6
Время отработки PHP0,12673187255859s
Пиковое потребление памяти2Mb
Родительский документ закешированный
Количество обращений к БД0
Время отработки PHP0,088196992874146s
Пиковое потребление памяти2Mb
Дочерний документ не кешированный
Количество обращений к БД6
Время отработки PHP0,20241808891296s
Пиковое потребление памяти4Mb
Дочерний документ закешированный
Количество обращений к БД0
Время отработки PHP0,089241981506348s
Пиковое потребление памяти2Mb
В логах MODX какие-либо предупреждения отсутствуют

Множественные проверки в шаблоне с выполнением условия

Вместо 1000 сформированных ссылок в шаблон помещаем 1000 конструкций, в которой ссылка проверяется по условию:

[[^isfolder:is=`1`:then=`[[-`]]
<a href="/[[~[[*parent]]]]" data-doc="1">test</a>
[[^isfolder:is=`1`:then=`]]`]]

, и делаем замер по заранее определенной схеме и снимаем показатели:

Родительский документ не кешированный
Количество обращений к БД6
Время отработки PHP0,67841005325317s
Пиковое потребление памяти4Mb
Родительский документ закешированный
Количество обращений к БД0
Время отработки PHP0,077855110168457s
Пиковое потребление памяти2Mb
Дочерний документ не кешированный
Количество обращений к БД6
Время отработки PHP0,76762890815735s
Пиковое потребление памяти6Mb
Дочерний документ закешированный
Количество обращений к БД0
Время отработки PHP0,084574937820435s
Пиковое потребление памяти2Mb
В логах MODX какие-либо предупреждения отсутствуют

Единичная проверка в шаблоне с чанком [[$testChunk]] с выполнением условия

Вместо 1000 конструкций возвращаемся к использованию нашего чанка, в котором у нас присутсвует 1000 сформированных ссылок. Используем в шаблоне один раз такую конструкцию:

[[^isfolder:is=`1`:then=`[[-`]]
[[$testChunk]]
[[^isfolder:is=`1`:then=`]]`]]

, и делаем замер по заранее определенной схеме и снимаем показатели:

Родительский документ не кешированный
Количество обращений к БД6
Время отработки PHP0,12418699264526s
Пиковое потребление памяти2Mb
Родительский документ закешированный
Количество обращений к БД0
Время отработки PHP0,086171865463257s
Пиковое потребление памяти2Mb
Дочерний документ не кешированный
Количество обращений к БД7
Время отработки PHP0,21409296989441s
Пиковое потребление памяти4Mb
Дочерний документ закешированный
Количество обращений к БД0
Время отработки PHP0,08700704574585s
Пиковое потребление памяти2Mb
В логах MODX какие-либо предупреждения отсутствуют

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

В общем как обычно, если будут предложения или пожелания по работе компонента, предлагайте :)

Комментарии (3)

  1. qqq27 декабря 2018, 10:32#
    Почему бы просто не использовать Fenom вместо модификаторов?
    1. Кудашев Сергей28 декабря 2018, 23:58#
      А почему бы просто не использовать Twig или Smarty, который из коробки есть?
    2. Andrey18 ноября 2019, 21:59#
      а так?
      // called chunk 'cnkName' 
      [[!cnk[[*template:is=`12` :then=`Name`]]]]