Изменения

Перейти к: навигация, поиск

Шаблонизатор VMX::Template

5282 байта добавлено, 20:43, 27 июля 2018
Нет описания правки
'''VMX::Template''' — простой и высокопроизводительный шаблонизатор, имеющий Perl- и PHP-версии (основная — в данный момент PHP).
* Лицензия: GNU GPL версии 3 или новее
* Исходники Полный набор исходников здесь: http://yourcmc.ru/git/vitalif/VMXTemplate** PHP-версия (PHP>= 5.4): {{SVN|[http://yourcmc.ru/git/vitalif/trunkVMXTemplate/Templateraw/master/}} — для работы нужны файлы template.php template.php] и [http://yourcmc.ru/git/vitalif/VMXTemplate/raw/master/template.parser.phptemplate. Исходник грамматики в parser.php].** Новая Perl-версия: [{{SVN|http://yourcmc.ru/git/vitalif/trunkVMXTemplate/Templateraw/templatemaster/VMXTemplate.lime}} templatepm VMXTemplate.limepm]и все его [http://yourcmc.ru/git/tree/VMXTemplate.git/master/VMXTemplate подмодули VMXTemplate/*.pm].* Исходники (* Старая Perl), несколько устаревшие-версия: [{{SVN|vitaphotohttp:/solstice/lib-swayyourcmc.ru/VMXgit/Templatevitalif/VMXTemplate/raw/master/VMX%2FTemplate.pm}} VMX/Template.pm], и [{{SVN|vitaphotohttp:/solstice/lib-swayyourcmc.ru/git/vitalif/VMXTemplate/raw/master/VMX%2FCommon.pm VMX/Common.pm].** Исходный код грамматики PHP-версии (LALR(1) [http://yourcmc.ru/git/vitalif/lime LIME]): [http://yourcmc.ru/git/vitalif/VMXTemplate/raw/master/template.lime template.lime].** Исходный код грамматики Perl-версии (LALR(1) {{CPAN|Parse::Yapp}} Common): [http://yourcmc.ru/git/vitalif/VMXTemplate/raw/master/template.yp template.yp] и [http://yourcmc.ru/git/vitalif/VMXTemplate/raw/master/template.skel.pm template.skel.pm].** Исходный код голой грамматики (LALR(1) yacc): [http://yourcmc.ru/git/vitalif/VMXTemplate/raw/master/template.y template.y].** В Git-репозитории можно найти полную историю разработки, начиная с самых старых phpbb-подобный версий.
* Простые настройки для подсветки синтаксиса шаблонов в [http://www.midnight-commander.org/ Midnight Commander]'а: [{{SVN|vitalif/trunk/scripts/tpl.syntax|markup}} tpl.syntax]. Чтобы подсветка нормально выглядела, к tpl.syntax в начало надо дописать html.syntax из стандартного комплекта поставки mc.
=== Perl ===
[[File:Warning iconPerl версия обновлена и теперь в точности соответствует PHP-версии.svg|32px|link=]] '''Версия устарела! См. [http://www.yourcmc.ru/wiki/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%82%D0%BE%D1%80_VMX::Template&oldid=6200 старую версию документации].''' {{WikiCutBegin|Всё-таки посмотреть на использование в Perl}}
<source lang="perl">
use VMX::TemplateVMXTemplate;
# Конструктор
$template = new VMX::TemplateVMXTemplate(
'root' => '.', # директория с шаблонами
'cache_dir' => undef, # директория для кэширования компилированного кода шаблонов
# если >0, то шаблоны будут перечитываться с диска не чаще чем раз в reload секунд
'print_error' => 1, # если TRUE, ошибки компиляции попадают в вывод шаблона
'log_error' => 1, # если TRUE, ошибки компиляции печатаются на STDERR
'raise_error' => 0, # если TRUE, при ошибке компиляции вызывается die()
'use_utf8' => undef, # если TRUE, использовать "use utf8" на шаблонах
'begin_code' => '<!--', # маркер начала директивы кода
'end_code' => '-->', # маркер конца директивы кода
'eat_code_line' => 1, # (похоже на TT CHOMP) съедать "лишний" перевод строки, если в строке только директива?
'begin_subst' => '{', # маркер начала подстановки выражения
'end_subst' => '}', # маркер конца подстановки выражения
'eat_code_line' => 1, # (похоже на TT CHOMP) если TRUE, съедать "лишний" перевод строки, если в строке только директива кода (begin_code..end_code)
'no_code_subst' => 1, # если TRUE, выполнять директивы кода (begin_code..end_code), но игнорировать их результат
'compiletime_functions' => # дополнительные компилируемые функции
{ 'func' => sub {} }, # хеш вида имя функции (в шаблонах) => coderef,
# которому передаются скомпилированные выражения всех аргументов и первым - сам $templateпарсер (объект VMXTemplate::Parser) 'filters' => [ sub {}, .. ], # немного legacyфильтры для запуска на выводе каждого внешнего шаблона (фильтр - функция, устаревшее:модифицирующая $_[0]) 'wrapperstrip_space' => undef0, # если coderefTRUE, через это будет пропущен вывод удалять пробелы и табы из начала и конца всех шаблонов ("глобальный фильтр")строк вывода 'strict_endauto_escape' => 0'', # требовать <!функция авто-экранирования, например "s" (для HTML- END имя_блока --> после <!-- BEGIN имя_блока -->безопасного режима)
);
# Присвоение переменных:$template->vars("ключ" { var => "значение", "ключ" => "значение"'value', ...});
# Выполнения полностью аналогичны PHP:
$template->clear;
</source>
 
{{WikiCutEnd}}
== Синтаксис шаблонов ==
Если вы знаете о порождающих грамматиках, то вот контекстно-свободная грамматика для LALR(1) алгоритма разбора:
* Голая Bison-грамматика, для получения представления о синтаксисе: {{SVN|http://yourcmc.ru/git/vitalif/trunkVMXTemplate/raw/Templatemaster/template.y|markup}}* Рабочая [httpshttp://github.com/vitalif/lime LIME]-грамматика: {{SVN|http://yourcmc.ru/git/vitalif/trunkVMXTemplate/raw/Templatemaster/template.lime|markup}} (для её работы нужен патченый LIME — см. https://github.com/vitalif/lime)* Рабочая {{CPAN|Parse::Yapp}}-грамматика: http://yourcmc.ru/git/vitalif/VMXTemplate/raw/master/template.yp
=== Пример ===
* <tt><nowiki>'{', '}'</nowiki></tt> — маркеры начала и конца подстановки выражения (между скобками не может быть инструкций типа IF/ELSE и т. п.)
* Подстановки можно использовать и в директивах
* Маркеры начала и конца директивы можно заменить другими — tслиесли, например, вы привыкли к TT, можно установить <tt><nowiki>[% %]</nowiki></tt>. Главное, чтобы маркер начала не был равен маркеру конца.* Маркеры подстановок можно отключить, но тоже заменить другими без правки грамматики парсера нельзяна другие, либо отключить вовсе.
=== Выражения ===
* Синтаксис обращений к переменным JS-подобный: <tt>hash.key</tt>, <tt>array[0]</tt>, <tt>hash['key']</tt>
* Имена переменных '''регистрозависимы''', имена встроенных функций и названия директив (BEGIN, END и т. п.) — '''регистронезависимы'''
* Никаких ошибок при обращениям обращениях к необъявленным переменным не происходит
* Компилируемые функции: <tt>function(arg1, arg2, ...)</tt> или <tt>function single_arg</tt> (через пробел с одним аргументом)
* Функции, определённые через FUNCTION: <tt>fn_name('arg' => 'value', 'arg2' => 'value2', ...)</tt> или <tt>exec('fn_name', { 'arg' => 'value', 'arg2' => 'value2', ... })</tt>
=== Функции ===
Функции используются в выражениях как Синтаксис вызова функций:* <tt>ФУНКЦИЯ(АРГУМЕНТ, АРГУМЕНТ, ...)</tt> или как * <tt>ФУНКЦИЯ (<пробел) > ОДИН_АРГУМЕНТ</tt>. Вместо запятой ", " можно также использовать «=>», например <tt>HASH(КЛЮЧ => ЗНАЧЕНИЕ, КЛЮЧ => ЗНАЧЕНИЕ)</tt>. Синтаксической разницы между ", " и «=>» никакой нет.
Существующие функции перечислены ниже. Через «=» в подзаголовках указываются синонимы функций.
* Случайно не забыть что-то экранировать, экранируя это руками
Работает так: если какое-то подставляемое значение не экранировано вами явно через одну из функций вроде перечисленных выше (s/t/h/i и т. п.), то оно будет экранировано функцией, заданной в auto_escape. Получается «авто-защита» от атак типа XSS. Значения, которые надо подставить «как есть», нужно предварить вызовом функции RAW: {raw value}. Тогда значение auto_escape’ом экранировано не будет.
На заметку: для удобства функции JSON, QUOTE, SQL_QUOTE и REQUOTE считаются «безопасными», хотя таковыми, строго говоря, не являются. Однако используются они обычно внутри JS-кода, поэтому лучше их вывод не трогать.
Ограничение длины строки <tt>str</tt> максимальной длиной <tt>len</tt> — <tt>strlimit(str, len, dots = "...")</tt>. Если строка превышает заданную длину, она обрезается предпочтительно по пробелу или Tab’у, а в конец добавляется <tt>dots</tt> или по умолчанию <tt>"..."</tt>, если аргумент <tt>dots</tt> не передаётся.
 
==== PLURAL_RU ====
 
Выбор правильного окончания в русском языке в зависимости от количества: <tt>plural_ru(число, один, несколько, много)</tt>. Например (1 шаблон, 2-3-4-102 шаблонА, 5-6-15-… шаблонОВ):
 
<tt><nowiki>{num} шаблон{plural_ru(num, '', 'а', 'ов')}</nowiki></tt>
=== Массивы и хеши ===
== Различия PHP и Perl версий ==
 
==== Perl-версия — устаревшая ====
 
На момент 2013-04-20 Perl-версия — устаревшая. А вот актуальная для неё [http://www.yourcmc.ru/wiki/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%82%D0%BE%D1%80_VMX::Template&oldid=6200 старая версия документации].
==== Кэширование работает по-разному ====
В целом, общий смысл — сделать так, чтобы шаблоны было не стыдно вызывать много раз, как много раз за один запрос, так и в целом, при этом максимально использовать механизмы интерпретатора самого языка. Но механизмы для этого применяются разные. Основная причина различий следующая:
* Perl: считается, что всё прогрессивное человечество уже давно использует <tt>mod_perl</tt> или [[Платформы для запуска Perl веб-приложений|другие способы запуска веб-приложений]], при которых частых переинициализаций интерпретатора не происходит. Иными словами, ''никто больше не использует CGI''. Таким образом, мы смело легко можем сохранить живой coderef (ссылку на функцию, или кому как больше нравится — анонимную функцию, замыкание, делегат) в промежутке между двумя запросами. Так и живём — скомпилированный шаблон представляет собой просто хеш с набором анонимных функций, которые сохраняются в ''экземпляре объекта VMXTemplate'' и вызываются при обращении к шаблону или его блокам. Также существует и файловый кэш компилированного кода. '''Важное следствие:''' объект VMXTemplate между запросами нужно оставлять живым. Если его убить — кэш полностью очищается.* PHP: интерпретатор PHP всегда инициализируется заново при обработке каждого HTTP-запроса. А , а живой coderef в промежутке между двумя инициализациями интерпретатора сохранить, видимо, невозможно. В PHP также есть ещё одна проблема — в процессе выполнения невозможно добавить метод в класс без использования извращений типа [http:Однако предполагается, что всё прогрессивное человечество давно использует APC/XCache/peclZendOpCache/eAccelerator, и поэтому, когда текст шаблонов компилируется в файлы, а файлы подгружаются путём require, на самом деле они загружаются не с диска, а из памяти кэшера, причём — в уже скомпилированном виде.phpКроме того, так как скомпилированный шаблон представляет собой класс — в рамках одного запроса он загружается максимум 1 раз, последующие вызовы происходят уже очень быстро.netНу и на всякий пожарный — хотя это, возможно, уже особого выигрыша и не даёт — нескомпилированный текст шаблонов тоже кэшируется в кэше переменных APC/packageXCache/classkit classkit]eAccelerator, а хочетсяесли таковой присутствует, потому что сгенерированные из кода и не перезагружается с диска лишний раз. Если <tt>reload = false</tt>, лишними считаются все разы, кроме первого, даже если файл шаблона функции должны быть методами — они используют контекст класса Templateменялся.
Поэтому компилированный шаблон PHP-версии — это класс, производный от класса Template. Единожды за один HTTP-запрос он загружается в память, а при каждом вызове шаблона создаётся пустой объект этого класса, в него записывается ссылка на В Perl действие <tt>tpldatareload</tt> и поле немного отличается — <tt>parentreload = 0</tt>работает так же, ссылающееся на родительский объект Templateкак <tt>reload = false</tt> в PHP, и вызывается метод классано если <tt>reload > 0</tt>, соответствующий функции шаблона (&lt;!то тексты шаблонов всё-- FUNCTION ... --&gt;)таки перезагружаются с диска при изменении, но не чаще, чем раз в <tt>reload</tt> секунд.
Кроме кэширования классов В PHP также есть ещё одна проблема — в рамках запроса процессе выполнения невозможно добавить метод в PHP существует ещё две ступеникласс без использования извращений типа [http:* Текст шаблонов кэшируется в XCache или eAccelerator, если таковые присутствуют, и не перезагружается с диска лишний раз//pecl. Если <tt>reload = false<php.net/tt>package/classkit classkit], лишними считаются все разыа хочется, кроме первогопотому что сгенерированные из кода шаблона функции должны быть методами — они дёргают разные функции от $this, даже если файл шаблона менялся.* Компилированный код шаблонов кэшируется в файлах на дискеподразумевая, и не компилируется лишний разчто это объект класса VMXTemplate.
В Perl действие <tt>reload</tt> немного отличается — <tt>reload = 0</tt> работает так жеПоэтому компилированный шаблон PHP-версии — это класс, как <tt>reload = false</tt> производный от класса VMXTemplate. Как уже сказано выше, единожды за один HTTP-запрос он загружается в PHPпамять, но если а при каждом вызове шаблона создаётся пустой объект этого класса, в него записывается ссылка на <tt>reload > 0tpldata</tt>, то тексты шаблонов перезагружаются с диска при изменении, но не чаще, чем раз в и поле <tt>reloadparent</tt> секунд. В остальном всё проще — компилированный шаблон представляет собой просто хеш с набором анонимных функций, которые сохраняются в my-переменной пакета VMX::ссылающееся на родительский объект Template , и вызываются при обращении к шаблону или его блокам. Также существует и файловый кэш компилированного кодавызывается метод класса, соответствующий функции шаблона (&lt;!-- FUNCTION … --&gt;).
==== Несколько различается действие <tt>use_utf8 = true</tt> ====
* PHP: «использовать mb_str* функции для работы со строками в выражениях».
* Perl: «я передаю в шаблон все переменные с флагом UTF-8 = On, их можно смело конкатенировать с UTF-ными частями шаблона». Если кто-то не знает, в Perl строки имеют на себе флаг UTF-8 = да или нет, и при конкатенации строки без флага со строкой с флагом строка без флага будет автоматически переведена в UTF-8 из кодировки, соответствующей текущей локали. Что означает двойное UTF-8-кодирование в случае, если строка на самом деле всё-таки в UTF-8, но просто на ней не установлен флаг.
*: Для приведения всех переменных шаблона к UTF-8 можно использовать функцию <tt>utf8on()</tt> из [{{SVN|vitaphoto/solstice/lib-sway/VMX/Common.pm}} VMX<tt>VMXTemplate::Common] Utils</tt> (рекурсивный <tt>Encode::_utf8_on()</tt>).
==== Различается способ вывода ошибок при <tt>print_error = true</tt> ====
==== Различается поведение сравнений ====
* PHP: Обычные сравнения — типозависимыеТип обычных операторов сравнения определяется во время выполнения. То есть, если во время выполнения одно из сравниваемых значений — число, они сравниваются как числа, иначе — как строки.* Perl: EQ и тТип обычных операторов сравнения определяется ''во время компиляции''. пТо есть, если из контекста понятно, что одно из сравниваемых значений — число (если это константа или результат, например, функции count), сравнение будет численным, иначе — строковым.* Пустые массивы и хеши ложны в PHP и истинны в Perl. без S/N эквивалентно строковому То есть простая проверка «IF array» (Sxxприведение к булеву типу), если array пуст, в PHP вернёт false, а в Perl — true.
==== Различается поведение некоторых функций работы с массивами и хешами ====
Ответы:
* Чтобы структурировать код, осознанно используя для генерации HTML-ек язык с ограниченными возможностями. Так как возможности ограничены, сложные вычисления писать на нём автоматически не хочется, соответственно, они перемещаются в логику, разделение становится более явным.
* Чтобы структурировать выполнение — сначала логика, потом HTML. В идеале «обратной связи» из шаблонов в логику быть не должно, то есть шаблону должно передаваться ровно столько данных, сколько ему нужно, чтобы в процессе выполнения он ничего не дочитывал. Это приблизительно называется MVP (Model-View-Presenter; View имеет связь с моделью только через Presenter) и сразу же ликвидирует:
** Трудноуловимые проблемы производительности, происходящие по вине ленивых вычислений и вызовов методов модели, дочитывающих данные из БД, из view.
** Проблемы с преждевременной отправкой HTTP-заголовков, после которой внезапно обнаруживается, что, оказывается, нужно было сделать редирект.
* Второй вариант — классический MVC, шаблон — это View (представление), во View передаётся модель, и View отображает состояние модели так, как ему хочется. То есть, шаблон общается напрямую с живыми объектами модели, которые ему передают, «обратная связь» присутствует.
*: {{Warning}} Важное ИМХО! Частая проблема классического подхода — по 100 запросов для чтения одного и того же свойства при отображении 100 объектов выборки, вместо того, чтобы прочитать это свойство за 1 запрос сразу для всех 100 объектов. Чтобы такого не было — нужно, чтобы объекты помнили, частью какой коллекции они являются, и при чтении свойства читали его сразу для всех объектов «своей» коллекции. Идея основана на предположении, что если у объекта, прочитанного из БД как часть большой выборки, запрашивается какое-то свойство — велика вероятность того, что это же свойство будет запрошено и у всех остальных объектов той же самой выборки. При такой реализации — и писать удобно (не нужно заморачиваться, что передавать в шаблон, а что нет), и производительность не страдает.
Примечание: так как PHP — «язык наизнанку», сам немножко являющийся шаблонизатором, то при выполнении следующих требований можно писать и без шаблонизатора:
Это бывает весьма полезно, если нужно написать модуль к системе, которая сама написана без шаблонизатора или с каким-нибудь полу-кривым собственным, и не хочется вводить дополнительную зависимость.
[[Категория:Sway]][[Категория:РазработкаТехактивы]]
[[Категория:Perl]]
[[Категория:PHP]]

Навигация