HTML-шаблоны для PHP и Perl, или Не делайте инструмент самоцелью!::Журнал СА 8.2004
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
О журнале
Журнал «БИТ»
Подписка
Где купить
Авторам
Рекламодателям
Магазин
Архив номеров
Вакансии
Контакты
   

ЭКСПЕРТНАЯ СЕССИЯ 2019


  Опросы

Какие курсы вы бы выбрали для себя?  

Очные
Онлайновые
Платные
Бесплатные
Я и так все знаю

 Читать далее...

1001 и 1 книга  
28.05.2019г.
Просмотров: 1420
Комментарии: 2
Анализ вредоносных программ

 Читать далее...

28.05.2019г.
Просмотров: 1542
Комментарии: 1
Микросервисы и контейнеры Docker

 Читать далее...

28.05.2019г.
Просмотров: 1169
Комментарии: 0
Django 2 в примерах

 Читать далее...

28.05.2019г.
Просмотров: 911
Комментарии: 0
Введение в анализ алгоритмов

 Читать далее...

27.03.2019г.
Просмотров: 1477
Комментарии: 1
Arduino Uno и Raspberry Pi 3: от схемотехники к интернету вещей

 Читать далее...

Друзья сайта  

Форум системных администраторов  

sysadmins.ru

 HTML-шаблоны для PHP и Perl, или Не делайте инструмент самоцелью!

Архив номеров / 2004 / Выпуск №8 (21) / HTML-шаблоны для PHP и Perl, или Не делайте инструмент самоцелью!

Рубрика: Веб /  Веб-технологии

ДМИТРИЙ ГОРЯИНОВ

HTML-шаблоны для PHP и Perl,
или не делайте инструмент самоцелью!

О шаблонах в веб-программировании знают или слышали буквально все. Интерпретируемые языки веб-программирования, такие как PHP, Perl, ASP VBScript, всегда давали возможность совмещать код языка и HTML. Это быстро и просто. Вначале. Как только проект или отдельный файл-сценарий начинают разрастаться, эта смесь так же быстро становится неудобоваримой. Взявшись переписывать какой-либо сценарий, сразу хочется выкинуть HTML-вставки просто потому, что они начинают мешать читать программный код.

Ради этого, ради разделения HTML-форматирования вывода и логики программы и был придуман механизм шаблонов. Логика остается программе, верстка – шаблону.

Программа обрабатывает запрос, формирует данные и заполняет шаблон. Шаблон всегда содержит HTML-код и некоторые дополнительные конструкции для вставки данных и управления выводом. Тут необходимо кое-что прояснить.

Небольшой взгляд в историю, или Каким должен быть шаблонный движок?

Несомненно, идея шаблонов пришла в голову программистам. И суть идеи явно была в том, чтобы убрать HTML-код из программы. А вот дальше… Дальше все складывалось по-разному. Понятно, что первыми «родились» простейшие шаблоны. Это просто HTML-код и специальные указания для вставки данных. По сути, переменные.

<html>

<head>

    <title>::title::</title>

</head>

<body>

::body::

</body>

</html>

 Такой простой HTML-документ. Можно сохранить и открыть в браузере. Будет страничка с заголовком «::title::» и содержимым «::body::».

Есть конструкция (::имя::), которую нужно выделить из HTML-кода, есть некоторое имя, вместо которого нужно подставить данные из программы. Можете называть это переменной, но лучше назовем это именованной вставкой.

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

Вот тут началось жуткое многообразие. В шаблоны стали добавлять макроязыки. При этом, в зависимости от квалификации авторов системы, в шаблоны внедряли условные операторы, операторы циклов, регулярные выражения, макрофункции и даже объекты (последним сильно грешат системы шаблонов для языка Java). К чему же это привело?

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

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

И при этом все, что нам действительно нужно от шаблона это:

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

В идеале синтаксис дополнительных конструкций должен позволять работать с шаблоном в традиционном HTML-редакторе и просматривать готовый файл-шаблон в браузере.

Систем шаблонов написано действительно много. Мы будем рассматривать две из них (одну для языка PHP и вторую – для Perl). Основная цель статьи – показать, как правильно работать с готовыми движками, чтобы минимизировать изменения в HTML-коде шаблонов и оставить логику за программой.

Добавление шаблонного движка в проект

Итак, что вообще требуется от шаблонного движка? Как минимум – установить, подключить и начать использовать.

Во-первых, основная цель проекта не в том, чтобы поработать с шаблонами. Наша система будет загружена собственной полезной работой, и тратить большие ресурсы на поддержание работы с шаблонами крайне нежелательно. Во-вторых, нам вряд ли подойдет движок, который потребует дополнительной компиляции каких-либо компонент или изменения конфигурации веб-сервера. Дело даже не в том, что у нас нет своего сервера, а злой дядька провайдер не желает что-то там собрать-подключить на своей площадке. Просто вариант «отдельный проект – отдельный сервер» почти нереален. Скорее всего, наш сервер уже обслуживает какие-то проекты и нет ни малейшего желания бросаться их переписывать на новый лад или менять рабочую конфигурацию. Да и останавливать боевой сервер ради добавления «новой забавы» мало кто (читай – системный администратор) согласится.

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

В качестве движка для PHP мы рассмотрим библиотеку XTemplate (http://sourceforge.net/projects/xtpl), а для Perl – модуль HTML::Template (http://html-template.sourceforge.net). Оба движка достаточно именно скачать и разархивировать. Вы, конечно, можете установить модуль для Perl по всем правилам, но вполне достаточно развернуть из архива каталог HTML с файлом-модулем Template.pm в нем.

Куда разархивировать? Для простоты предлагается создать отдельный каталог для дополнительных библиотек. Пусть каталог htdocs – это корневой каталог размещения документов на веб-сервере. Создадим на одном уровне с ним каталог lib, и разархивируем в него наши движки, каждый – в отдельный каталог. Получится вот такая структура директорий:

Рисунок

В каталоге XTemplate будут файлы PHP-библиотеки, демонстрационные примеры и т. п. В каталоге HTML может находиться один-единственный perl-модуль – Template.pm

Собственно, на этом все необходимые манипуляции по установке будут закончены. Просто и эффективно, не правда ли?

Просто отдельный класс

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

Движок должен быть инкапсулирован в отдельный класс. Он не должен иметь ветвистых зависимостей с другими модулями. Иначе устанешь их удовлетворять.

Множество хороших веб-программистов создавали и будут создавать свои, уникальные решения в рамках отдельных проектов. В таких случаях что-то непременно «затачивается» под конкретные нужды, что-то «дергается напрямую» и т. д. На базе таких решений вырастают системы публикаций или управления сайтами. Все это плотно упаковано, скручено в тугой узел, плохо отделимо друг от друга и, возможно, представляет собой законченный продукт, который, скорее всего, больше никому, кроме заказчика, не пригодится.

Если же речь идет именно о подсистеме для работы с шаблонами, то такая система должна представлять собой законченный интерфейс, отделимый от остальной системы. Фактически – уникальный класс либо модуль.

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

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

Библиотеку PHP XTemplate мы разместили в подкаталоге XTemplate каталога lib. Класс XTemplate содержится в файле xtpl.p.

Для того чтобы создать новый экземпляр класса, нам нужно подключить файл и создать объект:

<?php

...

include( getenv("DOCUMENT_ROOT")."/../lib/XTemplate/xtpl.p" );

$xtpl=new XTemplate( полное_имя_файла_шаблона );

...

?>

Для подключения библиотеки HTML::Template в Perl следует указать путь к её директории (если, как в нашем случае, она размещена отдельно от стандартных библиотек), подключить модуль и так же создать новый объект:

#!/usr/bin/perl

...

use lib $ENV{DOCUMENT_ROOT}."/../lib";

# Другой способ добавить каталог размещения библиотеки:

# unshift( @INC, $ENV{DOCUMENT_ROOT}."/../lib" );

use HTML::Template;

my $template = HTML::Template->new( filename => полное_имя_файла_шаблона );

Что же такое шаблон?

Вернемся к началу. Для чего затевалась вся эта история с шаблонами? В основном для того, чтобы отделить код HTML от кода программы. Идеальный шаблон – это файл, содержащий HTML-разметку, и программно заполняемый данными.

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

То есть никакое изменение в HTML-коде не может сразу отразиться на сайте. Пока такие системы не дают настолько ощутимого выигрыша в скорости, чтобы считать это действительно хорошим решением.

Так что сформулируем еще один постулат – движок не должен требовать дополнительной обработки шаблонов (читай – запретить предварительную компиляцию и прочие действия).

Шаблон должен редактироваться как HTML-файл, иметь возможность предварительного просмотра как HTML-файл и подключаться «как есть». Мы же не хотим заставить верстальщика установить у себя веб-сервер лишь для того, чтобы просматривать промежуточные результаты своей работы?

Вот два простых примера шаблонов для XTemplate и HTML::Template.

Пример шаблона для библиотеки Xtemplate: 

<!-- BEGIN: main -->

<html>

<head><title>Пример 1</title></head>

<body>

<dl>

<dt>Значения переменных добавляются в XTemplate через метод

<i><b>assign( PARAM, value )</I></b>:</dt>

<dd>$VARIABLE = <i>{VARIABLE}</i></dd>

</dl>

</body>

</html>

<!-- END: main -->

Если мы сохраним этот код в файле ex1.html и откроем его для просмотра в браузере, мы увидим такую «картинку»:

Рисунок 1. Вид шаблона для XTemplate в браузере

Рисунок 1. Вид шаблона для XTemplate в браузере

Пример шаблона для библиотеки HTML::Template:

<html>

<head><title>Пример 1</title></head>

<body>

<dl>

<dt>Значения переменных добавляются в HTML::Template через метод

<i><b>param( PARAM => value );</i></b>:</dt>

<dd>$VARIABLE = <i><TMPL_VAR NAME=VARIABLE></i></dd>

</dl>

</body>

</html>

Так как подстановки в шаблонах HTML::Template имеют синтаксис, схожий с HTML-тэгами, HTML-вид шаблона будет слегка отличаться:

Рисунок 2. Вид шаблона для HTML::Template в браузере

Рисунок 2. Вид шаблона для HTML::Template в браузере

Код подстановки () не отобразится в браузере. Фактически браузер воспримет этот как HTML-тэг, правило отображения которого ему неизвестно.

Лучшее – враг хорошего. Минимизируем логику, макроязык шаблонов

Теперь постараемся разобраться с тем, что нам действительно понадобится в шаблонах, кроме HTML-кода.

Есть несколько типичных задач, с которыми мы сталкиваемся постоянно и для которых мы хотим иметь четкое и простое решение. Хорошим примером такой задачи может служить вывод таблицы с чередованием фона строк. При выводе таблицы мы, во-первых, имеем дело с повторяющимися элементами (строками), во-вторых, чередование цвета фона – это выбор оформления в зависимости от условия.

Шаблон «ex2.html» для библиотеки Xtemplate:

<!-- BEGIN: main -->

<table bgcolor="gray" cellspacing=2 border=0 cellpadding=4>

  <tr><th colspan="2" bgcolor="silver">{TOPIC}</th></tr>

  <!-- BEGIN: item -->

    <!-- BEGIN: odd -->

    <tr bgcolor="white">

    <td>{DATA.NAME}</td><td>{DATA.VAL}</td>

    </tr>

    <!-- END: odd -->

    <!-- BEGIN: even -->

    <tr bgcolor="lightyellow">

    <td>{DATA.NAME}</td><td>{DATA.VAL}</td>

    </tr>

    <!-- END: even -->

  <!-- END: item -->

</table>

<!-- END: main -->

Рассмотрим шаблон подробнее. Прежде всего отметим, что весь шаблон для XTemplate заключен внутри именованного блока:

<!-- BEGIN: main -->

...

<!-- END: main -->

Это необходимое условие синтаксиса шаблонов XTemplate. Любой блок (и весь шаблон целиком) начинается с конструкции:

<!-- BEGIN: имя_блока -->

И заканчивается конструкцией:

<!-- END: имя_блока -->

Простая подстановка в шаблоне описывается так:

{имя_переменной_для_подстановки}

Там, где логика подсказывает нам сгруппировать данные для подстановки, можно использовать массивы подстановок, описывая их таким образом:

{имя_массива.имя_ключа}

К примеру, это логично сделать при оформлении подстановок значений ячеек табличной строки.

Шаблон описывает HTML-код для вывода данных. Сами данные формируются в программе. На уровне шаблона мы не можем предусмотреть количество элементов нашей таблицы.

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

<!-- BEGIN: item -->

...

<!-- END: item -->

Внутри блока item мы опишем два других блока – «odd» и «even» – для чередования оформления строк. Программа-обработчик, проверяя условие четности строки, будет выбирать нужный блок и управлять генерацией конечного HTML-вывода, формируя содержимое блока item.

В XTemplate вся логика организации циклов и условий ложится на код PHP-программы. В шаблоне описываются только блоки и имена для подстановки значений.

Опишем PHP-код, обрабатывающий этот шаблон:

<?php

// Подключение библиотеки

include( getenv("DOCUMENT_ROOT") ."/../lib/XTemplate/xtpl.p" );

// Создание нового объекта. файл шаблона ex2.html

// размещаем в каталоге htdocs/../data/xtemp

$xtpl=new XTemplate( getenv("DOCUMENT_ROOT") ."/../data/xtemp/ex2.html" ); 

// Простая подстановка значения в {TOPIC}

$xtpl->assign( TOPIC, "Вывод таблицы с чередованием фона строк" );

$even = 0;

// Цикл для формирования строк таблицы

for ($i=1; $i<=10; $i++) {

  $row = array(

    NAME => "Строка",

    VAL => $i

  );

  $xtpl->assign(DATA, $row);

  // В зависимости от четности строки указываем

  // блок-шаблон вывода «odd» или «even»

  if ( ($even = ($even XOR 1)) ) {

    $xtpl->parse("main.item.odd");

  } else {

    $xtpl->parse("main.item.even");

  }

  $xtpl->parse("main.item");

}

// Разбор главного блока шаблона

$xtpl->parse("main");

// Вывод результата разбора

$xtpl->out("main");

?>

Для того чтобы включить в результат вывода нужный блок из шаблона, используется метод parse( BLOCK ). Имя блока задается в виде строки, с указанием всех родительских блоков через точку:

BLOCK::= [<Имя_блока>.]<Имя_блока>

Таким образом, блок-шаблон для вывода нечетных строк будет именоваться «main.item.odd».

Разумеется, для того чтобы блок был включен в конечный вывод, необходимо вывести и все родительские блоки:

...

    $xtpl->parse(“main.item.odd”);

  ...

  $xtpl->parse(“main.item”);

...

$xtpl->parse(“main”);

...

Метод assign( PARAM, value ) служит для заполнения значения переменной шаблона с именем PARAM.

$xtpl->assign( TOPIC, "Вывод таблицы с чередованием фона строк" );

И наконец, метод out(BLOCK) печатает результат разбора шаблона. Если нам нужно не выводить, а сохранить его в виде готового текста, нужно использовать другой метод – text(BLOCK)…».

Обращение к методу out() равносильно такому коду:

...

$out = $xtpl->text("main");

echo $out;

Перепишем наш пример с таблицей для библиотеки HTML::Template. Сразу заметим, что изначально синтаксис шаблонов библиотеки HTML::Template более адаптирован «под программистов». Создатели библиотеки остались верны программированию. Здесь нет таких простых и понятных блоков, выделяемых как HTML-комментарии. Увы, здесь есть отдельные конструкции для циклов и условий. Для нас хорошо то, что синтаксис этих конструкций аналогичен тэговой разметке, и они не отображаются при просмотре файла-шаблона браузером.

Шаблон «ex2.html» для библиотеки HTML::Template:

<table bgcolor="gray" cellspacing=2 border=0 cellpadding=4>

  <tr><th colspan="2" bgcolor="silver"><TMPL_VAR NAME=TOPIC></th></tr>

  <TMPL_LOOP NAME=item>

    <TMPL_IF NAME=odd>

    <tr bgcolor="white">

    <td><TMPL_VAR NAME></td><td><TMPL_VAR VAL></td>

    </tr>

    <TMPL_ELSE>

    <tr bgcolor="lightyellow">

    <td><TMPL_VAR NAME></td><td><TMPL_VAR VAL></td>

    </tr>

    </TMPL_IF>

  </TMPL_LOOP>

</table>

В отличие от XTemplate мы не заключаем весь шаблон в блок.

Библиотека HTML::Template не требует заключать весь шаблон в блок. Она работает с целым файлом, не деля его на отдельные блоки.

Простую подстановку (замену) значения в шаблоне описывают так:

<TMPL_VAR NAME=имя_параметра_подстановки>

Для того чтобы описать в шаблоне повторяющийся HTML-код, его придется обрамить «оператором цикла»:

<TMPL_LOOP NAME=имя_повторяющегося_блока>

...

</TMPL_LOOP>

В программе для вывода такого цикла нужно сформировать массив и сопоставить его с именем повторяющегося блока. Каждый элемент массива соответствует включению HTML-кода, описанному внутри <TMPL_LOOP> … </TMPL_LOOP>. Элемент массива представляет собой хэш (наборы пар вида «ключ – значение»). То есть если в шаблоне мы описываем подстановку вида <TMPL_VAR NAME=имя_параметра_подстановки> внутри повторяющегося блока, то в хэше, описывающем элемент массива для подстановки, мы должны создать пару (имя_параметра_подстановки => значение_для_подстановки).

Создатели библиотеки внедрили в шаблон условный оператор. Мы будем использовать его только там, где нужно указать отдельный, вложенный блок HTML-кода, который будет подключаться в зависимости от условия, сформированного в программе:

    <TMPL_IF NAME=имя_блока>

    ...

    </TMPL_IF>

Подключение вывода блока зависит от значения, присвоенного обработчиком шаблона имени_блока. Если мы проинициализируем переменную блока значением «истина» – HTML-код, описанный внутри <TMPL_IF> … </TMPL_IF>, будет включен в результат разбора шаблона.

Если мы не определим никакого значения для блока, или определим его как «ложь», блок не будет включен в результаты разбора шаблона.

Как и в традиционном синтаксисе языков программирования, у условного блока (оператора) может существовать «альтернативная часть»:

    <TMPL_IF NAME=имя_блока>

    ...

    <TMPL_ELSE>

    ...

    </TMPL_IF>

Если переменная блока получила из программы значение «ложь», то HTML-код между <TMPL_ELSE> и </TMPL_IF> будет включаться в результат разбора.

Код для обработки этого шаблона будет выглядеть так:

#!/usr/bin/perl

use strict;

# Путь к каталогу библиотеки

use lib $ENV{DOCUMENT_ROOT}."/../lib";

# Подключение библиотеки

use HTML::Template;

# Создание нового объекта. В данном случае мы отдельно

# указываем в конструкторе краткое имя файла шаблона

# ex2.html и путь к каталогу htdocs/../data/htmltemp,

# где размещен шаблон

my $template = HTML::Template->new(

     filename => "ex2.html",

     path => $ENV{DOCUMENT_ROOT}."/../data/htmltemp/"

);

# Простая подстановка значения в <TMPL_VAR NAME=TOPIC>.

# Делается вызовом метода param( PARAM => value)

$template->param(TOPIC => "Вывод таблицы с чередованием фона строк" );

my $even = 0;

# Создание массива для вывода строк таблицы

my $items = ();

# Цикл заполнения массива данными

for (my $i=1; $i<=10; $i++) {

    # Каждый элемент массива (строка таблицы) представляет

    # собой хэш вида: [ { "NAME" => значение1, "VAL" =>

    # значение2, "odd" => [0|1] } ]

    my $row = {

      "NAME" => "Строка",

      "VAL" => $i

    };

    # Для нечетных элементов устанавливаем значение "odd",

    # равным "1", для четных - "0"

    if ( ($even = ($even xor 1)) ) {

      $row->{"odd"} = 1;

    } else {

      $row->{"odd"} = 0;

    }

    # Добавляем новый хэш-элемент в массив

    push(@{$items}, $row);

}

# Ассоциируем массив с повторяющимся блоком ( <TMPL_LOOP> )

$template->param(item => $items);

# Результат разбора шаблона возвращает нам в виде строки вызов метода output().

# Мы можем сохранить его в переменную, записать в файл или вывести, как любое другое значение:

print "Content-type: text/html\n\n";

print $template->output();

__END__

Вложенные шаблоны

Что еще может пригодиться в работе с шаблонами? Любой сайт содержит повторяющиеся элементы дизайна – шапка, заголовки, меню и так далее.

Хороший движок шаблонов должен иметь возможность подключать более мелкие шаблоны (назовите их заготовками или библиотечными элементами) внутрь более крупных, желательно – поддерживать многоуровневое, вложенное подключение.

Чуть выше мы написали два шаблона для вывода таблицы. Логично не копировать этот код везде, где нам понадобится отобразить такую таблицу, а иметь возможность сохранить этот шаблон отдельно и подключать его по мере необходимости в другие шаблоны.

Обе наши библиотеки такую возможность предоставляют. В XTemplate существует конструкция:

{FILE "имя_вложенного_файла_шаблона"}

В HTML::Template аналогичные функции выполняет конструкция:

 <TMPL_INCLUDE NAME="имя_вложенного_файла_шаблона">

А вот здесь остановимся. Если вы посмотрите внимательно, то заметите, что мы вынуждены писать имя файла непосредственно в шаблоне. Это значит, что мы стали жестко привязаны к расположению файлов. Даже если мы укажем относительный путь к файлу шаблона, мы вынуждены будем в любом другом проекте в точности повторять структуру директорий для размещения шаблонов!

Это плохо. Более того, это как-то противоречит самой идее о легкости подключения (читай – переноса) и замыслу о повторном использовании более мелких (библиотечных) шаблонов.

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

В случае HTML::Template эта возможность предусмотрена изначально. Обратите внимание, мы сразу использовали инициализацию каталога шаблонов в нашем примере:

my $template = HTML::Template->new(

     filename => "ex2.html",

     path => $ENV{DOCUMENT_ROOT}."/../data/htmltemp/"

);

В библиотеке XTemplate это не предусмотрено (во всяком случае в версии 0.2.4-3).

Сформировать имя файла шаблона динамически, написав нечто вроде:

{FILE "{TEMP_FILE}"}

и присвоить (проассоциировать) нужное значение подстановкой в {TEMP_FILE} тоже не выйдет. Конструктор класса производит рекурсивную inline-подстановку всех вложенных шаблонов, и только потом производит разбор и обработку полного шаблона со всеми подстановками.

Что делать? Отказаться от идеи повторного использования вложенных элементов? Ну не все так печально, как кажется. Библиотека XTemplate распространяется по лицензии GNU General Public License. Эта лицензия подразумевает возможность внесения изменений в исходный продукт при соблюдении определенных правил[1]. Нужные нам изменения ограничатся тремя строчками с добавлением комментариев.

Суть изменения сводится к добавлению еще одного члена класса и опциональной инициализации его в конструкторе. Этот новый член класса – path – будет определять путь к директории шаблонов. Остальные модификации в коде исходного класса ограничиваются конструктором и методом getfile(...).

class XTemplate {

...

/* Новый член класса */

var $path = "";

/***[ constructor ]*** /

function XTemplate ($file,$mainblock="main", $path="") {

  /* Инициализируем значение path, если соответствующий параметр не пуст */

  if ($path) $this->path= str_replace("\\", "/", $path)."/";

  …

}

/***[ getfile ]*** /

/*

  returns the contents of a file

*/

function getfile($file) {

  /* Используем конкатенацию значения path и переданного

     имени файла */

  $file = $this->path.$file;

  if (!isset($file)) {

    $this->set_error("!isset file name!");

    return "";

  }

  ...

}

...

} /* end of XTemplate class. */

После внесения таких изменений в класс XTemplate мы:

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

Теперь можно нормально работать.

Шаблон «ex_inc.html» для библиотеки XTemplate:

<!-- BEGIN: main -->

<html>

<head><title>Вложенные шаблоны</title></head>

<body>

<p>

Подключение шаблона таблицы <b><i>"{TEMPLATE}" </i></b>

через { FILE "{TEMPLATE}" }

</p>

{FILE "ex2.html"}

</body>

</html>

<!-- END: main -->

Обработчик шаблона выглядит так:

<?php

include( getenv("DOCUMENT_ROOT")."/../lib/XTemplate/xtpl.p" );

/*

    В конструктор добавился дополнительный параметр $path.

    Для совместимости нам придется вызывать конструктор с указанием нашего нового параметра и второго параметра вызова – названием

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

т.е. в шаблонах с вложением.

*/

$xtpl=new XTemplate( "ex_inc.html",

                    "main",

                    getenv("DOCUMENT_ROOT")."/../lib/data/xtemp" );

$xtpl->assign( TEMPLATE, "ex2.html");

/*

    Инициализация данных вложенного шаблона.

    При обращении к методу parse() нужно учитывать, что элементы шаблона ex2.html вложены в блок "main" более верхнего шаблона –

ex_inc.html. Поэтому указывать придется как $xtpl->parse("main.main.item") и т. п.

*/

$xtpl->assign( TOPIC, "Вывод таблицы с чередованием фона строк" );

$even = 0;

for ($i=1; $i<=10; $i++) {

  $row = array(

    NAME => "Строка",

    VAL => $i

  );

  $xtpl->assign(DATA, $row);

  if ( ($even = ($even XOR 1)) ) {

    $xtpl->parse("main.main.item.odd");

  } else {

    $xtpl->parse("main.main.item.even");

  }

  $xtpl->parse("main.main.item");

}

/* Разбор вложенного шаблона */

$xtpl->parse("main.main");

Результат наших действий в браузере отразится примерно таким образом:

Рисунок 3. Результат работы с вложенными шаблонами. Вывод таблицы реализован отдельным шаблоном

Рисунок 3. Результат работы с вложенными шаблонами. Вывод таблицы реализован отдельным шаблоном

Для библиотеки HTML::Template подобных модификаций исходного кода производить не требуется. Параметризованное указание каталога размещения шаблонов там предусмотрено заранее.

Шаблон «ex_inc.html» для библиотеки HTML::Template:

<html>

<head><title>Вложенные шаблоны</title></head>

<body>

<p>

Подключение шаблона таблицы <b><i>"<TMPL_VAR NAME=TEMPLATE>" </i></b>

через &lt;TMPL_INCLUDE NAME="ex2.html">

</p>

<TMPL_INCLUDE NAME="ex2.html">

</body>

</html>

Обработчик шаблона:

#!/usr/bin/perl

use strict;

# Путь к каталогу библиотеки

use lib $ENV{DOCUMENT_ROOT}."/../lib";

# Подключение библиотеки

use HTML::Template;

# Создание нового объекта. В данном случае мы отдельно

# указываем в конструкторе краткое имя файла шаблона

# ex_inc.html и путь к каталогу htdocs/../data/htmltemp,

# где размещен шаблон

my $template = HTML::Template->new(

      filename => "ex_inc.html",

      path => $ENV{DOCUMENT_ROOT}."/../data/htmltemp/"

);

$template->param(TEMPLATE => "ex2.html" );

# Заполнение данных вложенного шаблона

$template->param(TOPIC => "Вывод таблицы с чередованием фона строк");

my $even = 0;

my $items = ();

for (my $i=1; $i<=10; $i++) {

  my $row = {

    "NAME" => "Строка",

    "VAL" => $i

  };

  if ( ($even = ($even xor 1)) ) {

    $row->{"odd"} = 1;

  } else {

    $row->{"odd"} = 0;

  }

  push(@{$items}, $row);

}

$template->param(item => $items);

print "Content-type: text/html ";

print $template->output();

__END__

Результат работы аналогичен, не будем загромождать место еще одной похожей картинкой.

Еще один «интересный случай». Вложенные шаблоны хочется использовать для стандартных элементов. Легко предположить, что один и тот же элемент может использоваться на странице многократно. Если включить его как вложенный шаблон, получится путаница с заполнением. Как поступить?

Нам нужен управляемый способ многократного включения вложенных шаблонов (стандартных элементов дизайна). Звучит очень серьезно, реализуется – просто. Каждое включение нужно выделить в виде отдельного блока.

Шаблон «list.html» для библиотеки XTemplate:

<!-- BEGIN: list -->

<ol>

<!-- BEGIN: item -->

<li>{ITEM}</li>

<!-- END: item -->

</ol>

<!-- END: list -->

Шаблон «ex_inc2.html» для библиотеки XTemplate:

<!— BEGIN: main —>

<html>

<head><title>Вложенные шаблоны</title></head>

<body>

<p>

Подключение одного шаблона в разных местах страницы и с разным заполнением.

</p>

<!-- BEGIN: first -->

Список 1:

{FILE "list.html"}

<!-- END: first -->

<!-- BEGIN: second -->

Список 2:

{FILE "list.html"}

<!-- END: second -->

</body>

</html>

<!-- END: main -->

Обрабатываем шаблон на PHP:

<?php

include( getenv("DOCUMENT_ROOT")."/../lib/XTemplate/xtpl.p" );

$xtpl=new XTemplate( "ex_inc2.html",

                    "main",

                   getenv("DOCUMENT_ROOT")."/../lib/data/xtemp" ); 

// Данные для первого списка

$list = array("one", "two", "three");

foreach($list as $item) {

  $xtpl->assign(ITEM, $item);

  $xtpl->parse("main.first.list.item");

}

// Разбираем блок "first"

$xtpl->parse("main.first.list");

$xtpl->parse("main.first");

// Данные для второго списка

$list = array("раз", "два", "три");

foreach($list as $item) {

  $xtpl->assign(ITEM, $item);

  $xtpl->parse("main.second.list.item");

}

// Разбираем блок "second"

$xtpl->parse("main.second.list");

$xtpl->parse("main.second");

$xtpl->parse("main");

$xtpl->out("main");

?>

Для того чтобы реализовать то же самое с использованием библиотеки HTML::Template, придется кое-что уточнить. В этой библиотеке нет понятия именованного блока. Как же разделить пространство действия переменных шаблона?

Для этого нужно воспользоваться конструкцией <TMPL_LOOP …>…</TMPL_LOOP>. Внутри этого «цикла» переменные-подстановки являются вложенными в цикл. Воспользуемся этим фактом.

Шаблон «list.html» для библиотеки HTML::Template:

<ol>

<TMPL_LOOP NAME="item">

<li><TMPL_VAR NAME="item"></li>

</TMPL_LOOP>

</ol>

Шаблон «ex_inc2.html» для библиотеки HTML::Template:

<html>

<head><title>Вложенные шаблоны</title></head>

<body>

<p>

Подключение одного шаблона в разных местах страницы и с разным заполнением.

</p>

Список 1:

<TMPL_LOOP NAME="first">

<TMPL_INCLUDE NAME="list.html">

</TMPL_LOOP>

Список 2:

<TMPL_LOOP NAME="second">

<TMPL_INCLUDE NAME="list.html">

</TMPL_LOOP>

</body>

</html>

Вся тонкость обработки сводится к корректному формированию массивов:

#!/usr/bin/perl

use strict;

# Путь к каталогу библиотеки

use lib $ENV{DOCUMENT_ROOT}."/../lib";

# Подключение библиотеки

use HTML::Template;

my $template = HTML::Template->new(

  filename => "ex_inc2.html",

  path => $ENV{DOCUMENT_ROOT}."/../lib/data/htmltemp/"

);

# Заполняем массив для шаблона list.html

my $items = ();

push(

  @{$items},

  ({"item" => "one"}, {"item" => "two"}, {"item" => "three"})

);

# Организуем обрамляющий массив для <TMPL_LOOP NAME=”first”>

my $list = ();

push(@{$list}, {"item" => $items});

# Ассоциируем значение

$template->param("first" => $list);

# Обнуляем и заполняем массивы заново

$items = ();

push(

  @{$items},

  ({"item" => "раз"}, {"item" => "два"}, {"item" => "три"})

);

$list = ();

push(@{$list}, {"item" => $items});

# Ассоциируем значение

$template->param("second" => $list);

print "Content-type: text/html ";

print $template->output();

__END__

Не так уж и страшно, не так ли? Но обратите внимание на следующий факт. При заполнении конструкции <TMPL_LOOP …> используется не массив, а ссылка на массив. Это важно!

Заключение

Выбирая движок для шаблонов, старайтесь оставить за шаблонами верстку (оформление), а логику – за программой. Не надо перекладывать на шаблоны логику выполнения и ни в коем случае не надо внедрять в шаблоны какие-либо объекты!

Все «расширенные» (в сравнении с обычным кодом HTML) возможности шаблонов должны заканчиваться на возможностях:

  • пометить точку для вставки (переменная шаблона);
  • пометить блок для выбора (именованный IF);
  • пометить блок для повтора (именованный LOOP).

И даже это избыточная модель! В PHP-ом XTemplate все еще более «доведено до ума»:

<ul><!-- BEGIN: menulist -->

<!-- BEGIN: link -->

<li><a href="{ITEM.URL}">{ITEM.NAME}</a></li><!-- END: link -->

<!-- BEGIN: active -->

<li>{ITEM.NAME}</li><!-- END: active -->

<!-- END: menulist --></ul>

И все!

У вас есть блок вида: <— BEGIN: имя —> …<— END: имя —>. На стороне программы вы можете повторить его нужное количество раз или выбрать из двух смежных тот, который нужно вывести, исходя из логики.

Для библиотеки Perl HTML::Template считайте именованным блоком конструкцию <TMPL_IF NAME=имя >… </TMPL_IF>. Все, что нужно повторить несколько раз, должно быть заключено в конструкцию вида <TMPL_LOOP NAME=имя> …</TMPL_LOOP>.

У вас есть место вставки вида {имя} или явная поблажка {имя_структуры.имя}.

И последняя конструкция – включение одного шаблона в другой. По принципу inline-подстановки: {FILE «имя»} или <TMPL_INCLUDE NAME=«имя»>.

Класс (движок) шаблонов должен уметь работать с вложениями блоков друг в друга и воспринимать каждый блок как отдельное пространство имен. Для HTML::Template это будет верно, если вы начнете рассматривать хэш, заполняющий цикл как ограничитель такого пространства.

Этого достаточно, чтобы сформировать любой вывод.

Класс (движок) шаблонов должен позволять рекурсивное включение файлов-шаблонов друг в друга.

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

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

Движок также не должен требовать предварительной компиляции шаблонов ни в какое промежуточное представление. Это позволит вносить изменения в оформление именно на уровне шаблонов и полностью отделить HTML-форматирование вывода от программной части.


Комментарии
 
  17.11.2013 - 07:02 |  pik

Не могу настроить Apache для работы с perl, чтоб это всё заработало. Можно было б и про это написать статью)

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru