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

Когда я начал создавать свой сайт, я уже немного был знаком с шаблонами, когда администрировал форум PHPBB. Шаблоны были там хоть и достаточно простыми, но использовался свой язык. Зато их можно было редактировать как обычные HTML файлы в визуальном редакторе, например Dreamweaver MX. Наверное, я так бы и воспользовался ими, если бы не наткнулся на статью Дмитрия Смирнова "Шаблоны в PHP для чайников". Она подтолкнула меня к правильному решению - зачем использовать новый язык шаблонов, если уже есть PHP.

Дело в том, что в HTML страницу можно делать php-вставки.

Вставка <? code ?> выполняет код, заключенный между скобками и результат (если он есть) вставляет в html-код.

Вставка <?= expr ?> вычисляет выражение expr и вставляет его в html-код.

Например, если мы хотим, чтобы заголовок страницы проставлялся из php-переменной $p_title, можно было бы написать в html-файле <TiTLE> <?= $p_title ? > </TiTLE>.

Можно усложнить задачу, чтобы если переменная не определена, выводить в заголовок NONAME: <TiTLE> <? if (empty($p_title)) {? > NONAME <? } else echo $p_title ?></TiTLE>. Как видно, можно вставлять даже операторы php, вовремя входя и выходя из HTML. В нашем случае, если переменная $p_title, выполнится блок HTML между фигурными скобками {}, а если определена, то по ветке else сработает вывод командой echo.

И главное - весь этот код прекрасно смотрится в любом визуальном HTML-редакторе. Мы видим как надпись NONAME, так и php-скрипт в виде иконки (Dreamweaver).

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

Функция evaltos($str) - вычисляет шаблон, заданный в строке $str и возвращает html-код в виде строки.

Функция filetos($filename) - считывает шаблон, заданный в html-формате в файле $filename в строку и возвращает ее.

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

Пример - горизонтальное меню

Пусть нам нужно нарисовать горизонтальное меню. Каждый пункт меню поименован и ему соответствует определенная ссылка. Выбранный пункт меню выделен особым цветом и на нем ссылка отсутствует:

Используем шаблоны!

Сформируем в php массив $p_items, каждый элемент которого будет ассоциативным массивом, содержащим три элемента: name - имя ссылки, link - адрес перехода по ссылке и selected - выбрана ли ссылка.

Запишем шаблон рисования меню в файл hmenu.htm:

<table width="100%" border="0" cellpadding="1" cellspacing="0" bgColor="#0033cc">
<tr >
<? for ($i=0; $i<count($p_items); $i++)
{
$name=$p_items[$i]['name'];
$link=$p_items[$i]['link'];
$selected=$p_items[$i]['selected'];
?>
<td noWrap bgColor="<?= ($selected)? "#CCCCCC" : "#0033cc" ?>"
<? if (!$selected) { ?>
<a <? if ($external) { ?> target ="_blank" <? } ?> href="<?=$link ?>"> <?=$name?> </a>
<? } else { ?>
<?=$name?>
<? } ?>
</td>
<? } ?>
</tr>
</table>

Прочитаем шаблон в переменную $pat_hmenu=filetos("hmenu.htm").

Вычислим шаблон и результат запишем в переменную $p_hmenu=evaltos($pat_hmenu).

Как вычисляется шаблон? Меню представляется в виде таблицы. Запускается цикл по всем элементам $p_items и для каждого элемента формируется ячейка таблицы в зависимости от того, выбрана она или нет, подбирается цвет и вставляется ссылка. Код достаточно прозрачен и весь просматривается в любом визуальном редакторе.

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

Но это еще не все, вложенные шаблоны просты как никогда.

Запишем шаблон всей страницы в файл index.htm:

<body>
ПРОСТАЯ СТРАНИЧКА, ТОЛЬКО МЕНЮ <BR>
<?=$p_hmenu ?>
</body>

Прочитаем шаблон страницы в переменную $pat_page=filetos("index.htm").

Вычислим шаблон и результат запишем в переменную $p_page=evaltos($pat_hmenu).

Выведем наконец страницу в браузер командой echo $p_page, вот что получается в браузере:

Как видите, вложенные шаблоны не являются чем-то особенным. Готовые части страницы можно хранить в строках и вставлять по мере надобности.

Особенности реализации

Замечание по безопасности. В шаблонах при выводе используются глобальные переменные. Если вы забудете проинициализировать некоторые из них, то их можно переназначить с помощью GET или POST, поэтому после окончания считывания переменных из GET или POST я сразу же очищаю все глобальные переменные, установленные из GET или POST. Конечно, можно указать PHP, чтобы они просто не создавались, но я не знаю как использовать эту опцию. :)

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

Исходный код

Исходный код очень прост, но чтобы его написать, пришлось хорошенечко потрудиться.

В функции evaltos вывод перенаправляется в строку. Затем, прежде чем вычислить шаблон в функции eval, происходит выход из php в html и в конце опять вход в php. И кроме того - происходит копирование всех глобальных переменных в локальные, иначе они недоступны в функции, а писать $Globals[$p_name] не очень удобно.

В функции filetos используется для чтения функция file, которая считывает файл как массив строк. Затем мы объединяем эти строки в одну длинную строку шаблона.

function evaltos($str)
{
reset($GLOBALS);
while (list($var,$value)=each($GLOBALS))
$$var=$value;
ob_start(); //Output all what we have
eval(" ?>$str<?php ");
$s=ob_get_contents(); //
ob_end_clean();
return $s;
}

function filetos($filename)
{
return implode("",file($filename));
}

function clearPostGet()
{
$get_vars=$GLOBALS['HTTP_GET_VARS'];
$post_vars=$GLOBALS['HTTP_POST_VARS'];

if (!empty($get_vars))
{
reset($get_vars);
while (list($var,$value)=each($get_vars))
$GLOBALS[$var]="";
}

if (!empty($post_vars))
{
reset($post_vars);
while (list($var,$value)=each($post_vars))
$GLOBALS[$var]="";
}
}

(С) Осипов Сергей aka fixin, 2004. Перепечатка без ссылки на автора запрещена!