[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
modx.pro
6
7 028
+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
                                  Спасибо! Не внимательно смотрел…
                        Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                        46