Написать эту статью меня заставили попытки изменить один бесплатный скрипт-CGI гостевой книги под требования некого сайта. Нужно было сделать с ним - это везде изменить слова "гостевая книга" на "книга отзывов", и прописать определенные строки по центру. Но в даном скрипте оказалось четыре (!) блока вывода - один только для штатной ситуации ("Ваша запись была успешно добавлена...") и три - только для вывода разнообразных ошибок (отсутствует E-Mail, имя или текст). При этом текст данных блоков довольно крепко прописан в операторах print. Ясное дело, что пришлось попотеть.
Большинство хостеров дают веб-программисту предустановленные скрипты - различные гостевые книги, счетчики, доски объявлений и тому подобное. При этом у некоторых хостеров (а именно, TvoyHosting.com) данные скрипты очень хорошо настраиваются. Даже несмотря на то, что у мастера-веб, обычно, нет возможности редактировать код данных скриптов, веб-мастера могут изменить дизайн выводимых данными скриптами страничек, редактируя скрипты под дизайн Вашего сайта.
В данной статье разбирается суть шаблонного вывода, его преимущества использования и рассматривается, как организовать на практике данный вывод в Ваших скриптах на языке Perl, используя сильное средство данного языка - регулировка выражений.
Шаблон вывода обычно представляет текстовый файл, в котором заготовка странички-HTML, которую выводит скрипт. Если скрипт выводит несколько видов страничек ( например, форма добавления записи и страница с содержимым гостевой), для всех из них положено быть своему шаблону. При работе скрипт читает определенный шаблон, заполняет его данными и показывает клиенту.
Настройка скрипта под конкретный сайт сводится к редактированию этих самых шаблонов, представляющих по своей сути HTML-страницы. Это дает несколько преимуществ по сравнению с редактированием кода:
1. настроить скрипт может любой, который не писал такой скрипт и не знает его "тонкостей", да и вообще не шарит в Perl-программировании; :-)
2. при настройке просто невозможно внести в код скрипта синтаксические ошибки, которые потом придется поискать;
3. наглядность редактирования, возможность просмотреть примерный результат сразу в браузере; к тому же, у web-мастера обычно уже есть макет страниц, в который он вставляет содержимое;
4. один скрипт можно использовать с разными вариантами вывода, "подставляя" ему различные шаблоны.
Данным образом, организуя в скриптах вывод на основе шаблонов, Вы тем самым заранее позаботитесь о том, чтобы однажды написанный Вами скрипт можно было впоследствии без осоього труда "подогнать" под необходимый сайт.
В некоторых скриптах, не использующих шаблоны, есть текстовые файлы, содержащие "верх" и "низ" страницы. Скрипт считывает такие файлы, вставляет между ними свой вывод ("зашитый" в скрипте) и выдает страницу клиенту. Это позволяет разрешить много трудностей с "подгонкой" скрипта, но не позволяет изменить формат самого вывода. Поэтому я считаю шаблоны более универсальным средством.
Для того, чтобы скрипт "знал", в какие места шаблона какаие данные нужно пихать, шаблон содержит определенные метки - зарезервированные последовательности символов. Перед выдачей страницы скрипт заменяет такие последовательности конкретными данными. В качестве меток чаще применяются последовательности букв, "обрамленные" слева и справа хостинг для joomla определенными последовательностями символов, которые ведомо не могут встречаться в тексте HTML-шаблона. Часто такие метки делают на манер HTML-тэгов, например:
<--MESSAGE-->, <-NAME-> и т.п.
Это необязательно, хотя психологически привычнее. Далее в данной статье я буду употреблять метки вида <-XXX->, где XXX-последовательность символов, которую будем называть "имя метки".
В Perl есть довольно полезные средства для обработки шаблонов - операторы поиска и замены и регулярные выражения.
Предположим, что создаем скрипт добавления записи в гостевую книгу, который выдает пользователю страничку подтверждения, содержащую вбитые пользователем данные (имя, страна проживания, адрес e-mail и сообщение).
Простейший блок вывода такого скрипта может выглядеть так:
#Читаем файл шаблона open TF,"<template.txt"; sysread TF,$t,-s TF; close TF; $t=~s/<-NAME->/$name/g; #Подставляем значение <-NAME-> $t=~s/<-EMAIL->/$eml/g; #Подставляем значение <-EMAIL-> $t=~s/<-COUNTRY->/$ctry/g; #Подставляем значение <-COUNTRY-> $t=~s/<-MESSAGE->/$msg/g; #Подставляем значение <-MESSAGE-> print "Content-Type: text/html\n\n"; print $t;
Здесь предполагается, что в файле template.txt хранится шаблон вывода, а в переменных $name, $eml, $ctry, $msg хранятся определенные значения полей. Четыре оператора замены заменяют метки на определенные им значения. Параметр g означает "глобальная замена", т.е. каждая такая метка будет заменена определенным значением по всему шаблону, сколько бы раз она не встречалась.
Данным образом, шаблон может выглядеть, примерно, так:
<HTML> <HEAD> <TITLE><-NAME->, Ваша запись была успешно добавлена</TITLE> </HEAD> <BODY> <B><-NAME->, Ваша запись была успешно добавлена в Гостевую книгу.</B> <HR> <P>Вы ввели следующие данные: <P><B>Имя:</B> <-NAME-> <p><B>E-Mail:</B> <A href="mailto:<-EMAIL->"><-EMAIL-></A> <P><B>Страна:</B> <-COUNTRY-> <P><B>Текст сообщения:</B><I><-TEXT-></I> </BODY></HTML>
В этом шаблоне метка <-NAME-> встречается три раза, и везде при выводе будет заменена именем человека, оставившего запись.
Несколько необычно выглядит метка <-EMAIL-> внутри тэга <A href=...>. Однако метки можно вставлять в ЛЮБОМ МЕСТЕ HTML-документа, т.к. они будут заменены значениями ДО того, как документ будет выдан клиентской программе.
Теперь рассмотрим ситуацию, с которой можно столкнуться при создании реальных скриптов.
Допустим, мы хотим, чтобы значение <-NAME-> было ссылкой на E-Mail человека - чтобы, щелкнув по имени, можно было написать ему письмо.
Для этого можно изменить одну строчку рассмотренного выше шаблона следующим образом:
<P><B>Имя:</B><A href="mailto:<-EMAIL->"><-NAME-></A>.
Но в этом случае, если человек не ввел свой E-Mail, ссылка будет "пустая" и это будет выглядеть несколько некрасиво. Если мы хотим, чтобы имя человека служило ссылкой только в том случае, если он ввел адрес E-Mail, а в противном случае было просто текстом, то это надо сделать в самом Perl-скрипте - чтобы переменная $name уже содержала ссылку, если это нужно.
#"Художественно обрабатываем" имя для вывода
if ($eml ne ""){$name="<A HREF=\"mailto:$eml\">$name</A>"};
Однако, в двух других случаях использования метки <-NAME-> (в тэге <TITLE> и сообщении о добавлении записи) нам в любом случае нужен текст, без ссылки. Для этого надо для разных потребностей "завести" две разные метки, скажем, <-NAME-> для "текстового" имени и <-A_NAME-> для ссылки.
Вообще, если какие-то введенные значения обрабатываются "для красоты", предпочтительно иметь два набора значений - "обработанных" ("высокого уровня") и "необработанных" ("низкого уровня"), т.к. "голые", необработанные введенные значения всегда могут пригодиться.
Теперь рассмотрим скрипт гостевой книги, выводящий ее содержимое. Задать вывод этого скрипта в виде шаблона не так просто, т.к. гостевая книга может содержать разное количество сообщений.
Здесь нам понадобятся уже два шаблона - отдельно шаблон страницы и шаблон записи.
open TM,"<msg.txt"; # Загружаем шаблон сообщения
sysread TM,$tm,-s TM;
close TM;
open TP,"<page.txt"; # Загружаем шаблон страницы
sysread TP,$tp,-s TP;
close TP;
#В массивах $name[], $eml[], $text[] хранятся имя, электронный адрес
#и собственно оставленное сообщение. В переменной $num-кол-во
#сообщений.
$m=""; #Инициализируем переменную для сообщений
for ($i=$num;$i<=0;$i--){
$m1=$tm;
$m1=~s/<-NAME->/$name[$i]/g;
$m1=~s/<-EMAIL->/$eml[$i]/g;
$m1=~s/<-TEXT->/$text[$i]/g;
$m=$m.$m1; #Формируем список сообщений
};
#
$p=$tp;
$p=~s/<-MESSAGES->/$m/g;
#
# ... Подстановка других меток, если нужно
#
print "Content-Type: text/html\n\n";
print $p;
В шаблоне page.txt хранится общий вид выдаваемой страницы. В том месте, где скриптом должны быть вставлены сообщения, стоит метка <-MESSAGES-> На это место будет вставлено нужное количество сообщений, сформированных по шаблону msg.txt.
Бывают случаи, когда на момент написания скрипта неизвестен ни набор, ни количество меток, которые надо заменять значениями в выходных страницах. Допустим, скрипт, обрабатывающий несколько почтовых форм, в разных случаях имеет дело с разными полями разного содержания.
Соответственно, замена меток с "жесткими" именами, как мы делали до этого, в таких случаях невозможна, однако выход есть. Предположим, что набор параметров вывода лежит в хэше %PARAM; тогда блок вывода можно сделать так:
open TF,"<template.txt";
sysread TF,$t,-s TF;
close TF;
#
# ... Замена "жестких" меток, если необходимо
#
# А теперь меняем динамические метки
$t=~s/<-(.*)->/$PARAM{}/gie;
print "Content-Type: text/html\n\n";
print $t;
Если в процессе замены "динамических" меток в шаблоне будут встречены метки, для имен которых нет значений в хэше, на их место будет подставлено нулевое значение (метка просто исчезнет). Поэтому метки с "жесткими" именами надо обрабатывать до меток с динамическими именами.
Многие сайты используют SSI для вставки единых элементов оформления на все страницы. Это дает возможность быстрой полной смены дизайна при необходимости.
Как известно, в сгенерированных скриптами страницах директивы SSI не работают, а следовательно, не будут они работать и в шаблонах вывода CGI скриптов. Однако некоторые функции SSI можно "поручить" самим скриптам, благодаря чему в шаблонах можно будет использовать ссылки на те же общие фрагменты, которые используются в HTML-страницах через SSI.
Для этого можно предусмотреть метки специального вида, вместо которых скрипт будет вставлять содержимое файла с хостинга. Возможна даже обработка меток стандартного для SSI-директив вида!
В большинстве случаев достаточно обработки наиболее часто используемой директивы <!--#include virtual="..."-->
Для этого в блоке вывода можно предусмотреть приблизительно такой код:
$base="/usr/home/site"; #"Базовый" путь к сайту.
#Загружаем файл шаблона
open TF,"<template.txt";
sysread TF,$t,-s TF;
close TF;
#Обрабатываем "инклюды"
$t=~s/<!--#include virtual="(.*)"-->/getfile()/ge;
#
# ... Здесь, если нужно, обрабатываем другие метки
#
#выводим страницу клиенту
print "Content-Type: text/html\n\n";
print $t;
sub getfile{
if (substr($_[0],0,1) eq "/"){$fn=$base.$_[0];}
else {$fn=$_[0];};
open F,"<$fn";
sysread F,$t,-s F; close F; return $t;}
В этом фрагменте оператор ~s заменяет определенную вышеуказанной SSI-директиве конструкцию на значение функции getfile, которая возвращает содержимое нужного файла. Аргументом для функции служит путь к файлу, "извлекаемый" из "SSI-директивы". Если этот путь начинается со слэша '/' ("корневая папка сайта"), то в его начало добавляется базовый путь к сайту из переменной $base. Параметр e оператора ~s указывает на то, что заменяемая строка является функцией, значение которой надо использовать для замены.
Таким образом, можно использовать одни и те же включаемые файлы как для статичных страниц, так и для результатов работы CGI-скриптов, поддерживая таким образом структурную целостность вашего сайта.
