Система управления шаблонами
Эта статья написана для начинающих
Описание проблемы
Есть много способов делать сайты. Но существует ряд рекомендаций, следуя которым можно построить легко масштабируемый и гибкий сайт (до определенных пределов, разумеется).
Рассмотрим пример не самого маленького сайта (на 100 страниц), которым занимается начинающий дизайнер. Он берет FrontPage, создает прототип будущей страницы, содержащий, например, шапку со ссылками, пустую область контента (основного содержимого), боковую панель со служебной информацией и нижнюю область с копирайтом (куда ж без него). Для каждой добавляемой страницы в такой шаблон вставляется контент и сохраняется под новым именем.
К «плюсам» этого подхода относится возможность автоматической генерации навигационных ссылок, например, на дочерние страницы, по заранее составленной схеме сайта. Во FrontPage есть удобный редактор схем, пользоваться которым — одно удовольствие. На этом плюсы и заканчиваются.
Принципиальные недостатки таковы:
- Качество создаваемого кода. FrontPage обладает ограниченными возможностями,
HTML-код станиц содержит слишком много мусора. - Избыточные расходы. Изменение содержания одной страницы приводит к изменению соседних страниц, вследствие чего увеличивается количество файлов, передаваемых и хранимых на сервере.
- Изменение дизайна сайта весьма затруднительно. В принципе, поиск и замена по всем файлам с использованием регулярных выражений может помочь делу, но при
значительных изменениях они не помогут.более-менее
Последний недостаток становится особенно явным, когда есть несколько разделов с частично отличающимся оформлением страниц.
Применение более совершенного редактора, например, Dreamweaver, кардинально проблему не решает. Необходимо использование качественно иных технологий.
Идеи
С моей точки зрения очень важным и продуктивным оказывается принцип минимизации избыточности данных, описанный в [2]. Согласно
этому принципу, любые данные, которые использует в своей работе сайт, должны храниться в единственном месте. Действительно, недостатки рассмотренного варианта возникают потому, что создается шаблон, для каждой создаваемой страницы в шаблон вставляется контент и сохраняется под новым именем. Таким образом,
Из этого принципа вытекает другой, широко используемый принцип разделения оформления и содержания. Я предлагаю использовать многоуровневое разделение: в одном месте (файлы или база данных) хранить контент, в другом — логическую структуру документа (код HTML), в третьем — визуальное оформление (стили CSS), в четвертом — программный код.
Я собираюсь реализовать эти идеи в простой системе шаблонизации.
— Для чего нужна собственная система, когда можно взять готовую, — спросите вы, — ведь вокруг столько свободно распространяемых систем. Отвечаю. PHP — настолько простой и мощный (огромным количеством встроенных функций) язык, что за промежуток времени, сравнимый со временем освоения сторонней системы, вполне реально написать
Шаблонизатор
Вариантов написания шаблонизатора много. Простейший из них описан в заметке Шаблоны в PHP для чайников [1], с которой я рекомендую ознакомиться. Более продвинутая система приводится в [2]. За основу мы возьмем первую заметку и доведем шаблонизатор до рабочего состояния.
Из каждого файла мы убираем все оформление, оставляя только меняющуюся от страницы к странице информацию. Договоримся, что такие файлы будут иметь расширение htm (разумеется, можно выбрать любое расширение). Мы хотим, чтобы файл *.htm мог содержать несколько блоков, например, заголовок, основное содержимое, дату создания и т. д. Для разметки блоков удобно использовать конструкции РНР; достоинства этого подхода будут указаны ниже. Мы введем специальный набор переменных, каждой из которых будем присваивать значение соответствующего блока. В [2] разметка осуществляется вызовами функций. Последний вариант сложнее в реализации, но несколько удобнее на практике, так как, например, если в тексте встречается апостроф, то нам его придется экранировать символом обратной наклонной черты: \'. Как показывает практика, такой символ появляется очень редко. И даже если вы случайно сделаете эту ошибку, ее легко отловить. Создаем файл 1.htm в директории dir1:
<?
$page_title="Страница 1";
$page_text='
<p>Текст страницы 1</p>
';
?>
В шаблоне в необходимых местах нужно вывести значения переменных. Он может выглядеть, например, так:
<html>
<head>
<title><?=$page_title?> - Раздел 1 - Суперсайт</title>
</head>
<body>
<?=$page_text?>
</body>
</html>
Каждому типу страниц должен соответствовать свой шаблон. Связывать шаблоны и страницы, вставляя в последние команды типа include "template.php" — не самая лучшая идея.
Выделяем для файлов шаблонизатора отдельную директорию, например, engine, помещаем в нее основной скрипт под именем main.php. В этой же директории создаем поддиректорию template для хранения шаблонов. В начале main.php необходимо проверить, не вызвал ли пользователь его напрямую из браузера. Вот вариант такой проверки, взятый из [2]:
$FileName = strtr(__FILE__, "\\", "/");
$ReqName = ereg_Replace("\\?.*","",
strtr($_SERVER['QUERY_STRING'],"\\","/"));
if (eregi(quotemeta($ReqName), $FileName)) {
echo "Попытка неавторизованного доступа!";
exit();
}
В переменной $ReqName хранится часть URL до знака вопроса, то есть это путь к файлу от корневой директории
Теперь самое время подумать, какой шаблон будет использоваться для того или иного файла. Я предлагаю для этого использовать часть URL. Например, все страницы вида http://www.server.ru/dir1/* имеют шаблон dir1, http://www.server.ru/dir2/* — dir 2 и т.д.
$dir_array = explode("/", $ReqName);
$page_type = $dir_array[1];
if ($page_type == "index.htm")
$page_type = "home";
В переменной $page_type мы выделили первую директорию в URL (например, "dir1" или "dir2"). При обращении к главной странице в ней будет храниться значение "home". В [2] сделано
Запоминаем текущую директорию (она еще понадобится в дальнейшем) и переходим в директорию, в которой находится файл.
$php_dir = getcwd()."/";
chdir("../".dirname($ReqName));
Если запрашиваемого файла нет, выдаем код ответа 404, соответствующую страницу ошибки и завершаем работу.
if (!is_file(basename($ReqName))) {
header("HTTP/1.1 404 Not Found");
include $php_dir."error404.html";
exit();
}
В принципе, уже можно вызвать файл и шаблон:
require basename($ReqName);
require $php_dir."template/".$page_type.".php";
Чтобы скрипт main.php вызывался автоматически, в файле .htaccess корневой директории
Action htm_handler "/engine/main.php?"
AddHandler htm_handler .htm
Однако, в зависимости от настроек сервера, main.php может вызываться или при наличии соответствующего *.htm файла (а при его отсутствии
ErrorDocument 404 /engine/error404.html
Для файла ошибки выбрано расширение html, чтобы он не обрабатывался нашим главным скриптом.
Какие достоинства у построенной нами системы?
- Применение конструкций PHP для разметки блоков избавляет нас от написания парсера — участка кода, обеспечивающего считывание и обработку. Подобный парсер, написанный на PHP и интерпретируемый им, будет в любом случае работать медленнее, чем встроенный скомпилированный парсер
PHP-кода. - Шаблонизатор содержит только необходимый нам код, ничего лишнего в нем нет.
- Наш шаблонизатор выводит страницы практически с максимально возможной для PHP скоростью.
- Функциональность шаблонизатора легко расширяется.
Для демонстрации последнего свойства рассмотрим возможность обработки текста страниц. Вызов шаблона необходимо окаймить функциями, буферирующими вывод:
ob_start();
require $php_dir."template/".$page_type.".php";
$out = ob_get_contents();
ob_end_clean();
После выполнения этих инструкций в переменной $out окажется все содержимое страницы, предназначенное для вывода в браузер. С ним можно проводить любые манипуляции. Например, заменим все встречающиеся теги <br> на <br />, как того требует стандарт XHTML:
$out = str_replace('<br>', '<br />', $out);
Теперь для экономии трафика и ускорения загрузки страниц применим
ob_start();
ob_start('ob_gzhandler');
echo $out;
ob_end_flush();
header('Content-Length: '.ob_get_length());
ob_end_flush();
Когда текст страницы формируется PHP, в ответе сервера нет таких
Заголовок
/* получаем время последнего изменения файла */
$mt = filemtime(basename($ReqName));
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $mt).'; GMT');
Я расскажу о том, какие еще возможности можно реализовать. Переменные файла *.htm легко прочитать инструкцией include. Их значения можно вывести в форму, а потом сохранить опять в файл операторами вида fwrite($f, "$page_text='".$page_text."';\n"). Несколько строчек PHP — и мы получили редактор страниц. Картину несколько портит возможная необходимость исполнения
Я ничего не сказал об организации шаблонов — файлов в директории engine/template. К ним тоже можно применить наш руководящий принцип и отделить повторяющиеся фрагменты.
Представленный шаблонизатор почти по всем параметрам лучше варианта с использованием FrontPage, о котором говорилось в начале статьи. Правда, в нем нет возможности автоматической генерации навигационных ссылок. К сожалению, эта проблема не имеет простого решения. Нужно организовать грамотное хранение структуры сайта, обеспечивающее как возможность чтения при выводе страниц, так и удобное редактирование при добавлении страниц или изменении структуры. Если возможность редактирования структуры будет реализована на PHP и если к ней добавить редактор страниц, о котором шла речь немного выше, то мы получим практически полноценную CMS — систему управления сайтом.
Путь построения правильного сайта намечен. Дело за малым — реализовать этот путь :)
Чтобы помочь вам, я собрал в архив файлы, упомянутые здесь. Это рабочий пример описанного шаблонизатора, который можно взять за основу и изменить так, как вам нужно.
Скачать шаблонизатор (2 Кб)
Литература
- Смирнов Д. В., Шаблоны в PHP для чайников.
- Котеров Д. В., Самоучитель PHP 4,
BHV-Санкт-Петербург, Гл. 30. Код и шаблон страницы.
Комментарии
А файл template.zip тогда где на самом деле? Неужели всё же в этой же папке?
Движок блога с использованием БД я переписал месяца два назад. Летом дойдет очередь и до сайта.
На
приличный сайт на php. Достоинство шаблонизатора — одинаковая работа на php4 и php5.
Спасибо. Vlad.
Я бы сказал, что Вы привели шаблонизатор до понятного состояния для тех, кто не очень в HTML. Основная задача данной статьи — показать, каким образом на PHP пишется шаблонизатор. Поэтому HTML и CSS были урезаны до минимума, как не относящиеся к теме.
Оставьте свой комментарий