Универсальный шаблонный движок за 1 минуту из подручных материалов

    Шаблон

    Ни для кого сейчас уже не секрет, что шаблонный движок должен быть полноценным языком программирования.

    Не стоит забывать, что применение шаблонов далеко не ограничено 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 (или что-то другое).
    • Без серьёзной доработки не стоит напрямую совать туда контент от пользователей, скорее всего это приведёт к локальному выполнению кода и взлому.
    • Бинарные данные генерить можно, но надо, как минимум, подумать, как поступить с последним переводом строки (наверно, просто оторвать).
    • Если у вас уже есть «правильный» шаблонизатор, заточенный под вашу задачу, этот костыль вам тоже не нужен.

    В областях, где этот способ шаблонизации использовал я, метод показал себя прекрасно и кое-где работает до сих пор.

    Отвечу на все вопросы, если они возникнут.
    Поделиться публикацией
    Похожие публикации
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 13
      0
      Напомнило книги из серии «Стань гуру веб-разработки за 24 часа» :-) Спасибо.
        –1
        Mojolicious вам в помощь.
          0
          Интересная штука, спасибо.
          Когда я делал тот веб-сервис, perl был ещё 5.005, ничего такого не было.
          Если буду делать что-то для web, посмотрю на Mojolicious в первую очередь.
            0
            Одно из основных достижений сообщества Perl – это CPAN, огромная библиотека готовых решений для самых разных задач.

            eval строк обычно не рекомендуется использовать, т.к. он запускает отдельный экземпляр компилятора, плюс есть версия, что при определённых обстоятельствах он течёт по памяти.
              0
              Про cpan знаю и использую когда есть возможность.

              К сожалению, при использовании модулей с cpan приложение резко теряет в мобильности.
              Поэтому если к скрипту главное требование — чтобы он работал везде, где его запустят, cpan с собой уже таскать не стоит.
              Кроме этого, при апгрейде системы скомпилированные модули могут поломаться и всё придётся пересобирать, всё это связанно с проблемами при поддержке.
              А с бандлами в perl, к сожалению, до сих пор не всё гладко.

              Этот скрипт — абсолютно переносим.

              В этом его главное преимущество.

              Недостатки тоже есть и они известны.

              Ну а насчёт течки по памяти… к сожалению, в perl текут и более насущные места кроме eval.
                0
                Mojolicious не имеет никаки зависимостей, кроме Perl 5.8.7, так что его не нужно собирать в системе, достаточно скопировать модули и сделать use lib.
                  0
                  То есть он также абсолютно переносим.
                  0
                  >> Ну а насчёт течки по памяти… к сожалению, в perl текут и более насущные места кроме eval.
                  ?
                    0
                    Точного ответа у меня нет, но есть жизненный пример.

                    Некий сетевой сервис, который должен работать 24/7/365 пришлось перестартовывать по cron раз в сутки, иначе он раз в несколько месяцев при всплеске нагрузки терял файловые дескрипторы сокетов (или пайпов) и переставал работать.
                    Какой именно модуль тут оказался кривым — сказать сложно, из специфики могу сказать, что интенсивно использовались DBI (MySQL), LWP::Simple, локальные сокеты и fork().

                    С другой стороны, ещё на Perl 5.005 в каких-то лохматых годах я написал специфичный web-сервер, который именно работал круглосуточно и без перезапусков месяцами и годами — его аптайм всегда был равен аптайму сервера.

                    Как настоящий гик, наверно, я должен был кинуться и найти, что именно и куда утекает, но ежедневные задачи не дали мне этого сделать :) или попросту было лень :)
                      0
                      Спасибо за развернутый ответ!
                      По опыту могу сказать, что утечки все-таки имеют место быть в Perl-XS модулях, там где сборщик мусора уже не может помочь :)

            0
            кодобред, говнокод, устаревшее говно мамонта, мазохизм, шизофрения.
              +1
              Ни для кого сейчас уже не секрет, что шаблонный движок должен быть полноценным языком программирования.

              Не в этой вселенной.
                0
                да, дальше не читал.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое