В
этой статье я хотел бы рассказать о применении шаблонов в программировании
для WEB.
Материалов об этом довольно много в сети, но почему-то просматривая всевозможные
бесплатные или демо скрипты, написанные как профессионалами, так и любителями,
я практически не встречаю среди этих текстов использование шаблонов. В
связи с этим обстоятельством я решил написать данную статью. Может быть
прочитав этот материал вы откроете для себя, пока еще не понятное слово
template (шаблон), или расширите свои знания и начнете более активно использовать
их на практике. В любом случае думаю материал полезный для всех кто интересуется
разработкой WEB-приложений. Для программирования я буду использовать язык
Perl, гибкий и мощный, он идеально подходит для работы с текстом (а шаблоны
и есть обычный текст), а также адаптированный под огромное количество
платформ, мне он кажеться наиболее удобным, хотя поклонники PHP, тоже
могут для себя что-то открыть из этой статьи.
[INTRO]
Прежде чем начать писать эту статью, я долго пытался представиь
себе форму изложения материала. И как мне показалось нашел ее - написание
мощного приложения с использованием шаблонов, а конкретней - гостевой
книги, с разграничением доступа на пользователей, модераторов и администраторов,
с использованием SQL для хранения данных и все в этом роде. Я активно
взялся за статью, но "чем дальше в лес, тем больше дров" наломал
:-), в общем, когда статья близилась к завершению понял что читать ее
будет абсолютно невозможно человеку, чей уровень знаний perl средний и
ниже. А читать профессионалу это было бы неинтересно, по причине того
что все это ему должно быть известно. Я отказался от разработки громоздкого
интерактивного приложения и решил значительно упростить задачу, и более
наглядно показать работу с шаблонами.
[PART I]
А для чего?
Первый и наверно вплоне резонный вопрос - а зачем мне это
надо? Я ведь до этого свободно обходился безо всяких замут и писал себе
свои:
[CODE]
#!/usr/bin/perl -w
print "Content-Type: text/html\n\n";
print qq(<B><CENTER>HEllo world!</CENTER></B>);
[/CODE]
И не парился...
Но если у вас более сложный скрипт :-), например какой-нибудь интерфейс
работы с базами данных, или система электронного документооборота, или
даже просто гостевая книга для которой вы бы хотели сделать поддержку
скинов? Как бы вы решили эту задачу? В случае со скинами - в зависимости
от выбора пользователя грузили бы разные страницы HTML? Или все бы это
вписали бы в свой скрипт, как вложенный документ (конструкция print <<HTML;
...)? Не думаю что это красивое решение. Красивое решение в данном случае
будет - использование шаблонов.
Работать с шаблонами на Perl, можно как всегда - "более чем одним
способом". Можно использовать модуль HTML::Template (о его использовании
я напишу в [PART II]), а можно написать свою маа-а-аленькую функцию, которая
будет отвечать нашим скромным потребностям. Модуль HTML::Template предоставляет
разработчику широкий выбор методов, это и вложенные шаблоны, и конструкции
IF,ELSE, и циклы, но о нем - позже.
Я напишу скрипт, который будет формировать html-страницу,
в зависимости от выбора пользователем понравившегося ему скина (в скрипте
я не буду использовать работу с cookie-файлами, для запоминания выбора
пользователя, так как считаю этот вопрос опять же выходящим за рамки темы).
Наберите следующий HTML-код, и сохраните файл под именем skin.htm (имя
может быть любым, как и расширение файла)
[HTML]
<html>
<head>
<title>TMPL_TITLE</title>
<meta http-equiv="Content-Type" content="text/html;
charset=windows-1251">
<link href="TMPL_CSS" rel="stylesheet"
type="text/css">
</head>
<body>
<table width="100%" height="287" border="0">
<tr>
<td colspan="2">
<div align="center">
<img src="TMPL_IMG" border="0">
</div>
</td>
</tr>
<tr>
<td width="14%" align="center" valign="top"><p><a
href="http://localhost/some.cgi?param=drink">1. Пойти упиться</a></p>
<p><a href="http://localhost/some.cgi?param=marry">2.
Пойти жениться</a></p>
<p><a href="http://localhost/some.cgi?param=love">3.
Пойти влюбиться</a></p>
<img src="TMPL_BANNER" width="75"
height="75" border="0">
<p> </p></td>
<td width="86%" align="center">
Выберите skin из ниспадающего меню :-)
<form method="post" >
<select name="select" size="1">
<option value="1">Horror</option>
<option value="2">Lucky</option>
<option value="3">Crazy</option>
</select>
<input type="submit" value="Выбрать">
</form></td>
</tr>
</table>
</body>
</html>
[/HTML]
Только что мы создали шаблон, или template, на основе которого
скрипт будет формировать нашу страничку. Дизайн конечно ужасен, это я
и сам(!) понимаю :-)). Некоторые тэги в этом файле вам покажуться странными
(я их выделил жирным шрифтом), но читайте дальше и вы поймете их предназначение.
Шаблон есть, теперь напишем сам скрипт, из-за которого мы замутили этот
skin.htm.
Наберите следующий код и сохраните под именем skin.cgi
[CODE]
#!/usr/bin/perl -w
use strict;# включаем строгий контроль синтаксиса
use CGI qw(param header);# подключаем модуль (импортируем только используемые нами методы),
для уменьшения объема кода, вы можете создать стандартые функции обработки POST и GET запросов, чтобы обойтись без него.
Хотя, его применение, меня в 8 случаях из 10 устраивает.
my (%Horror,%Lucky,%Crazy);# объявляем хэши в которых будем хранить параметры соответствующих скинов.
my $path="http://localhost/files";# путь к включаемым файлам и картинкам.
my $rand=int(rand(4));# выбираем рандомное число, совпадающее с именем баннера (это для баннерокрутилки :-)
# Заполняем хэши
%Horror=('TITLE' => "HORROR-SKIN",
'CSS' => "$path/horror.css",
'IMG' => "$path/horror.jpg",
'BANNER' =>"$path/$rand.jpg");# отображаем рандомный баннер
%Lucky=('TITLE' => "LUCKY-SKIN",
'CSS' => "$path/lucky.css",
'IMG' => "$path/lucky.jpg",
'BANNER' =>"$path/$rand.jpg");
%Crazy=('TITLE' => "CRAZY-SKIN",
'CSS' => "$path/crazy.css",
'IMG' => "$path/crazy.jpg",
'BANNER' =>"$path/$rand.jpg");
# Функция обработки шаблона
sub template {
my $options;#объявляем переменную в которой будем хранить ссылку на хэш скина
$options=shift;# получаем ссылку на нужный хэш (shift - возвращает первый элемент массива @_)
open(MAIN,"<","../../htdocs/files/skin.htm") or return "Не могу открыть файл шаблона - проверьте пути к нему";
# открываем наш шаблон либо возвращаемся с сообщением об ошибке
{ local $/; $_=<MAIN> }# в этой строке используем локализованный разделитель записей внутри нового блока
# для импортирования целого файла в пространство переменной $_
close(MAIN);# закрываем файл шаблона
for my $key(keys %$options)
{ # обрабатываем переменную $_, делая замены для соответствующих полей, выделенных нами в шаблоне skin.htm.
# теперь я думаю вам стало понятен синтаксис этих записей в шаблоне.
s/TMPL_$key/$options->{$key}/;# для получения значения ключа хэша используем "->" оператор,
так как $options - является ссылкой на хэш, и требует разыменовывания, если бы мы использовали действительный хэш,
то использовали бы запись $options{$key}
}
return $_;# возвращаем обработанный HTML
}
########## Тело скрипта ######################
if(param('select') eq '2') {
print header(-charset=>'windows-1251');
print template(\%Lucky);# скин Lucky
exit 0;
}
if(param('select') eq '3') {
print header(-charset=>'windows-1251');
print template(\%Crazy);# скин Crazy
exit 0;
} else {# по умолчанию выводим скин Horror
print header(-charset=>'windows-1251');# выводим заголовок
print template(\%Horror);# по умолчанию выводим скин Horror (передаем ссылку на хэш в функцию)
}
__END__
[/CODE]
Я специально отделил комментариями тело скрипта, чтобы показать,
как удобно работать (а главное компактно и прозрачно), с шаблонами. Все
что было описано до # Тела скрипта #, можно было вынести с отдельный файл
+ наполнение хэшей автоматизировать, забирая их параметры из dbm-файла,
или парся обычный txt.
А теперь представьте себе код скрипта, реализующий тоже самое, но без
использования шаблонов. Он бы был гораааздо более объемистым :-). Плюс
мы не навязчиво прикрепили баннерокрутилку к нашей страничке :-). И помимо
компактности кода, мы получили куда более гибкий скрипт, нежели скрипт
содержащий включенные документы HTML. Чем же он гибок? Во-первых, мне
никогда не нравилось мешать программный код и дизайнерскую часть. За программой
должна оставаться только логика, но не вывод дизайна. Например, над этой
страничкой со скинами работали вы и ваш друг - вэб-дизайнер. Он ничего
не понимает в Perl, но круто рубит в хтмл, CSS и т.п. Для того чтобы изменить
дизайн он должен создать новую страничку, отдать ее вам, вы ее втисните
в ваш скрипт (в случае использования включенных документов). Если вы используете
шаблон, то вы можете дать ему досутп к фтп, он будет только перезаливать
файлы шаблонов и тут же просматривать результат. Скрипт будет по прежнему
работать так же как и в начале. Ему абсолютно все равно какой HTML-код
вы в него запихиваете. Или рассмотрим пример когда вам необходимы SSI
включения, но ваш хостер запретил доступ к SSI, шаблоны являются прекрасной
альтернативой технологии SSI.
Как правило шаблоны представляют собой небольшое кол-во информации, поэтому
их вывод не занимает много процессорного времени и памяти.
В первом примере есть один недостаток, довольно существенный. В случае
если в вашем шаблоне имеется параметр повторяющийся несколько раз, то
замена будет произведена только при первом вхождении этого параметра.
Например, TMPL_URL - представляет собой url вашего сайта, и в шаблоне
эта метка встречается более 1 раза, замена будет произведена только в
первом случае, так как функция template подставляет поочередно ключи хэша,
и не возвращается к уже пройденным ключам. Можно усовершенствовать нашу
функцию, но лучше давайте перейдем к рассмотрению такого замечательного
модуля для работы с шаблонами, как HTML::Template.
[PART II]
HTML::Template - модуль доступен для скачивания на сайте
http://html-template.sourceforge.net или в CPAN. Также он приаттачен к
этой статье. Его подключение к скрипту элементарно, достаточно поместить
его в одну директорию (или поддиректорию) с вашим скриптом, и указать
в скрипте директиву use lib qw(.) # в случае если модуль лежит в той же
директории что и скрипт, и модуль будет доступен для использования в скрипте.
Если вам нехватает возможностей функции рассмотренной в предыдущем примере,
читайте дальше - возможно этот модуль решит ваши проблемы. Мы же рассмотрим
кое-какие полезные методы этого модуля на том же примере страницы с разными
скинами. Для этого перепишем наш предыдущий пример с использованием HTML::Template
и немного расширим его функциональность.
Создадим новый шаблон, лишь немного отличающийся от шаблона из предыдущего
примера.
Назовем файл skin-tmpl.htm и напишем в него следующий html-код
[HTML]
<html>
<head>
<title><TMPL_VAR NAME=TITLE></title>
<meta http-equiv="Content-Type" content="text/html;
charset=<TMPL_VAR NAME=CHARSET>">
<link href="<TMPL_VAR NAME=CSS>" rel="stylesheet"
type="text/css">
</head>
<body>
<table width="100%" height="287" border="0">
<tr>
<td colspan="2">
<div align="center">
<img src="<TMPL_VAR NAME=IMG>" border="0">
</div>
</td>
</tr>
<tr>
<td width="14%" align="center" valign="top"><p><a
href="<TMPL_VAR NAME=URL>/param=drink">1.
Пойти упиться</a></p>
<p><a href="<TMPL_VAR NAME=URL>/param=marry">2.
Пойти жениться</a></p>
<p><a href="<TMPL_VAR NAME=URL>/param=love">3.
Пойти влюбиться</a></p>
<img src="<TMPL_VAR NAME=BANNER>"
width="75" height="75" border="0">
<p> </p></td>
<td width="86%" align="center">
Выберите skin из ниспадающего меню :-)
<form method="post" >
<select name="select" size="1">
<TMPL_LOOP NAME=CHOOSE>
<option value="<TMPL_VAR NUM>"><TMPL_VAR
NAME></option>
</TMPL_LOOP>
</select>
<input type="submit" value="Выбрать">
</form></td>
</tr>
</table>
</body>
</html>
[/HTML]
Как вы должны были заметить, объявление меток в шаблоне изменилось с TMPL_SOMEKEY
на <TMPL_VAR NAME="SOMENAME">, а также появились конструкции
типа <TMPL_LOOP NAME=""></TMPL_LOOP>. Если о первых
более менее понятно, то о последних скажу отдельно. Эта конструкция сообщает
скрипту о начале и завершении цикла, в процессе исполнения которого, скрипт
будет заполнять html-код, содержащийся между этими конструкциями, элементами
массива ассоциированного с этой конструкцией. Пока все это еще не совсем
понятно, думаю пример скрипта внесет ясность. Назовем скрипт skin-tmpl.cgi
[CODE]
#!/usr/bin/perl -w
use strict;
use lib qw('modules');# ищем модуль HTML::Template в папке modules (которая должна быть расположена в текущей)
use CGI qw(param header);
use HTML::Template;# подключаем модуль
#####################################
my $skin=param('select') || 1;# принимаем параметры из формы или присваиваем дефолтное значение 1
$skin=~ s/[^1-3]//;# фильтрация переменных (возможны значения от 1 до 3, все остальное удаляется)
$skin=1 unless($skin=~/[1-3]/);# если после фильтрации переменная не содержит ничего полезного :-) - присваиваем дефолт
#####################################
my $options=get_skin($skin); # инициализируем переменную ссылкой на нужный хэш
my $choose=get_list();# инициализируем переменную ссылкой на массив хэшей
########## формируем шаблон ################
my $template=HTML::Template->new(
filename => "skin-tmpl.htm",# имя файла шаблона
path => "../../htdocs/files" # путь к файлу шаблона
);
$template->param(TITLE => $options->{'TITLE'}); # делаем подстановки в шаблоне
$template->param(CHARSET => $options->{'CHARSET'});
$template->param(CSS => $options->{'CSS'});
$template->param(URL => $options->{'URL'});
$template->param(IMG => $options->{'IMG'});
$template->param(BANNER => $options->{'BANNER'});
$template->param(CHOOSE => $choose);# соотносим блок TMPL_LOOP со ссылкой на массив хэшей
#######################################
###### выводим на печать шаблон #######
print header(-charset=>$options->{'CHARSET'});
print $template->output();
####################################### функции ##################################
sub get_skin {
my $param=shift;# принимаем параметр для определения запрошенного хэша
### далее код почти целиком заимствован из предыдущего примера
my (%Horror,%Lucky,%Crazy);# объявляем хэши в которых будем хранить параметры соответствующих скинов.
my $path="http://localhost/files";# путь к включаемым файлам и картинкам.
my $rand=int(rand(4));# выбираем рандомное число, совпадающее с именем баннера (это для баннерокрутилки :-)
# Заполняем хэши
%Horror=('TITLE' => "HORROR-SKIN",
'CSS' => "$path/horror.css",
'IMG' => "$path/horror.jpg",
'CHARSET' => "windows-1251",
'URL' => "http://mazafaka.ru",
'BANNER' => "$path/$rand.jpg");# отображаем рандомный баннер
%Lucky=('TITLE' => "LUCKY-SKIN",
'CSS' => "$path/lucky.css",
'IMG' => "$path/lucky.jpg",
'CHARSET' => "windows-1251",
'URL' => "http://akafazam.ru",
'BANNER' => "$path/$rand.jpg");
%Crazy=('TITLE' => "CRAZY-SKIN",
'CSS' => "$path/crazy.css",
'IMG' => "$path/crazy.jpg",
'CHARSET' => "windows-1251",
'URL' => "http:/fakamaza.ru",
'BANNER' => "$path/$rand.jpg");
# возвращаем ссылку на хэш, в зависимости от номера запрошенноо скина
return \%Horror if $param==1;
return \%Lucky if $param==2;
return \%Crazy if $param==3;
}
# функция заполнения массива хэшей значениями
sub get_list {
my $menu=();# объявляем переменную (которую в дальнейшем будем использовать как ссылку на массив
# вносим в массив 3 анонимных хэша.
push (@{$menu},({"NUM" =>1,"NAME"=>"Horror"},{"NUM" =>2,"NAME"=>"Lucky"},{"NUM" =>3,"NAME"=>"Crazy"}));
return $menu;# возвращаем ссылку на массив
}
__END__
[/CODE]
В коде даны подробные комментарии. Работать с модулем как вы видите довольно
просто. Он производит рекурсивные замены меток, что не делала наша функция
в первом примере. Здесь я специально ввел метку <TMPL_VAR NAME=URL>
- чтобы продемонстрировать это. В примере представлены конечно же не все
возможности этого модуля, но наиболее важные и сложные (в случае с TMPL_LOOP).
В модуле также имеются методы:
- Условия
<TMPL_IF>
...
<TMPL_ELSE>
...
</TMPL_IF>
<TMPL_UNLESS>
...
<TMPL_ELSE>
...
</TMPL_UNLESS>
- Подключения более мелких шаблонов
<TMPL_INCLUDE NAME="filename.txt">
И прочее. Все вы их можете изучить через perldoc HTML::Template.
Там же имеются и примеры их использования. Документация по модулю более
менее ясная и подробная. Модуль имеет также некоторые расширенные вариации,
которые имеют больше методов, включая использование регулярных выражений
в шаблонах. Но мне кажеться это лишним, ведь логику я предпочитаю оставлять
за скриптом, чего и вам советую. И еще хотел бы посоветовать - используйте
этот модуль если он вам действительно необходим, в противном случае используйте
приведенную мной функцию для работы с шаблонами. Не забывайте модель DWIM
("Делать то что задумано") и не перегружать свои скрипты вызовами
излишних модулей.
[OUTRO]
Надеюсь эта статья была для вас полезной, и у вас появилось
желание самому что-нибудь написать. Что ж не буду вас больше задерживать,
упомяну лишь еще об одном модуле для работы с шаблонами - Template::Toolkit
(или в среде perl-программеров просто TT2). Это чрезвычайно мощное средство
для работы с шаблонами, но и довольно сложное, поэтому в этой статье я
не касался его. Возможно в следующий раз мы поговорим о нем. |