[Twiggy] - шаблонизатор TWIG в MODX Revolution
Пакет Twiggy представляет из себя парсер расширяющий modParser.
Twiggy перехватывает обработку twig тегов на странице.
Подробнее ознакомится с TWIG можно на официальном сайте и на сайте сообщества TWIG.
При установке пакета в контексте web создаются две настройки:
настройки пакета
Настройки twig вынесены в отдельную секцию. Доступны следующие настройки:
Режиме песочницы. Добавляет изолированный режим в среду Twig, что делает его безопасным для оценки ненадежного кода.
По умолчанию twig запускается со следующими расширениями
Вы можете самостоятельно добавить нужные расширения, найдя их на гитхаб или написав самостоятельно. Расширение может определять тэги, фильтры, тесты, операторы, глобальные переменные и функции.
Тема расширений достаточно обширна и пока я ее опущу. На данный момент что необходимо знать
— файл расширения должен лежать по пути указанному в path_extensions
— имя класса расширения соответствует TwiggyExtensionимя
— имя расширения должно быть указано в настройке extensions
шаблоны twig
Twiggy автоматически подгружает шаблоны с таблицы modTemplate, а так же файловые шаблоны из папки указанной в настройке path_templates
Имена шаблонов соответствуют их названиям, например
создадим шаблон base в MODX
доступные переменные
Доступны следующие глобальные переменные
расширения Tools
Для большего удобства написано небольшое расширения Tools github.com/vgrish/twiggy/blob/master/core/components/twiggy/elements/extensions/tools.class.php
там описаны основные функции и фильтры для комфортной работы с MODX api.
Ниже несколько примеров:
вывести заголовок ресурса
получить объект modUserProfile и преобразовать в json
вывести почту авторизованного пользователя
подсчитать кол-во modUserProfile
вывести чанк
обратится к процессору и вывести результат работы
лексиконы
расширение Cache
Написано небольшое расширение для кэширования секций Cache github.com/vgrish/twiggy/blob/master/core/components/twiggy/elements/extensions/cache.class.php
пример, указываем ключ кэша и время
расширение DebugBar
phpdebugbar.com/
пример вывода

уницикация
Для большей унификации описаны методы getChunk и parseChunk аналогичные методам из пакета pdoTools Василия Наумкина.
Код на github, создавайте isuue
Готовый пакет пока можно установить из моего репозитория rstore.pro/extras/
PS. Не рекомендую к установке на рабочие проекты
Чтобы отключить работу Twiggy парсера достаточно удалить настройки из контекста
Twiggy перехватывает обработку twig тегов на странице.
Подробнее ознакомится с TWIG можно на официальном сайте и на сайте сообщества TWIG.
При установке пакета в контексте web создаются две настройки:
- parser_class — класс парсера
- parser_class_path — путь к классу парсера
настройки пакета
- twiggy_extensions — список активных расширений через запятую, для подключения в Twiggy.
- twiggy_path_extensions — путь для загрузки расширений Twiggy.
- twiggy_path_templates — путь для загрузки шаблонов Twiggy.
- sandbox — разрешить режим песочницы, при подключении шаблона в Twiggy.
Настройки twig вынесены в отдельную секцию. Доступны следующие настройки:
- debug: При установке в true, генерируемые шаблоны имеют__toString() метод, который можно использовать для отображения сгенерированных узлов (по умолчанию false).
- charset: Кодировка, используемая в шаблонах (по умолчанию в UTF-8)
- base_template_class: Шаблон базового класса, который используют для сгенерированных шаблонов (по умолчанию Twig_Template).
- cache: Абсолютный путь, где хранятся скомпилированные шаблоны или false чтобы отключить кэширование (который по умолчанию).
- auto_reload: При разработке с Twig, полезно повторно компилировать шаблон при изменении кода-исходника. Если вы не установили значение опции auto_reload, то она будет определена автоматически на основании переменной debug.
- strict_variables: Если установлено false, Twig будет по умолчанию игнорировать недействительные переменные (переменные и или атрибуты/методы, которые не существуют) и заменять их значением null. Когда устанавлено true, Twig генерирует исключение (по умолчанию false).
- autoescape: Если установить true, авто-сохранение будет разрешено по умолчанию для всех шаблонов (по умолчанию true). Начиная с Twig 1.8, вы можете выбрать какую методику сохранения использовать (html, js, false для блокировки). Начиная с Twig 1.9, вы можете выбрать какую методику сохранения использовать (css, url, html_attr, или обратный вызов PHP, который берет шаблон “имя файла” и должен вернуть методику сохранения для использования — обратный вызов не может быть названием функции, чтобы избежать конфликта (коллизии) со встроенными методами сохранения. optimizations: Флаг, который указывает, какие оптимизации применять (по умолчанию -1 — все оптимизации разрешены; установите его на 0 для отключения).
Режиме песочницы. Добавляет изолированный режим в среду Twig, что делает его безопасным для оценки ненадежного кода.
- sandbox_tags — список разрешенных тэгов через запятую, для подключения в режиме песочницы.
- sandbox_filters — список разрешенных фильтров через запятую, для подключения в режиме песочницы.
- sandbox_methods — строка, закодированная в JSON, с разрешенными методами для подключения в режиме песочницы.
- sandbox_properties — строка, закодированная в JSON, с разрешенными свойствами для подключения в режиме песочницы.
- sandbox_functions — Список разрешенных функций через запятую, для подключения в режиме песочницы.
По умолчанию twig запускается со следующими расширениями
- Twig_Extension_Core — определяет все основные возможности Twig.
- Twig_Extension_Escaper — добавляет автоматический вывод данных-сохранение, а также возможность сохранить/пропустить блоки кода
- Twig_Extension_Optimizer — оптимизирует древо узлов перед компиляцией
Вы можете самостоятельно добавить нужные расширения, найдя их на гитхаб или написав самостоятельно. Расширение может определять тэги, фильтры, тесты, операторы, глобальные переменные и функции.
Тема расширений достаточно обширна и пока я ее опущу. На данный момент что необходимо знать
— файл расширения должен лежать по пути указанному в path_extensions
— имя класса расширения соответствует TwiggyExtensionимя
— имя расширения должно быть указано в настройке extensions
шаблоны twig
Twiggy автоматически подгружает шаблоны с таблицы modTemplate, а так же файловые шаблоны из папки указанной в настройке path_templates
Имена шаблонов соответствуют их названиям, например
создадим шаблон base в MODX
<!DOCTYPE html>
<html>
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - Мой сайт</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2013 <a href="http://example.com/">Вы</a>.
{% endblock %}
</div>
</body>
</html>создадим расширяющий шаблон{% extends "base" %}
{% block title %}Главная{% endblock %}
{% block head %}
{{ parent() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<h1>Главная</h1>
<p class="important">
Приветсвую на своем потрясном сайте!
</p>
{% endblock %}доступные переменные
Доступны следующие глобальные переменные
- get — $_GET
- post — $_POST
- session — $_SESSION
- cookie — $_COOKIE
- request — $_REQUEST
- files — $_FILES
- server — $_SERVER
- modx — $modx
- pls — массив доступных плейсходеров
расширения Tools
Для большего удобства написано небольшое расширения Tools github.com/vgrish/twiggy/blob/master/core/components/twiggy/elements/extensions/tools.class.php
там описаны основные функции и фильтры для комфортной работы с MODX api.
Ниже несколько примеров:
вывести заголовок ресурса
{{ modx.resource.pagetitle }}
//или
{{ resource('pagetitle') }}получить объект modUserProfile и преобразовать в json
{{ getObject('modUserProfile', {'email':'admin@vgrish.ru'})|toJson }}вывести почту авторизованного пользователя
{{ profile('email') }}подсчитать кол-во modUserProfile
{{ getCount('modUserProfile') }}вывести чанк
{{ chunk('@INLINE {{ name }} - {{ fio }}', data) }}обратится к процессору и вывести результат работы
{% set response = processor('mgr/valute/getlist',{'ns':'currencyrate', 'sortdir':'asc'}) %}
{# { dump(response) }#}
{% for result in response.response.results %}
<li>
{{ result.name }}
</li>
{% endfor %}лексиконы
{{ loadLexicon('ru:core:default') }}
{{ _('access_denied') }}и тд. Если вам интересны задавайте вопросы.расширение Cache
Написано небольшое расширение для кэширования секций Cache github.com/vgrish/twiggy/blob/master/core/components/twiggy/elements/extensions/cache.class.php
пример, указываем ключ кэша и время
{% cache 'neighbors' 3000 %}
{{ snippet('pdoNeighbors') }}
{% endcache %}расширение DebugBar
phpdebugbar.com/
пример вывода
{% if hasSessionContext('mgr') %}
{{ dbgHead() }}
{{ dbgMessage(array) }}
{{ dbgRender() }}
{% endif %}
уницикация
Для большей унификации описаны методы getChunk и parseChunk аналогичные методам из пакета pdoTools Василия Наумкина.
$Twiggy->getChunk('twig', $row);Код на github, создавайте isuue
Готовый пакет пока можно установить из моего репозитория rstore.pro/extras/
PS. Не рекомендую к установке на рабочие проекты
Чтобы отключить работу Twiggy парсера достаточно удалить настройки из контекста
Комментарии: 43
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
Вообще сборка шаблона на сервере, по-моему большая ошибка. Зачем сервер загружать тем, что должно делаться во фронте 1 раз при сборке проекта? А так каждый сброс кэша — сборка шаблона. Практически отказался по этой причине от Fenom (пусть он и очень быстрый), использую только для удобства написания условий.
Это мое мнение при всем уважении к Володе и Василию :)
По поводу вашей реализации с интересном бы прочел.
Спасибо!
И тут и там компилируется шаблон/код и хранить его явно в html нет никакого смысла, иначе бы все это отрабатывало один раз, при первичной загрузке.
Вы можете использовать кэширование секций, указав ключ кэша и время
данная секция сохранится в html
Fi1osofОн же не скомпилированный. Кеширование отдыхает тогда в принципе. Это то, что было долгое время в самом MODX (я раньше не однократно писал про его проблемы, из-за того, что он даже кешируемые теги в кеш пишет как есть, и потом все равно каждый раз реплейс делает), и что в MODX наконец-то поправилось (теперь он в кеш пишет сразу конечный HTML из кешируемых элементов), и теперь ты это достижение ломаешь. Шаблоны будут чуть более гибкие, но зато более тормознутые.
Хранить в конечном html очень даже есть смысл, в этом и есть смысл кеширования ресурсов. А вот то, что иначе не будет отрабатываться каждый раз — так вот для этого и были придуманы не кешируемые MODX-теги. Напиши один сниппет вида [[!twig?tpl=`some_tpl`]] и все. И где надо некешируемые блоки вставлять — там его и прописывать в шаблонах.
Еще раз: в MODX с точки зрения шаблонизации только два минуса:
1. нет компиляции в нормальный php (чтобы логика была)
2. нет расширения шаблонов.
В остальном она довольно продвинутая (особенно крут вложенный парсинг).
Ты добавляешь расширяемости и логики, но ломаешь родное кеширование.
Fi1osofВот это еще глянь, должно пригодиться: modx.pro/help/7278/#comment-52671
Тогда изначальна вся затея ни к чему. Вся гибкость шаблонизатора пропадает.
Или я не прав?
Fi1osofПри таком подходе и общий шаблон кешируется, и есть гибкость на динамических блоках.
Fi1osofОпять к вопросу о сниппете.
Создал ресурс, вызвал сниппет
смотрю в кэш ресурса — логично там именно этот вызов сниппета.
Вопрос все тот же — чем это помогает кэшированию? В чем фишка я не пойму.
Спасибо!
Fi1osofЧтобы это помогало кешированию, надо вызов делать кешируемым. [[smarty?tpl=`tpl/test.tpl`]]
А некешируемые вызовы — это уже внутри самого шаблона, к примеру
Здесь разъяснять же не надо?
Fi1osofВот у тебя MODX-шаблон «Основной»:
[[smarty?tpl=`tpl/index.tpl`]]
В tpl/index.tpl прописано:
А вот у тебя MODX-шаблон «Главная страница»:
[[smarty?tpl=`tpl/mainpage.tpl`]]
В tpl/mainpage.tpl прописано:
На выходе все кроме [[!smarty?tpl=`inc/login.tpl`]] у тебя будет кешируемым, на главной помимо контента еще и слайдер появится, и не придется дублировать код в MODX-шаблонах.
Fi1osofВопрос первый: Зачем тебе вот это? Оно итак в modResponse::outputContent() отрабатывается. (на самом деле я предполагаю зачем оно тебе, но тут несколько вариантов, так что хотелось бы именно твой вариант услышать).
И если включена настройка pdotools_fenom_parser и сниппеты, регистрирующие скрипты и стили, вызываются прямо в контенте документа, то файлы от них в эти массивы они уже не попадают и приходится добавлять их вручную. Вот тут багрепорт.
Fi1osofПочему я про этот кусок кода спросил? Во-первых, этот метод парсера в процессе отработки документам может быть вызван не один раз. Во-вторых, в разные стадии вызова его в массивах этих могут содержаться разные наборы скриптов (что не страшно, конечно же, ибо все равно они будут в шаблон подставлены только в одном месте, но как минимум бессмысленно).
А вообще, тут всё просто. Без этой правки скрипты и стили из кэшируемых сниппетов, вызванных через Fenom, не добавлялись на страницу. А с ней — добавляются.
Сделать так мне показалось просто и логично, но если ты считаешь, что MODX нужно делать дополнительный вызов плагина для этой операции — шли PR на GitHub, добавим.
Fi1osofТут ответ однозначный: где-то неверная последовательность вызова/вывода. Ты четко знаешь когда бага возникает. Если не лень, создай тестовый сайт с этой багой (феном + минифи), закомментируй этот свой хак и пришли доступ, я посмотрю. Уверен, найду суть проблемы.
Может быть.
Но по моему, дело в том, что теги Fenom на странице отрабатывают после процессинга документа через MODX, и данные кэшированных сниппетов не попадают в массивы скриптов и стилей. То есть, сниппет кэшируется, а то что он регистрировал в первый раз — нет.
Вот тестовый сайт:
чанк с кэшированным вызовом MinifyX
класс pdoParser с закоментированным добавлением скриптов и стилей после обработки тегов Fenom
Логин s3914
Пароль UAChpcjgpO9r
Fi1osofЯ пока не буду ничего отвечать. Мысли есть, но нужны факты. Щас поковыряю сайт и по результатам отпишусь.
Тем более, что технически ничего не мешает написать сторонний парсер, который будет наследовать pdoTools со всеми его возможностями. А pdoParser просто отключить при установке и возвращать обратно при удалении.
Но это моё сугубо личное мнение, никому не навязываю.
Fi1osofFi1osofВсе верно. Из-за этого и была проблема. Дело в том, что $modx->regClientStartupScript() и подобные методы работают со свойствами самого $modx, а вот при сохранении кеша используются свойства самого ресурса. А так, как в отработанных уже после процессинга тегах выполняется типа $modx->regClientStartupScript() (который устанавливает свойства для $modx, но не устанавливает их для $modx->resource), то при генерации кеша документа этих скриптов в кеше просто нет. joxi.ru/4Ak3wb9tMX8nGA
Решение: пишем плагин на событие OnBeforeSaveWebPageCache, простейший вид:
И тогда при генерации кеша документа будут сохранены все скрипты. joxi.ru/LmGVQx0uRJN1Xr
При чем это будет выполняться только при первом заходе на страницу. Когда документ уже закеширован будет, это не будет выполняться.
UPD: Может даже имеет смысл это в ядро запулить (то есть код кешманагера поправить), так как очень похоже на багу самого MODX-а. Какая-то глупость в двух отдельных сущностях хранить эти переменные и создавать/получать в разных местах на разных этапах.
Думаю, ничего страшного не случится, если я пока оставлю всё как есть.
Fi1osofКонечно нет))
PS: разрабатываю новый сайт, и вопрос опять встал ребром — какой шаблонизатор использовать. Благо для modx уже есть выбор-)
Fi1osofFi1osof1. pdoResources
2. Smarty + getdata-процессор.
В консоли:
Шаблон:
3. Smarty + чистый xPDO
В консоли:
Шаблон:
Разница очевидна.
P.S. наверняка феном + чистый xPDO тоже хороший результат покажет.
При пустом шаблоне документа:
[[!Fenom?&tpl=`@INLINE {var $resources = $_modx->getResources([], ['limit' => 4000])} {foreach $resources as $resource} <p>{$resource.id} - {$resource.menuindex} - {$resource.createdon} - {$resource.publishedon}</p> {/foreach} <pre>{$_modx->getInfo()}</pre> `]]Результат:
Fi1osofИли я не прав? Если так, то все равно, хоть и не из базы данных, но все равно объект каждый раз создается. Так не может быстро работать. Вот если модифицировать в такое (упрощенно), то должно быстрее работать.
Но и оверхеда всякого достаточно, хотя лично мне скорости вполне хватает, чтобы не морочиться дальше. Решение должно быть достаточно универсально и работать удобно из коробки, без танцев.
Сейчас заканчиваю один сайт, полностью построенный на Fenom и готовых дополнениях — все страницы открываются за 0.2 — 0.3 сек., может позже напишу заметку с подробностями.
Fi1osofgist.github.com/vgrish/8189582355782ec13394
totalTime: 1.3793 s
queryTime: 0.0526 s
phpTime: 1.3267 s
queries: 9
source: cache
в консоли xPDO
gist.github.com/vgrish/2530a0364bf49b05481f
totalTime: 0.1909 s
queryTime: 0.0011 s
phpTime: 0.1898 s
queries: 3
source: cache
Думаю, не только мне будет интересно прочитать.
П.С. Подобные ошибки валятся и из некоторых других библиотек, с которыми я экспериментирую. Поэтому хотелось бы покончить с ними раз и навсегда. В других шаблонизаторах используется особый синтаксис типа {{ или {% или {!!. Что теоретически должно исключить данную проблему.
П.П.С. А вообще, идея совмещать 2 механизма парсинга хреновая. Ни один нормально не работает. Это относится к любому стороннему шаблонизатору в MODX.
Мне односкобочный синтаксис очень люб. Гибкость, быстрота и легкая расширяемость феном — что еще нужно?
Прямых конкурентов я не вижу…
не сталкиваюсь с таким, потому и решать не приходилось.