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

Версия от 12:42, 21 апреля 2013; VitaliyFilippov (обсуждение | вклад) (Операторы)

VMX::Template — простой и высокопроизводительный шаблонизатор, имеющий Perl- и PHP-версии (основная — в данный момент PHP).

  • Лицензия: GNU GPL версии 3 или новее
  • Исходники (PHP): http://svn.yourcmc.ru/viewvc.py/vitalif/trunk/Template/ — для работы нужны файлы template.php и template.parser.php. Исходник грамматики в template.lime
  • Исходники (Perl), несколько устаревшие: Template.pm, Common.pm
  • Простые настройки для подсветки синтаксиса шаблонов в Midnight Commander'а: tpl.syntax. Чтобы подсветка нормально выглядела, к tpl.syntax в начало надо дописать html.syntax из стандартного комплекта поставки mc.

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.

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

Содержание

Что это за шаблонизатор?

VMX::Template изначально реализован по мотивам примитивного шаблонизатора, взятого из кода форума phpBB 2 (историческое описание старой версии), и с тех пор (с 2006 года) переписанного 4 раза:

  1. На регулярные выражения →
  2. На поиск подстроки →
  3. На метод рекурсивного спуска →
  4. И, наконец, на LALR(1) грамматику на основе генератора парсеров LIME

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

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

Это к тому, что и идея, и синтаксис шаблонов вообще-то схожи, но сама реализация гораздо проще и быстрее.

Использование

Здесь можно прочитать об: использовании в PHP, использовании в Perl, различиях реализаций.

PHP

require_once 'template.php';
 
# Конструктор
$template = new VMXTemplate(array(
    'root'          => '.',        # директория с шаблонами
    'cache_dir'     => './cache',  # директория для кэширования компилированного кода шаблонов
    'print_error'   => true,       # если true, ошибки компиляции выводятся на STDOUT
    'raise_error'   => false,      # если true, при ошибке компиляции вызывается die()
    'reload'        => true,       # если false, шаблоны будут считываться с диска только 1 раз, и вызовов stat() происходить не будет
    'use_utf8'      => true,       # если true, использовать кодировку UTF-8 для строковых операций
    'begin_code'    => '<!--',     # маркер начала директивы кода
    'end_code'      => '-->',      # маркер конца директивы кода
    'eat_code_line' => true,       # (похоже на TT CHOMP) съедать "лишний" перевод строки, если в строке только директива?
    'begin_subst'   => '{',        # маркер начала подстановки выражения (либо {, либо false)
    'end_subst'     => '}',        # маркер конца подстановки выражения (либо }, либо false)
    'compiletime_functions' =>     # дополнительные компилируемые функции
        array('func' => callback), # хеш вида имя функции (в шаблонах) => callback($template, array $args),
                                   # которому передаются скомпилированные выражения всех аргументов
    'strip_space'   => true,       # встроенный фильтр: срезание пробелов из начал и концов строк
    'filters'       =>             # фильтры - выполняются над выводом всех шаблонов
        array(callback1, ...),
));
 
# Присвоение одной переменной:
$template->vars("ключ", "значение");
 
# Присвоение кучи переменных:
$template->vars(array("ключ" => "значение", ...));
 
# Выполнение шаблона и получение результата:
# (возможно с передачей данных в шаблон)
$page = $template->parse('имя_файла.tpl' [, array("ключ" => "значение", ...)]);
 
# Выполнение именованного блока из файла:
$page = $template->exec_from('имя_файла.tpl', 'имя_блока' [, array("ключ" => "значение", ...)]);
 
# Выполнение кода из строки:
$page = $template->parse_inline('код' [, array("ключ" => "значение", ...)]);
 
# Выполнение именованного блока из кода (не рекомендуется, но возможно):
$page = $template->exec_from_inline('код', 'имя_блока' [, array("ключ" => "значение", ...)]);
 
# Очистка сохранённых данных для генерации ещё одной страницы:
$template->clear;

Perl

Warning icon.svg Версия устарела! См. старую версию документации.

use VMX::Template;
 
# Конструктор
$template = new VMX::Template(
    'root'          => '.',        # директория с шаблонами
    'cache_dir'     => undef,      # директория для кэширования компилированного кода шаблонов
    'reload'        => 2,          # если 0, то шаблоны не будут перечитываться с диска, и вызовов stat() происходить не будет
                                   # если >0, то шаблоны будут перечитываться с диска не чаще чем раз в reload секунд
    'print_error'   => 1,          # если TRUE, ошибки компиляции попадают в вывод шаблона
    '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'     => '}',        # маркер конца подстановки выражения
    'compiletime_functions' =>     # дополнительные компилируемые функции
        { 'func' => sub {} },      # хеш вида имя функции (в шаблонах) => coderef,
                                   # которому передаются скомпилированные выражения всех аргументов и первым - сам $template
    # немного legacy, устаревшее:
    'wrapper'       => undef,      # если coderef, через это будет пропущен вывод всех шаблонов ("глобальный фильтр")
    'strict_end'    => 0,          # требовать <!-- END имя_блока --> после <!-- BEGIN имя_блока -->
);
 
# Присвоение переменных:
$template->vars("ключ" => "значение", "ключ" => "значение", ...);
 
# Выполнения полностью аналогичны PHP:
$page = $template->parse('имя_файла.tpl' [, { "ключ" => "значение", ... }]);
$page = $template->exec_from('имя_файла.tpl', 'имя_блока' [, { "ключ" => "значение", ... }]);
$page = $template->parse_inline('код' [, { "ключ" => "значение", ... }]);
$page = $template->exec_from_inline('код', 'имя_блока' [, { "ключ" => "значение", ... }]);
 
# Очистка сохранённых данных для генерации ещё одной страницы:
$template->clear;

Синтаксис шаблонов

Шаблон — любой текст (типично — HTML), в который местами включены директивы и/или подстановки.

Если вы знаете о порождающих грамматиках, то вот контекстно-свободная грамматика для LALR(1) алгоритма разбора:

Пример

<!-- SET title -->
    Статистика
<!-- END -->
<!-- SET headscripts -->
    <script language="javascript" type="text/javascript" src="{DOMAIN}/tpldata/jquery.min.js"></script>
<!-- END -->
<!-- INCLUDE "admin_header.tpl" -->
<!-- IF NOT srcid -->
    <p>Добро пожаловать в простую OLAPообразную статистику. Выберите источник данных:</p>
    <form action="?" method="GET">
        <select style="width:100px" name="datasource">
            <!-- FOR s = sources -->
            <option value="{s s.id}">{s s.name}</option>
            <!-- END -->
        </select>
        <input type="submit" value=" Продолжить " />
    </form>
<!-- ELSEIF srcid == "test" || sources[srcid].mode == 'test' -->
    <p>Тестовый режим.</p>
<!-- END -->
<!-- INCLUDE "admin_footer.tpl" -->

Маркеры

  • '<!--', '-->' — маркеры начала и конца директивы
  • '{', '}' — маркеры начала и конца подстановки выражения (между скобками не может быть инструкций типа IF/ELSE и т. п.)
  • Подстановки можно использовать и в директивах
  • Маркеры начала и конца директивы можно заменить другими — tсли, например, вы привыкли к TT, можно установить [% %].
  • Маркеры подстановок можно отключить, но заменить другими без правки грамматики парсера нельзя.

Выражения

  • Выражения состоят из переменных, операторов и вызовов функций и методов объектов
  • Синтаксис обращений к переменным JS-подобный: hash.key, array[0], hash['key']
  • Имена переменных регистрозависимы, имена встроенных функций и названия директив (BEGIN, END и т. п.) — регистронезависимы
  • Никаких ошибок при обращениям к необъявленным переменным не происходит
  • Компилируемые функции: function(arg1, arg2, ...) или function single_arg (через пробел с одним аргументом)
  • Функции, определённые через FUNCTION: fn_name('arg' => 'value', 'arg2' => 'value2', ...) или exec('fn_name', { 'arg' => 'value', 'arg2' => 'value2', ... })
  • Включение других шаблонов: INCLUDE 'template.tpl', INCLUDE('template.tpl', { 'arg' => 'value', ... })
  • Выполнение функции из другого шаблона: exec_from('template.tpl', 'fn_name', { 'arg' => 'value', 'arg2' => 'value2', ... })
  • Методы объектов: var.method(arg1, arg2, ...)

Операторы

a .. b Конкатенация
a || b, a OR b Логическое ИЛИ. JS- или Perl-подобное — возвращает первое истинное значение.
a XOR b XOR — логическое исключающее ИЛИ
a && b, a AND b Логическое И
a & b Побитовое И
a == b, a != b, a < b, a > b, a <= b, a >= b Операторы сравнения
a+b, a-b, a*b, a/b, a%b Сложение, вычитание, умножение, деление, взятие остатка от деления
!a, NOT a Логическое НЕ
(exp) Выражение в скобках
{ 'a' => 'b', … } Создание хешрефа (Perl), ассоциативного массива (PHP)

Директивы

<!--# Комментарий --> Комментарий
<!-- FOR item = array -->
…код…
<!-- END -->
Цикл. Вместо FOR можно использовать слово FOREACH. Внутри цикла можно обратиться к счётчику через {item_index}
<!-- IF выражение -->
…код…
Если (выражение)
<!-- ELSEIF выражение -->
…код…
Иначе если (выражение)
<!-- ELSE -->
…код…
Иначе
<!-- END --> Конец если / цикла / присваивания
<!-- SET var = выражение --> Присваивание переменной var результата выполнения выражения
<!-- SET var -->
…код…
<!-- END -->
Присваивание переменной var результата выполнения кода
<!-- FUNCTION name (arg1, arg2) = выражение --> Определение функции шаблона как результата выполнения выражения
<!-- FUNCTION name (arg1, arg2) -->
…код…
<!-- END -->
Определение функции / «блока» шаблона. Вместо FUNCTION можно использовать также слова BLOCK или MACRO

Функции

Функции используются в выражениях как ФУНКЦИЯ(АРГУМЕНТ, АРГУМЕНТ, ...) или как ФУНКЦИЯ (пробел) ОДИН_АРГУМЕНТ. Вместо запятой ", " можно также использовать «=>», например HASH(КЛЮЧ => ЗНАЧЕНИЕ, КЛЮЧ => ЗНАЧЕНИЕ). Синтаксической разницы между ", " и «=>» никакой нет.

Существующие функции перечислены ниже. Через «=» в подзаголовках указываются синонимы функций.

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

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

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

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

Числа, логические операции

LOG

Логарифм.

EVEN, ODD

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

INT=I=INTVAL

Преобразование к целому числу.

SEQ, SNE, SGT, SLT, SGE, SLE

Строковые сравнения.

NEQ, NNE, NGT, NLT, NGE, NLE

Численные сравнения.

YESNO

YESNO($1, $2, $3) — тернарный оператор ($1 ? $2 : $3).

Строки

LC=LOWER=LOWERCASE, UC=UPPER=UPPERCASE

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

LCFIRST, UCFIRST

Преобразование первого символа строки в нижний и верхний регистр соответственно.

Q=QUOTE=ADDSLASHES, SQ=SQL_QUOTE, REQUOTE=RE_QUOTE=PREG_QUOTE

  • Экранирование символов " ' \ и перевода строки бэкслэшем — quote(строка).
  • Экранирование символа " удвоением — sql_quote(строка). (актуально также для CSV)
  • Экранирование символов, являющихся специальными в регулярных выражениях — re_quote(строка). (см. perldoc perlre).

URI_QUOTE=URIQUOTE=URLENCODE

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

REPLACE, STR_REPLACE

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

STRLEN

Длина строки в символах.

SUBSTR=SUBSTRING

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

TRIM

Удаление пробелов из начала и конца строки.

SPLIT

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

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

  • Преобразование символов < > & " ' в HTML-сущности.
  • Удаление всех HTML/XML тегов.
  • Удаление только «небезопасных» HTML-тегов.
  • Преобразование переводов строк (\n) в HTML-тег <br />.

CONCAT, JOIN=IMPLODE

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

SUBST, SPRINTF, STRFTIME

Subst — подстановка на места подстрок вида $ЧИСЛО соответствующих параметров функции или элементов переданного массива — 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.

STRLIMIT=TRUNCATE

Ограничение длины строки str максимальной длиной len — strlimit(str, len, dots = "..."). Если строка превышает заданную длину, она обрезается предпочтительно по пробелу или Tab’у, а в конец добавляется dots или по умолчанию "...", если аргумент dots не передаётся.

Массивы и хеши

HASH

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

Соответственно в хеше аргументы идут парами КЛЮЧ => ЗНАЧЕНИЕ, КЛЮЧ => ЗНАЧЕНИЕ и т. п.

KEYS, HASH_KEYS, ARRAY_KEYS

Массив ключей хэша. Понятное дело, в PHP их порядок сохраняется, а в Perl — нет.

SORT

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

PAIRS

Массив хэшей вида { key => ключ, value => значение } для хэша, в случае Perl ключи будут отсортированы по имени.

ARRAY, RANGE

  • Создание массива.
  • Диапазон от A до B — range(A, B).

IS_ARRAY

Проверка, является ли аргумент массивом. В PHP-версии не проверяется, а не является ли он при этом хэшем, ибо трудоёмко.

COUNT, SUBARRAY=ARRAY_SLICE, SUBARRAY_DIVMOD

  • Количество элементов массива, или 0, если аргумент — не массив — count(аргумент).
  • Аналог функции array_slice из PHP.
  • Выбор из массива каждого div’того элемента, начиная с номера mod или нуля по умолчанию — subarray_divmod(массив, div, mod).

GET

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

SET

SET(куда, что) — присваивание «куда» значения «что». Уравнения, понятное дело, не решает, то есть, как и обычно, присваивать можно только lvalue :)

ARRAY_MERGE

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

SHIFT, POP, UNSHIFT, PUSH

  • Вынуть элемент из начала массива — shift(array)
  • Вынуть из конца — pop(array)
  • Добавить в начало — unshift(array, value)
  • Добавить в конец — push(array, value).

Включения

Сюда относятся функции выполнения других шаблонов и/или их блоков. Во все эти функции можно передавать «данные» (tpldata) либо с помощью создания хеша функцией hash(), либо просто передачей аргументов как КЛЮЧ => ЗНАЧЕНИЕ, ....

PARSE=INCLUDE=PROCESS

Включение другого шаблона.

parse('имя файла')
parse('имя файла', hash( ключ => значение, ... ))
parse('имя файла', ключ => значение, ...)

PARSE_INLINE=INCLUDE_INLINE=PROCESS_INLINE

Включение кода не из файла, а просто из строки — parse_inline('код шаблона'[, аргументы]).

EXEC

Включение блока из текущего шаблона — exec('имя блока'[, аргументы]).

EXEC_FROM

Включение блока из другого шаблона — exec_from('имя файла', 'имя блока'[, аргументы]).

EXEC_FROM_INLINE

Ещё больше не рекомендуется, но можно вызывать и функции из кода из строки — exec_from_inline('код шаблона', 'имя блока'[, аргументы]).

Прочее

VOID

Вычислить аргумент и вернуть пустую строку. Потенциально нужно для игнорирования результата, ибо все возвращаемые значения радостно подставляются в выходной поток.

DUMP=VAR_DUMP

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

JSON

Форматирование любой структуры данных в формат JSON.

CALL

Вызов метода объекта по «динамическому» имени — call(varref, method_name, arg1, arg2, arg3, ...).

MAP

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

Изменения относительно старых версий

  • Ликвидированы assign_vars(), assign_block_vars(), tr_assign_vars() — теперь, как и обычно, передаётся просто хеш с данными $vars
  • Синтаксис типа {a->key} ликвидирован
  • Авто-переводы, которые были в перловой версии — тоже тю-тю (хотя, может, и будут возрождены)
  • SET теперь заканчивается обычным END, а не ENDSET
  • Обращение к счётчику цикла теперь {block_index}, а не {block.#}
  • Добавлены функции, операторы
  • Добавлены детальные сообщения об ошибках
  • Добавлен встроенный фильтр для ликвидации пробелов из начал/концов каждой строки шаблона

Различия PHP и Perl версий

Perl-версия — устаревшая

На момент 2013-04-20 Perl-версия — устаревшая. А вот актуальная для неё старая версия документации.

Кэширование работает по-разному

В целом, общий смысл — сделать так, чтобы шаблоны было не стыдно вызывать много раз, как много раз за один запрос, так и в целом, при этом максимально использовать механизмы интерпретатора самого языка. Но механизмы для этого применяются разные. Основная причина различий следующая:

  • Perl: считается, что всё прогрессивное человечество уже давно использует mod_perl или другие способы запуска веб-приложений, при которых частых переинициализаций интерпретатора не происходит. Иными словами, никто больше не использует CGI. Таким образом, мы смело можем сохранить живой coderef (ссылку на функцию, или кому как больше нравится — анонимную функцию, замыкание, делегат) в промежутке между двумя запросами.
  • PHP: интерпретатор PHP инициализируется заново при обработке каждого HTTP-запроса. А живой coderef в промежутке между двумя инициализациями интерпретатора сохранить, видимо, невозможно.

В PHP также есть ещё одна проблема — в процессе выполнения невозможно добавить метод в класс без использования извращений типа classkit, а хочется, потому что сгенерированные из кода шаблона функции должны быть методами — они используют контекст класса Template.

Поэтому компилированный шаблон PHP-версии — это класс, производный от класса Template. Единожды за один HTTP-запрос он загружается в память, а при каждом вызове шаблона создаётся пустой объект этого класса, в него записывается ссылка на tpldata и поле parent, ссылающееся на родительский объект Template, и вызывается метод класса, соответствующий функции шаблона (<!-- FUNCTION ... -->).

Кроме кэширования классов в рамках запроса в PHP существует ещё две ступени:

  • Текст шаблонов кэшируется в XCache или eAccelerator, если таковые присутствуют, и не перезагружается с диска лишний раз. Если reload = false, лишними считаются все разы, кроме первого, даже если файл шаблона менялся.
  • Компилированный код шаблонов кэшируется в файлах на диске, и не компилируется лишний раз.

В Perl действие reload немного отличается — reload = 0 работает так же, как reload = false в PHP, но если reload > 0, то тексты шаблонов перезагружаются с диска при изменении, но не чаще, чем раз в reload секунд. В остальном всё проще — компилированный шаблон представляет собой просто хеш с набором анонимных функций, которые сохраняются в my-переменной пакета VMX::Template и вызываются при обращении к шаблону или его блокам. Также существует и файловый кэш компилированного кода.

Несколько различается действие use_utf8 = true

  • Общий смысл — «мои шаблоны и страницы в кодировке UTF-8».
  • PHP: «использовать mb_str* функции для работы со строками в выражениях».
  • Perl: «я передаю в шаблон все переменные с флагом UTF-8 = On, их можно смело конкатенировать с UTF-ными частями шаблона». Если кто-то не знает, в Perl строки имеют на себе флаг UTF-8 = да или нет, и при конкатенации строки без флага со строкой с флагом строка без флага будет автоматически переведена в UTF-8 из кодировки, соответствующей текущей локали. Что означает двойное UTF-8-кодирование в случае, если строка на самом деле всё-таки в UTF-8, но просто на ней не установлен флаг.
    Для приведения всех переменных шаблона к UTF-8 можно использовать функцию utf8on() из VMX::Common (рекурсивный Encode::_utf8_on()).

Различается способ вывода ошибок при print_error = true

  • Общий смысл — при print_error = true ошибки и предупреждения должны попасть на экран.
  • PHP: ошибки группируются и выводятся в отдельном div’е в конце страницы, либо просто print’ами по месту возникновения при вызове из консоли.
  • Perl: текст ошибок прицепляется к выводу шаблонизатора (возвращается вместе с результатом parse()).

Различается поведение сравнений

  • PHP: Обычные сравнения — типозависимые.
  • Perl: EQ и т. п. без S/N эквивалентно строковому (Sxx).

Различается поведение некоторых функций работы с массивами и хешами

  • KEYS — в PHP порядок ключей массива/хеша сохраняется, а в Perl — нет и принимаются только хеши. Обусловлено реализацией хешей в этих языках.
  • PAIRS — в PHP порядок ключей сохраняется, в Perl-версии ключи будут отсортированы по имени.
  • RANGE — в Perl-версии принимает буквенные аргументы (A..Z = весь алфавит).
  • IS_ARRAY — в PHP-версии не проверяется, а не является ли он при этом хэшем, ибо трудоёмко (надо проверить, численные ли все ключи).
  • AGET и HGET в PHP идентичны GET.
  • ARRAY_MERGE: под Perl — только массивы (не хеши), под PHP — любые массивы.
  • DUMP — это Dumper в Perl’е и var_dump в PHP.

Строка исходного файла

В PHP-версии в шаблоны не включаются C-подобные «прагмы» #line, а в текст ошибок не включается имя файла шаблона и строка. Ибо решил — раз уж #line не поддерживается, нечего на строки заморачиваться.

А кстати, зачем вообще нужен шаблонизатор?

Ответы:

  • Чтобы структурировать код, осознанно используя для генерации HTML-ек язык с ограниченными возможностями. Так как возможности ограничены, сложные вычисления писать на нём автоматически не хочется, соответственно, они перемещаются в логику, разделение становится более явным.
  • Чтобы структурировать выполнение — сначала логика, потом HTML. В идеале «обратной связи» из шаблонов в логику быть не должно, то есть шаблону должно передаваться ровно столько данных, сколько ему нужно, чтобы в процессе выполнения он ничего не дочитывал. Это сразу же ликвидирует:
    • Трудноуловимые проблемы производительности, происходящие по вине ленивых вычислений и вызовов методов модели, дочитывающих данные из БД, из view.
    • Проблемы с преждевременной отправкой HTTP-заголовков, после которой внезапно обнаруживается, что, оказывается, нужно было сделать редирект.

Примечание: так как PHP — «язык наизнанку», сам немножко являющийся шаблонизатором, то при выполнении следующих требований можно писать и без шаблонизатора:

  • Руками писать в «шаблонном стиле»:
    1. Не смешивать сложные конструкции с HTML.
    2. Шаблоны выносить в отдельные функции и общаться с ними через 1 ассоциативный массив с данными.
    3. Вывод не печатать, а буферизовать (ob_start() → ob_get_contents() → ob_end_clean()) и возвращать.
  • Использовать читаемый стиль кода (?> и <?php только в конце строк, отступы насквозь через PHP и HTML).

Это бывает весьма полезно, если нужно написать модуль к системе, которая сама написана без шаблонизатора или с каким-нибудь полу-кривым собственным, и не хочется вводить дополнительную зависимость.