Наверняка Вы часто замечали, что в пределах одного сайта все (или покрайней мере большинство) страниц имеют много общего: структуру, расположение элементов, дизайн и так далее. Основным различием обычно является лишь содержание. Естественно, что делается это не спроста: именно общие компоненты сайта создают в голове посетителей тот самый образ, который производит общее подсознательное впечатление о сайте, а также позволяет посетителям отличать сайт А от сайта Б.

Продолжая разговор, начатый еще в одной из предыдущих статей, рассмотрим организацию интерфейса между двумя другими составляющими практически любого интернет-проекта: скриптов (все так же на примере PHP) и страницами, отправляемыми посетителям посредством http-сервера.

С точки зрения веб-разработчика было бы как минимум не логично мешать в кучу постоянные части страниц с динамическими. Для этого существует множество причин, в том числе, например, экономия вычислительной мощности сервера на отсутствии необходимости каждый раз заново генерировать статичные элементы или неминуемое сокращение объемов кода, который необходимо написать, в случае если статический и динамический контент разделены. Отделенную подобным образом статическую часть страниц (слегка модифицированную с целью обозначить правила, по которым будет проводиться "заполнение" ее динамическим контентом) принято называть словом "шаблон".

Наверняка у Вас уже возникло два вполне логичных вопроса:

  1. Как можно разделить таким образом контент?
  2. Как потом восстановить страницу в исходном виде?

Вариантов ответа на каждый из них можно придумать множество: начиная с банальных вариаций на тему применения echo, заканчивая применением достаточно серьезных готовых решений вроде широкораспространенного Smarty или существенно более эффективного Blitz. Каждый из них имеет свои сильные и слабые стороны, но в целом любой из них можно оценить по двум критериям: производительности и удобстве организации кода.

Какие-либо цифры оценки производительности приводить не буду, так как, во-первых, в Сети можно найти много benchmark'ов, посвященных этой теме, а, во-вторых, просто-напросто вовсе не о цифрах я хотел с Вами поговорить. Как известно самым производительным по крайней мере с теоретической точки зрения является метод под названием php mess, заключается он в следующем: вся страница размещается в рамках одного файла, при этом статическая часть документа пишется просто "как есть" в соответствии с необходимым стандартом, а изменяемые части организуются размещенным в необходимых местах PHP-кодом, окруженным стандартной конструкцией <?php   ?>. Но огромнейший недостаток очевиден - огромное количество информации расположенной в одном файле, при отсутствии какого-либо более четкого разделения PHP-кода и остального содержимого, чем указанная выше конструкция, приводит к постоянной путанице в коде, а также существенным затратам времени программиста при попытках исправить ту или иную часть документа.

На противоположной стороне нашей шкалы удобство-производительность я бы расположил уже упомянутое выше решение под названием Smarty. Представляет оно собой целую систему, реализованную также на PHP, и предоставляющую огромное количество возможностей по решению нашей задачи. Шаблоны хранятся в отдельных файлах, для определения мест расположения динамического контента используется специальный синтаксис, который прост как три копейки, так как разрабатывался с расчетом не на программистов, а по принципу "чем проще, тем лучше". Именно этот факт сделал Smarty одним из самых (если не самым) распространенных движков шаблонизации (или как их принято правильно называть "Template Engine"). Но, к сожалению, за удобство приходится платить, в этом случае производительностью: вся система сама по себе громоздка и состоит из множества файлов, между которыми все данные так или иначе передаются, а так как написано она на PHP (который является далеко не самым производительным языком программирования, в основном в силу своей интерпритируемости и некоторых других особенностей), конкуренции в плане производительности многим другим вариантам решения нашей задачи Smarty составить не в состоянии.

Одним из лучших "компромиссных" вариантов, которые доступны на данный момент, могу назвать также упомянутый выше Blitz. Реализован он в виде модуля PHP, написанного на языке C, что является залогом его отличной производительности. При этом общая его концепция близка к Smarty: шаблоны также хранятся в отдельных файлах и подчинены незамысловатому синтаксису (который вообще можно понять и запомнить буквально за 15-20 минут, прочитав статью, ссылку на которую я уже приводил выше), а в PHP-скриптах после установки становится доступен специальный класс для управления модулем. Но основное достоинство этого решения является одновременно и его основным недостатком - редкий хостинг имеет этот модуль в списке предустановленных (видимо в силу своей не очень обширной известности, обусловленной ), а доступ к http-серверу и PHP-интерпретатору, который необходим для установки PHP-модулей, предоставляется чаще всего только на дорогих тарифах виртуального хостинга или на различных вариантах VPS или арендуемых серверов.

Помимо этого некоторые энтузиасты берутся на написание "собственных" Template Engine, базирующихся на различных вариантов использования PHP-функций вроде preg_replace. Если честно такие попытки редко заканчиваются успехом: в лучшем случае удается добиться удобства использования самим разработчиком, но чаще всего в ущерб производительности. Заниматься подобными экспериментами я Вам не советую, вместо этого я предлагаю написать "обертку" к приглянувшемуся распространенному Template Engine, что позволит не только сделать его использование более удобным конкретно для Вас, но и позволит заменить его на другой с минимальными затратами сил и времени (например в случае, если модуль Blitz недоступен).

Разрабатываем "обертку"

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

Если Ваш выбор всетаки пал на написание "оболочки", не смотря на принесение в жертву несущественной части производительности, то стоит для начала определиться: а что же мы будем "заворачивать"? В качестве примера я, пожалуй, буду использовать Blitz, как самый оптимальный вариант (по крайней мере с моей точки зрения). Начать стоит как обычно с пустой заготовки для класса.

Далее следует решить какие все же модификации мы будем производить для собственного удобства над стандартным решением. Попробую привести несколько примеров в отношении Blitz, для начала хочу обратить внимание, что при внимательном прочтении все той же статьи от разработчика этого шаблонизатора, можно обнаружить, что модуль показывает более высокие показатели производительности при однократном вызове метода set. Достичь это можно выполнением этого метода с указанием в качестве одного из входных параметров "многоуровнего" массива, составленного специальным образом (надеюсь Вы все же к этому моменту уже успели прочитать неоднократно упоминавшуюся статью, и представляете принцип работы модуля). Написание механизма составления такого массива позволит как сократить время разработки, так и сэкономит драгоценные миллисекунды, вычитаемые из свободного времени посетителей сайта в процессе генерации страницы.

В любом случае понадобится переменная для его хранения:

<?php
class TemplateEngine
{
  {
    array=array();
    //можно сразу указать указать путь к папке с шаблонами
    $this->engine=new Blitz('./template/'.$template.'.tpl');
  }
}
?>

А также метод, переопределяющий стандартный set на метод, добавляющий новые значения к нашему массиву (хотя можно и любое другое понравившееся название использовать):

<?php
function set($caption,$value)
{
  $this->array[$caption]=$value;
}
?>

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

<?php
function parse()
{
  if(count($this->array))$this->engine->set($this->array);
  echo $this->engine->parse();
}
?>

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

<?php
function set($caption,$value)
{
  $this->array[$caption]=$this->html($value);
}
function rawset($caption,$value)
{
  $this->array[$caption]=$value;
}
private function html($array)
{
  if(is_array($array))
  {
    foreach($array as $caption => $value)
    $value=$this->html($value);
    return $array;
  }
  else return htmlspecialchars($array,ENT_QUOTES);
}
?>

Как нетрудно заметить, в методе используется рекурсия, так как структура передаваемых параметром массивов неизвестна.

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

26 января 2008 |  Иван Блинков  |  PHP