[pdoTools] Версия 2.0 с шаблонизатором и кэшем

Доступна стабильная версия pdoTools 2.0, в которой я уже определился, как именно должен работать шаблонизатор Fenom.

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

Сначала настройки:

  • pdotools_fenom_default включает обработку синтаксиса Fenom во всех чанках сайта.
  • pdotools_fenom_parser включает обработку синтаксиса Fenom на страницах сайта. Контент ресурсов, шаблоны — везде. По умолчанию отключено.
  • pdotools_fenom_php включает возможность выполнения произвольных функций PHP в шаблонах через {$.php.функция()}. Опция эта очень опасная, так что тоже отключена.
  • pdotools_fenom_modx — чуть менее опасная опция, но во многих случаях, пока, необходимая — работа с объектами modX и pdoTools через переменные {$modx} и {$pdoTools}. Если вы не доверяете своим менеджерам — выключите её от греха подальше, потому что через объект modX можно удалить начисто весь сайт.
  • pdotools_fenom_cache — включает кэшированние чанков (только чанков, не страниц сайта) через кэшер MODX (а не как раньше). Стоит использовать только на продакшн сайтах при больших и сложных чанках.
Параметра &useFenom у сниппетов больше нет, включение шаблонизатора возможно только из системных настроек.

Порядок запуска шаблонизатора


Еще одним важным изменением является доработанный порядок обработки страниц сайта. Теперь, если включен pdoParser и системная опция pdotools_fenom_parser, то шаблонизатор запускается ровно вот здесь.

В этот момент все кэшированные чанки и сниппеты на странице обработаны (или загружены из кэша) и вы можете использовать вот такие конструкции:
{if $.get.test == 1}
	[[!pdoResources?parents=`0`]]
{else}
	[[!pdoMenu?parents=`0`]]
{/if}
То есть, в зависимости от $_GET['test'] на странице будет запущен или один сниппет или другой. Парсер MODX же запустил бы оба и результат выполнения одного неподходящего просто не показал.

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

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

Кэширование чанков Fenom


По умолчанию этот функционал Fenom отключен, потому что по моим тестам, толку от него в MODX нет. Но, это на моих мелких и простых чанках, а у вас может быть что-то посложнее.

Поэтому вы можете включить системную настройку pdotools_fenom_cache и тогда скомпилированные шаблоны будут сохранены в /cache/default/fenom/ в зависимости от своего типа.

Чанки из БД кэшируются под своими id, а INLINE именуются как хэш от своего содержимого, то есть — путь к обычному чанку будет выглядеть как cache/default/fenom/chunk/90.cache.php, а к INLINE уже как cache/default/fenom/inline/35e115c27fdc3814b6f41a1015aa67e6.cache.php.

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

Как это работает дальше?

При первом запуске с пустым кэшем pdoTools получает нужный чанк, определяет его тип и отдаёт в Fenom.
Тот компилирует шаблон и сохраняет его во внутренний кэш pdoTools методом setStore(). Этот кэш находится в ОЗУ и сохраняется только на время выполнения скрипта, он нужен чтобы не компилировать 10 раз один и тот же чанк при выводе pdoResources.

А вот если включена опция pdotools_fenom_cache, то исходный код скомпилированного шаблона сохраняется на HDD сервера, и при следующем запуске Fenom уже не нужно его компилировать. Кэшер MODX отдаёт исходный код, из него получается объект Fenom\Render который передаётся в setStore() и оттуда уже работает.

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

Обычно выходит, что на маленьких и простых чанках (как у сниппетов pdoTools) выигрыша нет, а лишних файлов много, а вот на больших и сложных чанках (которые вы наверняка создадите, используя возможности Fenom) разница уже может быть. Время компиляции и работы с кэшем выводится в &showLog=`1`, так что каждый может проверить сам.

Кэширование pdoMenu


В новой версии доработаны методы pdoTools::setCache() и pdoTools::getCache(), так что теперь данные всегда сохраняются в /cache/default/. А если указан параметр &cache_key, то данные будут сохраняться под этим именем для всех страниц сайта.

Из этого следует улучшение pdoMenu, который при включенном параметре &cache=`1` сохраняет чистые данные, а при загрузке страницы тратит время только на оформление чанков.
Меню кэшируется один раз для всего сайта и работает без каких-либо проблем на всех страницах, правильно показывая активные пункты.

А вот pdoPage работает как и прежде, кэшируя уже готовый вывод запущенного внутри него сниппета, сохраняя его только для своей страницы.

Разница в том, что меню, как правило, для всех страниц сайта одинаковое, а пагинация в документах уникальна.
Возможно, в будущем я научу pdoResources также кэшировать данные, как и pdoMenu, и тогда pdoPage не нужно будет сохранять уже обработанный вывод.
Это зависит от ваших отзывов касательно новой работы меню.

Заключение


В стабильной версии pdoTools 2.0 добавлены системные параметры для ограничения потенциально опасных функций нового шаблонизатора Fenom, улучшен кэш pdoMenu и заложен функционал для развития кэширования в других сниппетах.

Дополнение доступно как в нашем репозитории, так и в официальном. На данный момент совокупное количество закачек pdoTools составляет почти 45 000 раз, при этом тестовые сайты на modhost.pro никак не учитывается.

Планы на будущее:
  • Обновление документации на docs.modx.pro/components/pdotools/
  • Подумать над кэшированием других сниппетов
  • Попробовать подружить debugParser с Fenom
  • Добавление новых тегов Fenom, чтобы можно было вызывать сниппеты, чанки и строки лексикона без объекта {$modx}

Обновлено 31.05.2015


В версии pdoTools 2.0.1-pl системная настройка pdotools_fenom_modx отключена по умолчанию, из соображений безопасности.
Василий Наумкин
22 мая 2015, 05:21
6
5 331
+13
Поблагодарить автора Отправить деньги

Комментарии: 46

Spam
22 мая 2015, 09:53
0
траблы с пагинацией s1908.h3.modhost.pro/index.php?id=1&id=1
[[!pdoPage?	
				&parents=`2`
				&fastMode=`1`
				&setMeta=`0`
				&limit=`2`
				&maxLimit=`2`
				&cache=`1`
				&cacheTime=`0`
				&cache_key=`main_page`
				&scheme=`abs`
				&showHidden=`0`
				&hideContainers=`1`
				&tpl=`@INLINE {$id} - {$pagetitle}<br_/_>`
				&ajax=`1`
			]][[!+page.nav]]
    Spam
    22 мая 2015, 10:14
    0
    ну и debugParser обновить бы, чтобы видеть $modx->runSnippet, если это возможно
    Василий Наумкин
    22 мая 2015, 10:24
    0
    Не нужно указывать cache_key для pdoPage.
      Spam
      22 мая 2015, 10:31
      0
      тогда, как я понимаю надо будет включать кеш феном. Ибо когда +300 страниц, и на каждой странице (?page=) будет время 3-10 секунд
        Василий Наумкин
        22 мая 2015, 11:26
        +1
        Нет, ты неправильно понимаешь.

        Раньше &cache_key указывал только префикс для кэша — директорию куда его складывать. Сейчас префикс по умолчанию default, если указывается свой &cache_key. Если не указывается, то для pdoPage генерируется ключ по старому алгоритму, который отправляет его в кэш ресурса.

        То есть, указав произвольную строку можно получить кэширование сниппета для всех страниц сайта. Но в случае с pdoPage это не имеет смысла, потому что контент меняется в зависимости от номера страницы, поэтому там лучше оставить автоматическую работу.

        Но, если вдруг тебе необходимо на разных страницах сайта выводить один и тот же контент, с одной и той же постраничной навигацией, то теперь можно сделать и так:
        [[!pdoPage?
        	&parents=`0`
        	&ajaxMode=`default`
        	&cache=`1`
        	&cache_key=`pdopage/page1-[[!#request.page]]`
        ]]
        Еще раз повторяю — раньше такого не было, ты просто менял директорию для хранения кэша, а внутри там всё равно были пути к ресурсам.

        Сейчас всё гораздо круче:


        Ибо когда +300 страниц, и на каждой странице (?page=) будет время 3-10 секунд
        У нас здесь тысячи страниц выводятся без кэша — и нет проблем. Как так?
      Spam
      22 мая 2015, 10:45
      0
      как можно реализовать кэширования для всех страниц pdoPage?
Виктор
22 мая 2015, 12:25
0
А что все-таки лучше для вызова чанков, например?
Обновившись, прогнал эти вызовы еще раз и получается тоже самое, если:

— возможен частый вызов пустого чанка то pdoTools, потому что не делает запрос при пустом значении
{$pdoTools->getChunk($modx->resource->id == 1 ? '' : 'chunk2')}
— чанк всегда выводится, то $modx, потому что не делает лишние запросы на вызов чанка
{$modx->getChunk($modx->resource->id == 1 ? 'chunk1' : 'chunk2')}
Есть ли выигрышн во времени пока проверить возможности нет.
    Василий Наумкин
    22 мая 2015, 12:55
    0
    Ну а тут разницы особой и быть не может, потому что с одной стороны разбор тега MODX и создание из него объекта, что занимает милисекунды, а с другой — компиляция шаблона или вытаскивание его из кеша.

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

    Мне больше нравится новый синтаксис, потому что там нормальные массивы и типы переменных.
      Виктор
      22 мая 2015, 13:19
      +1
      О мне тоже новый синтаксис, одни переменные чего стоят. Единственное что пока удручает это то что 1 ошибка ломает всю страницу, но жить можно. Осталось привыкнуть к новому синтаксису и вытащить его в продакшен.
        Василий Наумкин
        22 мая 2015, 13:22
        0
        Это же наоборот круто, больше не будет пустых или кривых тегов, от которых может повеситься парсер MODX.

        В конце концов, любые изменения оформления сайта — это разработка, и её нужно проверять.
Spam
24 мая 2015, 12:26
0
Я так понимаю, теперь в &where надо ставить {ignore}, если там json
&where=`{ignore}{"uri:LIKE": "food/B%"}{/ignore}`
Юрий Волощук
30 мая 2015, 01:19
0
Поясните мне, пожалуйста как использовать fenom, где его надо прописывать, в чанках или в шаблоне, или ещё где.
Я пытался сделать в шаблоне так
{if $id==1}
1
{else}
not 1
{/if}
В настройках установил параметр
pdotools_fenom_parser в Да
но мне вывело не обработаное. Что я делаю не так. Как вообще это всё работает. Уж очень нужная и полезная штука, хотелось бы разобраться.
Владимир
31 мая 2015, 01:05
0
Василий, отправил тебе СПАСИБО!
PdoTools плюс Fenom — это круто!
Чего лично мне не хватало в pdoTools, так это для какой то конкретной, выводимой в ленте сайта статьи, использовать свое оформление не злоупотребляя модификаторами в чанках, т.е. понятно, что четные, нечетные и NN-е чанки-шаблоны многое решали, но теперь можно легко менять оформление комбинируя с различными условиями.
Шикарно)
Evgeny Epifanov
02 июня 2015, 23:57
0
Подскажите, а как можно этим способом проверить наличие дочерних документов?
    Виктор
    03 июня 2015, 01:03
    +1
    у ресурса нет информации о том есть ли у него дочерние ресурсы, нужно либо проверять на наличие в бд ресурсов с parent = id, либо верить чекбоксу «Контейнер»
    Василий Наумкин
    03 июня 2015, 06:26
    0
    {if $modx->getCount('modResource', ['parent' => 10, 'published' => 1, 'deleted' => 0])}
    Потомки есть
    {else}
    Потомков нет
    {/if}
    Должен быть включен системный параметр pdotools_fenom_modx.
      Evgeny Epifanov
      05 июня 2015, 18:00
      0
      Спасибо. Будем пробовать.
Михаил Божко
04 июня 2015, 01:07
0
Почему может не работать Fenom на страницах? При том, что на шаблоне BaseTemplate он работает.
    Василий Наумкин
    04 июня 2015, 07:34
    0
    В шаблоне — это и есть на странице.

    Может быть, просто неправильный код не компилируется — смотри в логе ошибок.
      Михаил Божко
      04 июня 2015, 09:46
      0
      Не, код верный и один и тот же {$modx->resource->pagetitle}. И даже в теле одного и того же ресурса, только при разных шаблонах. Если у ресурса шаблон BaseTemplate, то всё работает (выводит заголовок страницы), а если шаблон мой, то код не компилируется, а просто выводится текстом {$modx->resource->pagetitle}. Похоже, что Fenom в pdoParser просто-напросто не включается, но такие плейсхолдеры работают [[#2.pagetitle]].
      Михаил Божко
      04 июня 2015, 09:52
      0
      Ааа, похоже конфликт возникает из-за Я.Метрики вот из-за таких конструкций:
      Ya.Metrika({id:44444
Evgeny Epifanov
06 июня 2015, 10:37
0
Вопрос относится не совсем к этой ветке, но возник он в связи с Fenom'ом.
Имеем такую конструкцию в чанке:
{set $parent = $modx->resource->parent}
        {set $vendormenuclasskey = $modx->getObject('modResource', $parent)->class_key}
        [[!pdoMenu@{$vendormenuclasskey == "msCategory" ? "vendor-list-subcat" : "vendor-list-cat"}? 
        	&level=`1`
        	&displayStart=`1`
                &cache=`1` 
                &firstClass=`0` 
                &lastClass=`0` 
                &tplOuter=`@INLINE [[+wrapper]]` 
                &tpl=`@INLINE <li[[+classes]]><a href="[[+link]]" title="[[+longtitle]]" [[+attributes]]>[[+menutitle]]</a>[[+wrapper]]</li>` 
                &showLog=`1`
                &showUnpublished=`1`
         ]]
В параметрах указан 'parents' ([[*id]] либо [[*parent]]) и 'tplStart' (@INLINE ...)
Результат — полный список всех ресурсов, как будто «parents=`0`».
Прописываю:
[[!pdoMenu@vendor-list-cat?
	....
]]
Результат такой же.
Только если прописать все параметры непосредственно в чанке (без набора параметров), работает как нужно.
С [[mFilter2]] такая же конструкция работает нормально, а тут вообще наборы не работают.

Работа [[pdoMenu]] так задумана или я снова где-то накосячил?
    Василий Наумкин
    06 июня 2015, 10:42
    0
    В параметрах указан 'parents' ([[*id]] либо [[*parent]])
    Вполне возможно, что оно вот так и попадает в сниппет, необработанным.

    Сделай там в начале сниппета
    echo '<pre>';print_r($scriptProperties);die;
    и посмотри, что приходит.
      Evgeny Epifanov
      06 июня 2015, 10:55
      0
      Все верно, так и есть. Спасибо.
      А что можно сделать в таком случае?
        Василий Наумкин
        06 июня 2015, 11:06
        0
        Прописывать те параметры, в которых есть теги MODX явно, а не в наборы. Иначе парсер не может их обработать.

        Как-то так:
        [[!pdoMenu@{$vendormenuclasskey == "msCategory" ? "vendor-list-subcat" : "vendor-list-cat"}? 
        	&parents=`{$vendormenuclasskey == "msCategory" ? "[[*id]]" : "[[*parent]]"}`
        ]]

        Даже лучше так:
        {if $vendormenuclasskey == 'msCategory'}
        [[!pdoMenu@vendor-list-cat? 
        	&parents=`[[*id]]`
        ]]
        {else}
        [[!pdoMenu@vendor-list-subcat? 
        	&parents=`[[*parent]]`
        ]]
        {/if}
        После обработки Fenom останется всё равно только один вызов.
          Evgeny Epifanov
          06 июня 2015, 11:15
          0
          Большое спасибо, Василий.
Ян Сонов
15 июня 2015, 01:02
0
Почему-то при включении в настройках фенома для страниц, перестает работать феном в инлайн чанках (в обычных чанках работает). Пробовал только для pdoMenu:
&tpl = `@INLINE <li><a href="{$uri}">{$pagetitle}</a></li>`

В логе ошибок нет, просто не «отрабатывают» плейсхолдеры фенома и выводится:
<li><a href=""></a></li>
Алексей
12 сентября 2015, 10:02
0
Добрый день! возник вопрос:
как в сниппете AjaxForm указать плейсхолдеры, принимаемые из формы и передаваемые в FormIt? Раньше синтаксис был такой:
'emailTo' 			=> 'order@[[++http_host]]',
'emailFrom' 			=> 'order@[[++http_host]]',
'emailReplyTo' 			=> '[[+email]]',
'emailReplyToName' 		=> '[[+name]]',
а как это сделать через fenom?
вот до чего дошел:
'emailTo' 			=> 'order@{$_modx->config.http_host}',
'emailFrom' 			=> 'order@{$_modx->config.http_host}',
'emailReplyTo' 			=> '[[+email]]',
'emailReplyToName' 		=> '[[+name]]',
Никак не могу взять в толк, откуда fenom'у взять эти плейсхолдеры и передать в сниппет Formit
Полный вызов сниппета
{$_modx->runSnippet('!AjaxForm', [
	'as_mode' 	=> 'onload',
	'snippet' 	=> 'FormIt',
	'form' 		=> 'formscontact',
	'emailTpl' 		=> 'EmailContactForm',
	'hooks' 		=> 'email',
	'emailFromName'		=> '{$_modx->config.site_name}',
	'emailTo' 		=> 'order@{$_modx->config.http_host}',
	'emailFrom' 		=> 'order@{$_modx->config.http_host}',
	'emailReplyTo' 		=> '[[+email]]',
	'emailReplyToName' 	=> '[[+name]]',
	'validate' 		=> 'name:required',
	'validationErrorMessage'	=> 'В форме содержатся ошибки!',
	'successMessage' 		=> 'Сообщение успешно отправлено'
])}
Wassi Wassinen
30 января 2016, 23:56
0
Василий, а так и задумано, что в pdoMenu не работает общий параметр tpl_n и tpl_nN?

И второй вопрос-предложение — можешь добавить в блок приглашения к авторизации, на месте создания комментария ( joxi.ru/Vrw8O87IKvoydm ), такую же ссылку, как и в блоке вверху справа ( joxi.ru/RmzxWxJhWGvE5A )? Когда неавторизован, будет удобно сразу же логиниться, без промотки наверх. Спасибо.
    Воеводский Михаил
    31 января 2016, 02:31
    0
    Так и задумано. У pdoMenu используется чуть иная логика оборачивания данных в чанки. Связано это с древовидностью вывода, в отличие от простых перечней остальных сниппетов.
      Wassi Wassinen
      31 января 2016, 03:31
      0
      Спасибо за ответ.
      Wassi Wassinen
      31 января 2016, 19:27
      0
      Михаил, подскажите, когда использую вызов pdoPage с &element=`msProducts` и пытаюсь фильтровать по пользователю через &user=`[[+modx.user.id]]` то выводит все товары, независимо от того, кто их создал. Это как-то связано с логикой работы msPdoducts или ошибка? И если ошибка, то подскажите как отфильтровать по createdby через &where.

      Заранее благодарен.
Богдан
08 февраля 2016, 16:18
0
Подскажите, как с помощью Fenom вывести плейсхолдер с модификатором, а точнее это:
[[*publishedon:strtotime:date=`%e %B %Y`]]
Заранее благодарю за помощь!
      Богдан
      08 февраля 2016, 16:36
      0
      Василий, спасибо, я это видел, но не понял как использовать. Пробовал так:
      {$_modx->resource.publishedon|date_format:$format = `%e %B %Y`}
      не работает:(
        Василий Наумкин
        08 февраля 2016, 16:38
        +1
        Вот эти строки только мне видно?
        {$ts|date_format:"%Y/%m/%d %H:%M:%s"} выведет 2013/02/08 21:01:43
        {$ts|date_format:"-1 day"} выведет вчерашний день, например 2013/02/07 21:01:43
          Богдан
          08 февраля 2016, 17:11
          0
          Я видел эти строки. Выше я привел пример, как пробовал я, по аналогии с этими строками.
            Василий Наумкин
            08 февраля 2016, 17:14
            +1
            Значит только я вижу разницу между строкой с описанием функции в общем формате, и двумя рабочими примерами ниже.

            Вот так правильно:
            {$_modx->resource.publishedon | date_format : '%e %B %Y'}
              Богдан
              08 февраля 2016, 17:19
              0
              Спасибо! Не внимательно смотрел…
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.