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

Версия от 18:22, 29 июля 2010; VitaliyFilippov (обсуждение | вклад)

Данный модуль представляет собой новую версию VMX::Template, построенную на некоторых новых идеях, ликвидировавшую безобразие и legacy-код, накопленный в старой версии, однако сохранившую высокую производительность и простоту.

Есть PHP-версия и Perl-версия шаблонизатора. Реализация, естественно, несколько отличается по причине различий языков — например, в Perl’е для кэширования кода используются coderef’ы, а в PHP предполагается, что кэшированием занимается какой-нибудь XCache или eAccelerator, ибо там сохранить coderef между запросами, по-видимому, невозможно.

Развивается то одна, то другая, в зависимости от проекта, над которым я работаю в моменте.

Также есть простенький (и кривоватенький) файл настроек синтаксиса шаблонов для Midnight Commander'а: tpl.syntax.

Про VMX::Template можно сказать «ох уж эти перлисты — что ни пишут, всё Template::Toolkit получается». Это к тому, что идея вообще-то схожая, но реализация гораздо проще и быстрее.

Do you want to try some new features? By joining the beta, you will get access to experimental features, at the risk of encountering bugs and issues.

Ок Нет, спасибо

Идеи

Уйти от assign_vars(), assign_block_vars(). Передавать, как и в обычных движках, просто хеш с данными $vars. Как, например, в Template::Toolkit. При этом сохранить данные методы для совместимости.

Почистить синтаксис: ликвидировать «преобразования», «вложенный путь по переменной» (->key->index->key->и т. п.), специальный синтаксис для окончания SET, неочевидное обращение к счётчику block.#, tr_assign_* и т. п.

Переписать с нуля компилятор.

Добавить в употребление функции, но только самые необходимые.

Добавить обработку ошибок и диагностические сообщения.

Реализация

Маркеры начала и конца кода <!-- --> и подстановки { } могут быть заменены любыми другими. Если, например, вы привыкли к TT, можно установить [% %]. Маркеры подстановки можно вообще убрать, ибо подстановка тоже является кодом.

Путь к переменной теперь может включать в себя числа. Это будут обращения к элементам массивов, в то время как всё остальное — обращения к элементам хешей.

Блоки

Вне блока {block} будет иметь значение ARRAY(0x…), то есть массив всех итераций блока block, а {block.0} будет иметь значение HASH(0x…), то есть первую итерацию блока block.

<!-- BEGIN block -->

Теперь, внутри блока {block} теперь будет иметь значение HASH(0x…), то есть уже значение текущей итерации блока block, а {block.#} будет иметь значением номер текущей итерации блока, отсчитываемый с 0, а не с 1, как в старой версии.

<!-- END block -->

На <!-- END другоеимя --> после <!-- BEGIN block --> шаблонизатор выдаст ошибку, «ибо нефиг» (c). Если block в хеше данных — не массив, а хеш — это значит, что итерация у блока только одна, и тогда <!-- BEGIN block --> работает как for($expression) {} в Perl.

BEGIN ... END — это циклы в «старом стиле». А можно использовать и TT-подобный:

<!-- FOR var = block -->
...
<!-- END -->

Причём, var может быть само block'ом. Это, по сути, и есть то, что делает BEGIN: эквивалентно . Предыдущее значение переменной цикла после выхода из цикла всегда восстанавливается.

К номеру итерации можно обратиться через {var#}.

Функции

Операторов нет, фильтров нет, есть функции. Пример:

<!-- IF OR(function(block.key1),AND(block.key2,block.key3)) -->

Синтаксис вызова функции нескольких аргументов:

<!-- function(block.key, 0, "abc") -->

Подстановка:

{function(block.key, 0, "abc")}

Синтаксис вызова функции одного аргумента:

<!-- function(block.key) -->
<!-- function block.key -->
{block.key/s}
{s block.key}

IF

Условный вывод:

<!-- IF function(block.key) --><!-- ELSEIF ... --><!-- END -->
<!-- IF NOT block.key -->...<!-- END -->                      

ELSIF эквивалентно ELSE IF и ELSEIF.

SET

Запись значения переменной:

<!-- SET block.key -->...<!-- END --></nowiki>
<!-- SET block.key = выражение -->

Включения

Включение другого шаблона также осталось:

<!-- INCLUDE another-file.tpl -->

По «динамическому» имени шаблона включение производится функцией INCLUDE (она же PARSE).

Функции

Note.svg Первое, что обычно нужно — это S(), H(), T(), Q(), I(), то есть «фильтры» для различных преобразований строки:

  • S() — это htmlspecialchars(), экранирует HTML/XML-спецсимволы в строках.
  • H() — удаляет все HTML-теги, кроме «безопасных».
  • T() — удаляет все HTML-теги.
  • Q() — это addslashes(), экранирует символы для использования, например, в JS.
  • I() — преобразует значение к целому числу.

Расширяемость в области функций:

Run-time функции
В качестве функции можно использовать переданный в хеше данных coderef (замыкание, ссылку на функцию, или любое «is_callable» в случае PHP). Если же хочется вынести в «функцию» блок кода из шаблона — проще создать отдельный шаблон и вызывать его, предварительно делая <!-- SET --> именованных аргументов. Это действительно лучше, так как позволяет оптимально работать кэшированию.
Compile-time функции
При создании объекта шаблона можно передать параметр compiletime_functions, равный хешу, в котором ключи — имена дополнительных функций, а значения — любые coderef’ы (Perl) или callable (PHP). Эти функции вызываются в контексте объекта шаблона с параметрами, равными коду для вычисления соответствующего аргумента, и должны возвращать код для вычисления результата. То есть, они выполняются на этапе компиляции. (пока только PHP-версия)

OR, AND, NOT

Логические ИЛИ, И, НЕ, действующие аналогично Perl операторам ||, &&, !.

EVEN, ODD

Истина в случае, если аргумент чётный или нечётный соответственно.

INT=I, ADD, MUL, DIV, MOD, LOG

Преобразование к целому числу, арифметические операции и логарифм.

EQ, NE, SEQ, SNE, GT, LT, GE, LE, SGT, SLT, SGE, SLE

Действуют аналогично Perl операторам == eq > < >= <= gt lt ge le.

В PHP-версии на данный момент такого много-безобразия нет, есть просто EQ NE GT LT GE LE. Хотя это и имеет свои минусы - если хотя бы один аргумент принимается PHP как численный, сравнение становится численным.

COUNT, SUBARRAY=ARRAY_SLICE, SUBARRAY_DIVMOD

Количество элементов массива, или 0, если аргумент — не массив — count(аргумент).

Аналог функции array_slice из PHP.

Выбор из массива каждого div’того элемента, начиная с номера mod или нуля по умолчанию — subarray_divmod(массив, div, mod).

ARRAY, HASH

Создание массива или хэша из всех атрибутов.

Соответственно в хеше атрибуты идут парами КЛЮЧ, ЗНАЧЕНИЕ, КЛЮЧ, ЗНАЧЕНИЕ и т.п. (специального синтаксиса "=>" нет).

SORT

Сортировка массива по значениям.

ARRAY_MERGE

Слить массивы в один. Под Perl — только массивы (не хеши), под PHP — любые массивы.

GET, AGET, HGET

Получение элемента массива/хэша по «динамическому» ключу. По-моему, это лучше, чем зюки-хрюки Template Toolkit’а: hash.${hash2.$key} и т. п.

GET(откуда, что) автоматически решает, «откуда» — это массив или хеш, AGET служит только для массивов, а HGET только для хешей. В PHP-версии все три идентичны.

GET(что) — получение значения переменной верхнего уровня.

MAP

Применение функции, имя которой передано как первый аргумент, ко всем переданным аргументам и элементам всех переданных массивов — map(«имя_функции», аргументы).

LC=LOWER=LOWERCASE, UC=UPPER=UPPERCASE

Нижний и верхний регистр.

STRLIMIT

Ограничение длины строки s максимальной длиной l — strlimit(s, l). Если строка превышает заданную длину, она обрезается предпочтительно по пробелу или Tab’у, а в конец добавляется «…» (троеточие).

S=HTML=HTMLSPECIALCHARS, T=STRIP, H=STRIP_UNSAFE, NL2BR

Преобразование символов < > & " ' в HTML-сущности.

Удаление всех HTML/XML тегов.

Удаление только запрещённых тегов.

Преобразование переводов строк (\n) в HTML-тег <br />.

URI_QUOTE=URIQUOTE=URLENCODE

URL-кодирование строки (URI::Escape в Perl и urlencode() в PHP).

CONCAT, JOIN=IMPLODE

Конкатенация всех своих аргументов — concat(аргументы). Конкатенирует также все элементы всех переданных массивов.

Конкатенация элементов массива через разделитель — join(строка, аргументы). Конкатенирует также все элементы всех переданных массивов.

SUBSTR=SUBSTRING, STRLEN

Стандартная (для всех кроме жавистов стандартная) функция подстроки — substr(строка, начало, длина), или substr(строка, начало). Причём начало и длина могут быть отрицательными, тогда они считаются относительно длины строки.

Ну и функция «длина строки».

Q=QUOTE=ADDSLASHES, REQUOTE=RE_QUOTE=PREG_QUOTE

Экранирование символов " ' \ и перевода строки бэкслэшем — quote(строка).

Экранирование символов, являющихся специальными в регулярных выражениях — re_quote(строка). (см. perldoc perlre).

REPLACE, SPLIT

Замена Perl- (соответственно PCRE- в PHP-версии) регулярного выражения в строке — replace(RegExp, замена, строка).

Разделение строки по регулярному выражению и лимиту — split(RegExp, аргумент, лимит). Лимит необязателен. (см. perldoc -f split)

DUMP, JSON

Вывод всех данных из структуры — Dumper в Perl’е и var_dump в PHP.

Форматирование структуры данных в формат JSON. (пока только PHP)

INCLUDE=PARSE

Включение файла с именем, переданным аргументом.

SUBST, SPRINTF, STRFTIME

Подстановка на места подстрок вида $ЧИСЛО соответствующих параметров функции или элементов переданного массива — subst(строка, $1, $2, …).

Sprintf — он и в Африке sprintf.

Форматирование даты и/или времени с помощью функции strftime — strftime(формат, дата [, часть_даты]). Формат strftime’овский (например, «%d %b %Y»). Дата может передаваться как один или два аргумента, если два — они конкатенируются через пробел. Далее дата разбирается способом, похожим на wfTimestamp() в MediaWiki. Принимается следующее:

  • UNIX время.
  • Времена типа MySQL DATE, MySQL DATETIME, EXIF, ISO 8601, MediaWiki, и любые другие, подпадающие под следующий формат: 1 группа из 4 или более цифр (год) и 2 (месяц, день) или 5 (месяц, день, часы, минуты, секунды) групп по 2 цифры, разделённые любыми нецифровыми символами и в конце — опционально временная зона — 2 цифры, предварённые пробелом, плюсом или минусом. Короче говоря,
    ^\D*(\d{4,})\D*(\d{2})\D*(\d{2})\D*(?:(\d{2})\D*(\d{2})\D*(\d{2})\D*([\+\- ]\d{2}\D*)?)?$
  • Оракловский формат даты-времени: ДД-Мес-ГГ[ГГ] ЧЧ.ММ.СС.
  • RFC 822.