Изменения

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

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

149 байтов добавлено, 17:35, 20 апреля 2013
м
Нет описания правки
'''VMX::Template''' -  — высокопроизводительный шаблонизатор, имеющий Perl- и PHP-версии (основная - основная — в данный момент PHP).
* Лицензия: GNU GPL версии 3 или новее
* Исходники (PHP): {{SVN|vitalif/trunk/Template/}} -  — для работы нужны файлы template.php и template.parser.php. Исходник грамматики в [{{SVN|vitalif/trunk/Template/template.lime}} template.lime]
* Исходники (Perl), несколько устаревшие: [{{SVN|vitaphoto/solstice/lib-sway/VMX/Template.pm}} Template.pm], [{{SVN|vitaphoto/solstice/lib-sway/VMX/Common.pm}} Common.pm]
* Простой файл настроек для подсветки синтаксиса шаблонов в [http://www.midnight-commander.org/ Midnight Commander]'а: [{{SVN|vitalif/trunk/scripts/tpl.syntax|markup}} tpl.syntax]
# И, наконец, на LALR(1) грамматику на основе генератора парсеров LIME
Есть PHP и Perl версии шаблонизатора, основная версия шаблонизатора - шаблонизатора — в данный момент PHP. Есть некоторые различия реализации - реализации — например, в Perl’е для кэширования кода используются coderef’ы, а в PHP предполагается, что кэшированием занимается какой-нибудь [http://xcache.lighttpd.net/ XCache] или [http://eaccelerator.net/ eAccelerator], ибо там сохранить coderef между запросами невозможно.
<span style="border: 2px #FF8000 dashed; padding: 4px">Про VMX::Template можно сказать «ох уж эти перлисты — перлисты — что ни пишут, всё Template::Toolkit получается».</span> Это к тому, что идея вообще-то схожая, но реализация гораздо проще и быстрее.
== Использование ==
== Синтаксис шаблонов ==
Шаблон — Шаблон — любой текст (типично - типично — HTML), в который ''местами'' включены директивы и/или подстановки.  === Пример:===
<pre>
</pre>
=== Маркеры:=== * <tt><nowiki>'<!--', '-->'</nowiki></tt> -  — маркеры начала и конца директивы* <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>var.method(arg1, arg2, ...)</tt>
=== Операторы:===
<tab sep="bar" class="wikitable">
a .. b | Конкатенация
a &#x7C;&#x7C; || b, a OR b | Логическое ИЛИa XOR b | XOR - XOR — логическое исключающее ИЛИ
a && b, a AND b | Логическое И
a & b | Побитовое И
!a, NOT a | Логическое НЕ
(exp) | Выражение в скобках
{ 'a' => 'b', ... } | Создание хешрефа (Perl), ассоциативного массива (PHP)
</tab>
=== Директивы:===
<tab sep="bar" class="wikitable">
<tt>&lt;!--# Комментарий --&gt;</tt> | Комментарий
<tt>&lt;!-- FOR item = array --&gt;</tt><br />...код...…код…<br /><tt>&lt;!-- END --&gt;</tt> | Цикл. Вместо FOR можно использовать слово FOREACH. Внутри цикла можно обратиться к счётчику через <tt>{item_index}</tt><tt>&lt;!-- IF выражение --&gt;</tt><br />...код... …код… | Если (выражение)<tt>&lt;!-- ELSEIF выражение --&gt;</tt><br />...код... …код… | Иначе если (выражение)<tt>&lt;!-- ELSE --&gt;</tt><br />...код... …код… | Иначе
<tt>&lt;!-- END --&gt;</tt> | Конец если / цикла / присваивания
<tt>&lt;!-- SET var = выражение --&gt;</tt> | Присваивание переменной var результата выполнения выражения
<tt>&lt;!-- SET var --&gt;</tt><br />...код...…код…<br /><tt>&lt;!-- END --&gt;</tt> | Присваивание переменной var результата выполнения кода
<tt>&lt;!-- FUNCTION name (arg1, arg2) = выражение --&gt;</tt> | Определение функции шаблона как результата выполнения выражения
<tt>&lt;!-- FUNCTION name (arg1, arg2) --&gt;</tt><br />...код...…код…<br /><tt>&lt;!-- END --&gt;</tt> | Определение функции / "блока" «блока» шаблона. Вместо FUNCTION можно использовать также слова BLOCK или MACRO
</tab>
Расширяемость в области функций:
;Run-time функции: В качестве функции можно использовать метод переданного в хеше данных объекта. В «функцию» можно вынести и блок кода из шаблона — шаблона — см. [[#Блоки]]. Оно хорошо кэшируется.;Compile-time функции: При создании объекта шаблона можно передать параметр <tt>compiletime_functions</tt>, равный хешу, в котором ключи — ключи — имена дополнительных функций, а значения — значения — любые coderef’ы (Perl) или callable (PHP). Эти функции вызываются в контексте объекта шаблона с параметрами, равными '''коду для вычисления соответствующего аргумента''', и должны возвращать '''код для вычисления результата'''. То есть, они выполняются на этапе компиляции.
{{note}} Первое, что обычно нужно — нужно — это S(), H(), T(), Q(), I(), то есть «фильтры» для различных преобразований строки:
* S()  — это htmlspecialchars(), экранирует HTML/XML-спецсимволы в строках.* H()  — удаляет все HTML-теги, кроме «безопасных».* T()  — удаляет все HTML-теги.* Q()  — это addslashes(), экранирует строки для использования, например, в JS.* I()  — преобразует значение к целому числу.
=== Числа, логические операции ===
==== YESNO ====
YESNO($1, $2, $3) -  — тернарный оператор ($1 ? $2 : $3).
=== Строки ===
==== Q=QUOTE=ADDSLASHES, SQ=SQL_QUOTE, REQUOTE=RE_QUOTE=PREG_QUOTE ====
* Экранирование символов " ' \ и перевода строки бэкслэшем — бэкслэшем — quote(строка).* Экранирование символа " удвоением — удвоением — sql_quote(строка). (актуально также для [[rupedia:CSV|CSV]])* Экранирование символов, являющихся специальными в регулярных выражениях — выражениях — re_quote(строка). (см. [http://perldoc.perl.org/perlre.html perldoc perlre]).
==== URI_QUOTE=URIQUOTE=URLENCODE ====
==== REPLACE, STR_REPLACE ====
* Замена Perl- (соответственно PCRE- в PHP-версии) регулярного выражения в строке — строке — replace(RegExp, замена, строка).* Замена подстроки в строке — строке — str_replace(искомое, замена, строка).
==== STRLEN ====
==== SUBSTR=SUBSTRING ====
Стандартная (для всех, кроме жавистов) функция подстроки — подстроки — substr(строка, начало, длина), или substr(строка, начало). Причём начало и длина могут быть отрицательными, тогда они считаются относительно длины строки.
==== TRIM ====
==== SPLIT ====
Разделение строки по регулярному выражению и лимиту — лимиту — split(RegExp, аргумент, лимит). Лимит необязателен. (см. [http://perldoc.perl.org/functions/split.html perldoc -f split])
==== S=HTML=HTMLSPECIALCHARS, T=STRIP, H=STRIP_UNSAFE, NL2BR ====
==== CONCAT, JOIN=IMPLODE ====
* Конкатенация всех своих аргументов — аргументов — concat(аргументы). Конкатенирует также все элементы всех переданных массивов.* Конкатенация элементов массива через разделитель — разделитель — join(строка, аргументы). Конкатенирует также все элементы всех переданных массивов.
==== SUBST, SPRINTF, STRFTIME ====
Subst - Subst — подстановка на места подстрок вида $ЧИСЛО соответствующих параметров функции или элементов переданного массива — массива — subst(строка, $1, $2, …).
Sprintf — Sprintf — он и в Африке [http://perldoc.perl.org/functions/sprintf.html sprintf].
Форматирование даты и/или времени с помощью функции [http://www.manpagez.com/man/3/strftime/ strftime]  — strftime(формат, дата [, часть_даты]). Формат strftime’овский (например, «%d %b %Y»). Дата может передаваться как один или два аргумента, если два — два — они конкатенируются через пробел. Далее дата разбирается способом, похожим на wfTimestamp() в MediaWiki. Принимается следующее:
* UNIX время.
* Времена типа MySQL DATE, MySQL DATETIME, EXIF, ISO 8601, MediaWiki, и любые другие, подпадающие под следующий формат: 1 группа из 4 или более цифр (год) и 2 (месяц, день) или 5 (месяц, день, часы, минуты, секунды) групп по 2 цифры, разделённые любыми нецифровыми символами и в конце — конце — опционально временная зона — зона — 2 цифры, предварённые пробелом, плюсом или минусом. Короче говоря, <pre>^\D*(\d{4,})\D*(\d{2})\D*(\d{2})\D*(?:(\d{2})\D*(\d{2})\D*(\d{2})\D*([\+\- ]\d{2}\D*)?)?$</pre>
* Оракловский формат даты-времени: <nowiki>ДД-Мес-ГГ[ГГ] ЧЧ.ММ.СС</nowiki>.
* RFC 822.
==== STRLIMIT=TRUNCATE ====
Ограничение длины строки <tt>str</tt> максимальной длиной <tt>len</tt>  — <tt>strlimit(str, len, dots = "...")</tt>. Если строка превышает заданную длину, она обрезается предпочтительно по пробелу или Tab’у, а в конец добавляется <tt>dots</tt> или по умолчанию <tt>"..."</tt>, если аргумент <tt>dots</tt> не передаётся.
=== Массивы и хеши ===
Создание хэша из всех аргументов.
Соответственно в хеше аргументы идут парами КЛЮЧ => ЗНАЧЕНИЕ, КЛЮЧ => ЗНАЧЕНИЕ и ти т. п п.
==== KEYS, HASH_KEYS, ARRAY_KEYS ====
Массив ключей хэша. Понятное дело, в PHP их порядок сохраняется, а в Perl — Perl — нет.
==== SORT ====
* Создание массива.
* Диапазон от A до B — B — range(A, B).
==== IS_ARRAY ====
==== COUNT, SUBARRAY=ARRAY_SLICE, SUBARRAY_DIVMOD ====
* Количество элементов массива, или 0, если аргумент — аргумент — не массив — массив — count(аргумент).
* Аналог функции [http://php.net/manual/en/function.array-slice.php array_slice] из PHP.
* Выбор из массива каждого div’того элемента, начиная с номера mod или нуля по умолчанию — умолчанию — subarray_divmod(массив, div, mod).
==== GET ====
<tt>GET(что)</tt>  — получение значения переменной верхнего уровня.
==== SET ====
<tt>SET(куда, что)</tt>  — присваивание «куда» значения «что». Уравнения, понятное дело, не решает, то есть, как и обычно, присваивать можно только lvalue :)
==== ARRAY_MERGE ====
Слить массивы в один. Под Perl — Perl — только массивы (не хеши), под PHP — PHP — любые массивы.
==== SHIFT, POP, UNSHIFT, PUSH ====
* Вынуть элемент из начала массива — массива — <tt>shift(array)</tt>* Вынуть из конца — конца — <tt>pop(array)</tt>* Добавить в начало — начало — <tt>unshift(array, value)</tt>* Добавить в конец — конец — <tt>push(array, value)</tt>.
=== Включения ===
==== PARSE_INLINE=INCLUDE_INLINE=PROCESS_INLINE ====
Включение кода не из файла, а просто из строки — строки — <tt>parse_inline('код шаблона'[, аргументы])</tt>.
==== EXEC ====
Включение блока из текущего шаблона — шаблона — <tt>exec('имя блока'[, аргументы])</tt>.
==== EXEC_FROM ====
Включение блока из другого шаблона — шаблона — <tt>exec_from('имя файла', 'имя блока'[, аргументы])</tt>.
==== EXEC_FROM_INLINE ====
Ещё больше не рекомендуется, но можно вызывать и функции из кода из строки — строки — <tt>exec_from_inline('код шаблона', 'имя блока'[, аргументы])</tt>.
=== Прочее ===
==== DUMP=VAR_DUMP ====
Вывод всех данных из структуры — структуры — Dumper в Perl’е и var_dump в PHP.
==== JSON ====
==== CALL ====
Вызов метода объекта по «динамическому» имени — имени — <tt>call(varref, method_name, arg1, arg2, arg3, ...)</tt>.
==== MAP ====
Применение функции, имя которой передано как первый аргумент, ко всем переданным аргументам и элементам всех переданных массивов — массивов — map(«имя_функции», аргументы).
== Изменения относительно старых версий ==
* Ликвидированы assign_vars(), assign_block_vars(), tr_assign_vars() -  — теперь, как и обычно, передаётся просто хеш с данными $vars
* Синтаксис типа {a->key} ликвидирован
* SET теперь заканчивается обычным END, а не ENDSET
* Добавлен встроенный фильтр для ликвидации пробелов из начал/концов каждой строки шаблона
=== Различия PHP и Perl версий ===
==== Кэширование работает по-разному ====
В целом, общий смысл — смысл — сделать так, чтобы шаблоны было не стыдно вызывать много раз, как много раз за один запрос, так и в целом, при этом максимально использовать механизмы интерпретатора самого языка. Но механизмы для этого применяются разные. Основная причина различий следующая:
* Perl: считается, что всё прогрессивное человечество уже давно использует <tt>mod_perl</tt> или [[Платформы для запуска Perl веб-приложений|другие способы запуска веб-приложений]], при которых частых переинициализаций интерпретатора не происходит. Иными словами, ''никто больше не использует CGI''. Таким образом, мы смело можем сохранить живой coderef (ссылку на функцию, или кому как больше нравится — нравится — анонимную функцию, замыкание, делегат) в промежутке между двумя запросами.
* PHP: интерпретатор PHP инициализируется заново при обработке каждого HTTP-запроса. А живой coderef в промежутке между двумя инициализациями интерпретатора сохранить, видимо, невозможно.
В PHP также есть ещё одна проблема — проблема — в процессе выполнения невозможно добавить метод в класс без использования извращений типа [http://pecl.php.net/package/classkit classkit], а хочется, потому что сгенерированные из кода шаблона функции должны быть методами — методами — они используют контекст класса Template.
Поэтому компилированный шаблон PHP-версии — версии — это класс, производный от класса Template. Единожды за один HTTP-запрос он загружается в память, а при каждом вызове шаблона создаётся пустой объект этого класса, в него записывается ссылка на <tt>tpldata</tt> и поле <tt>parent</tt>, ссылающееся на родительский объект Template, и вызывается метод класса, соответствующий блоку шаблона (см. [[#Блоки]]).
Кроме кэширования классов в рамках запроса в PHP существует ещё две ступени:
* Компилированный код шаблонов кэшируется в файлах на диске, и не компилируется лишний раз.
В Perl действие <tt>reload</tt> немного отличается — отличается — <tt>reload = 0</tt> работает так же, как <tt>reload = false</tt> в PHP, но если <tt>reload > 0</tt>, то тексты шаблонов перезагружаются с диска при изменении, но не чаще, чем раз в <tt>reload</tt> секунд. В остальном всё проще — проще — компилированный шаблон представляет собой просто хеш с набором анонимных функций, которые сохраняются в my-переменной пакета VMX::Template и вызываются при обращении к шаблону или его блокам. Также существует и файловый кэш компилированного кода.
==== Несколько различается действие <tt>use_utf8 = true</tt> ====
* Общий смысл — смысл — «мои шаблоны и страницы в кодировке UTF-8».
* PHP: «использовать mb_str* функции для работы со строками в выражениях».
* Perl: «я передаю в шаблон все переменные с флагом UTF-8 = On, их можно смело конкатенировать с UTF-ными частями шаблона». Если кто-то не знает, в Perl строки имеют на себе флаг UTF-8 = да или нет, и при конкатенации строки без флага со строкой с флагом строка без флага будет автоматически переведена в UTF-8 из кодировки, соответствующей текущей локали. Что означает двойное UTF-8-кодирование в случае, если строка на самом деле всё-таки в UTF-8, но просто на ней не установлен флаг.
==== Различается способ вывода ошибок при <tt>print_error = true</tt> ====
* Общий смысл — смысл — при <tt>print_error = true</tt> ошибки и предупреждения должны попасть на экран.
* PHP: они просто выводятся print()'ами.
* Perl: здесь так нельзя, потому что HTTP-заголовки сами могут и не отправиться, поэтому текст ошибок прицепляется к выводу шаблонизатора (возвращается вместе с результатом <tt>parse()</tt>).
==== Различается поведение сравнений ====
* PHP: Обычные сравнения - сравнения — типозависимые.* Perl: EQ и ти т. п п. без S/N эквивалентно строковому (Sxx).
==== Различается поведение некоторых функций работы с массивами и хешами ====
* KEYS — KEYS — в PHP порядок ключей массива/хеша сохраняется, а в Perl — Perl — нет и принимаются только хеши. Обусловлено реализацией хешей в этих языках.* PAIRS — PAIRS — в PHP порядок ключей сохраняется, в Perl-версии ключи будут отсортированы по имени.* RANGE — RANGE — в Perl-версии принимает буквенные аргументы (A..Z = весь алфавит).* IS_ARRAY — IS_ARRAY — в PHP-версии не проверяется, а не является ли он при этом хэшем, ибо трудоёмко (надо проверить, численные ли все ключи).
* AGET и HGET в PHP идентичны GET.
* ARRAY_MERGE: под Perl — Perl — только массивы (не хеши), под PHP — PHP — любые массивы.* DUMP — DUMP — это Dumper в Perl’е и var_dump в PHP.
==== Строка исходного файла =====
В PHP-версии в шаблоны не включаются C-подобные «прагмы» #line, а в текст ошибок не включается имя файла шаблона и строка. Ибо решил — решил — раз уж #line не поддерживается, нечего на строки заморачиваться.
== А кстати, зачем вообще нужен шаблонизатор? ==
Ответы:
* Чтобы структурировать код, осознанно используя для генерации HTML-ек язык с ограниченными возможностями. Так как возможности ограничены, сложные вычисления писать на нём автоматически не хочется, соответственно, они перемещаются в логику, разделение становится более явным.
* Чтобы структурировать выполнение — выполнение — сначала логика, потом HTML. В идеале «обратной связи» из шаблонов в логику быть не должно, то есть шаблону должно передаваться ровно столько данных, сколько ему нужно, чтобы в процессе выполнения он ничего не дочитывал. Это сразу же ликвидирует:
** Трудноуловимые проблемы производительности, происходящие по вине ленивых вычислений и вызовов методов модели, дочитывающих данные из БД, из view.
** Проблемы с преждевременной отправкой HTTP-заголовков, после которой внезапно обнаруживается, что, оказывается, нужно было сделать редирект.
Примечание: так как PHP — PHP — «язык наизнанку», сам немножко являющийся шаблонизатором, то при выполнении следующих требований можно писать и без шаблонизатора:
* ''Руками'' писать в «шаблонном стиле»:
*# Не смешивать сложные конструкции с HTML.

Навигация