Ни для кого сейчас уже не секрет, что шаблонный движок должен быть полноценным языком программирования.
Не стоит забывать, что применение шаблонов далеко не ограничено WEB-ом.
Это генерация различного вида отчётов, генерация кода программ на различных языках, генерация исходных данных для обработки и многое другое.
Вообще, шаблоны хочется применить везде, где много текста и мало логики.
В раздумьях, как бы это сделать побыстрее и подешевле, не следует обманывать себя (в основном, на раннем этапе) мыслями типа «да у меня статический текст, только надо приклеить шапку», «тут нужен только счётчик», «мне надо только вывести дату» и т.д.
Сначала — шапка и счётчик, дальше будет условная вставка блоков (if), потом понадобится генерация таблиц (тут уже нужны циклы и массивы), далее потянутся строковые операции и все остальные конструкции нормального языка программирования.
Поиск решения
Поскольку свой язык программирования я изобретать не собирался, а решение хотелось быстро и дёшево, взял я для начала perl с его regexp-ами и начал генерить генерить вывод по тэгам вида
<?=value?>
и <? code ?>
.Код получался вида
print "text";print $value; code
С другой стороны, посмотрев, как происходит подстановка в двойные кавычки, я понял, что занимаюсь тем, что уже сделано до меня: компилятор perl при разборе текста сам заменяет строки вида
"abc $var cde"
на "abc ".$var." cde"
Ну и прекрасно, можно вставлять значения и через $var, а результат получать eval-ом.
Это уже половина дела: переменные и элементы массивов в шаблон мы уже вставлять можем.
Осталось решить, что делать с кодом.
Тут приходит на помощь интерполяция массивов:
print "5*5 = @{[5*5]}"
выведет5*5 = 25
Возможно, этот хак требует пояснения.
При интерполяции @ означает, что сейчас мы будем выводить массив. Далее, {} отделяет сам массив от окружающего текста, а [ ] говорит о том, что вставлять мы будем массив по ссылке.
Однако, содержимое массива мы уже генерим кодом, который там подставлен. И об этом необходимо помнить в дальнейшем: у всего, что мы делаем тут в квадрантых скобках — контекст массива.
То есть, механизм работы: создание массива (часто, с одним элементом), создание ссылки на него и потом вывод этого массива по ссылке. Неоптимально, за то — работает.
Итак, код в строку вставлять тоже умеем.
Ещё немного — и…
Решение
#!/usr/bin/perl -w use strict; { local $/ = undef; $txt = <>; } print eval qq{<< "__END_OF_TEMPLATE"\n$txt\n__END_OF_TEMPLATE\n}; die $@ if $@;
Всё.
Это и есть шаблонный движок.
При желании он даже помещается в одну строку в perl -e.
Что это и зачем
Это действительно самый короткий и простой template engine, который я когда-либо видел. Запустить его можно везде, где есть perl и при этом ничего не надо скачивать, собирать, инсталлировать — в некоторых случаях это достаточно большая проблема.
Тут есть подстановка значений через $, выполнение кода на perl через @{[ ]}, есть правильный вывод ошибок с правильным указанием номера строки. Тут даже есть модульность: важные, часто используемые и длинные функции отделяются в отдельный модуль или просто исходник, который из самого шаблона потом подключается через use или do соответственно.
Практически ничего не сделав, мы получили почти всё что надо!
Шаблоны имеют такой вид:
Привет!
Дата на сервере: @{[''.localtime]}.
PID: $$
\@INC path: @INC
Environment:
@{[join "\n", map {".... $_ => $ENV{$_}"} sort keys %ENV]}
@{[do{use IO::File; IO::File->new("<tail.inc")->getlines()}]}
Практически в неизменном виде я использовал его уже в нескольких задачах:
• в специфичном самописном http-сервере страницы содержались в hash (по пути) и через этот же механизм результат вычислялся и отдавался клиенту;
• генерил код на C++ со сложными структурами и зависимостями, данные описывались в глубокой разветвлённой perl-структуре — там был аналог XSLT на хешах и массивах;
• делал заменитель autotools для генерации Makefile.
Не говоря уже о множестве мелких применений.
Универсальность тут состоит в том, что нигде нет вообще никакой привязки к герерируемому контенту, а специфичных функций можно наделать для любого применения.
В общем, если у вас ничего нет и вам срочно нужно что-то подставить в шаблон, этот способ — для вас.
Если хочется принимать код от клиентов, можно посмотреть в сторону таких модулей как Safe или ops, но сразу хочу сказать, что готового решения тут я не предлагаю.
Где это использовать НЕ надо:
• Не надо это пытаться отдавать дизайнерам и не надо от них требовать шаблонов в этом формате. Помните, что реально это — программа на PERL, хоть и выглядящая как HTML (или что-то другое).
• Без серьёзной доработки не стоит напрямую совать туда контент от пользователей, скорее всего это приведёт к локальному выполнению кода и взлому.
• Бинарные данные генерить можно, но надо, как минимум, подумать, как поступить с последним переводом строки (наверно, просто оторвать).
• Если у вас уже есть «правильный» шаблонизатор, заточенный под вашу задачу, этот костыль вам тоже не нужен.
В областях, где этот способ шаблонизации использовал я, метод показал себя прекрасно и кое-где работает до сих пор.
Отвечу на все вопросы, если они возникнут.