Blitz: шаблонный подход к построению контента
Мне кажется, что пора уже рассматривать тему шаблонизаторов - специальных библиотек для построения шаблонов. Проблема любого крупного проекта, я думаю, - отделение дизайна от кода (т.е. html-страниц от php-кода). Зачем это делать? Ну на то конечно есть причины. Во-первых, разделенные дизайн и код легче поддерживать и изменять. |
Во-вторых, если на сайте нужна смена скинов, без шаблонов не обойтись, потому что для каждого скина придется дублировать все php-файлы (код), а это не есть хорошо (потому что отнимает много времени) для постоянно изменяющегося кода в результате воздействия заказчика
Так вот, существует множество шаблонизаторов, из которых я бы воспользовался одним из двух: Smarty и Blitz. О Smarty мы поговорим в других статьях, а в этой коснемся Blitz - шаблонизатора, о котором я совсем недавно узнал, в отличие от Smarty.
Blitz - шаблонизатор, написанный нашим соотечественником - Алексеем Рыбаком - на основе движка php_templates. Считается самым быстрым шаблонизатором на данный момент. Причина такого быстродействия - написан на языке Си (как модуль расширения PHP). Но у каждой вещи в этом мире есть свои достоинства и недостатки. Про главное достоинство Blitz я уже сказал, а главных недостатков я выделил у него три:
- На данный момент еще сыроват, т.к. не доделан даже до версии 1.0 (на момент написания этой статьи последняя версия была 0.6.1);
- Функционал не такой мощный, как в Smarty (этот недостаток можно едва компенсировать наличием механизма контекстов и итераций, которого нет у Smarty). Я бы сказал даже, Smarty гораздо мощнее;
- Библиотеку нужно устанавливать в качестве расширения PHP, что возможно не у всех хостинг-провайдеров.
Но, несмотря на эти недостатки, этот шаблонизатор стоит того, чтобы рассмотреть его возможности.
Установка Blitz
У меня, как Вы знаете (а может и не знаете), система Windows, поэтому я просто прикрутил модуль php_blitz.dll, ссылку на который можно найти здесь. В Linux как он устанавливается, я не знаю, но в официальной документации, которая состоит всего из одной странички, можно найти ссылку на исходники и готовые модули. Там же написано про то, как устанавливается этот Blitz.
Изучаем Blitz
Начнем с самого простого - выведем в окно браузера содержимое переменной:
example1.tpl
Содержимое переменной $b: {{$b}}
example1.php
header('Content-Type: text/plain');
$T = new Blitz('example1.tpl');
$T->set(Array('b' => 'world'));
echo $T->parse(Array('a' => 'Hello'));
?>
Вывод:
Содержимое переменной $b: world
В этом примере мы вывели содержимое двух переменных двумя способами. Функция set позволяет задать ассоциативный массив локальных переменных, а функция parse - возвращает результат выполнения шаблонизатора. Функция parse тоже может принимать массив переменных, но при этом они будут глобальными, т.е. видимыми не только в шаблонах как таковых, но и в контекстах (о контекстах я расскажу ниже).
Можно не передавать методу parse массив с глобальными переменными, а передать его отдельно методу setGlobal:
$T->setGlobal(Array(‘b’ => ‘world’));
Теперь покажу различие глобальных и локальных переменных в контекстах:
example2.tpl
Я {{$g}} переменная.<br />
{{BEGIN root}}<br />
Я {{$l}} переменная.<br />
Я {{$g}} переменная.<br />
{{END}}
example2.php
header('Content-Type: text/plain');
$T = new Blitz('example2.tpl');
$T->set(Array('l' => 'локальная в пределах шаблона'));
$T->setGlobal(Array('g' => 'глобальная'));
$T->block('/root', Array('l' => 'локальная в пределах контекста'));
echo $T->parse();
?>
Вывод:
Я глобальная переменная.<br />
Я локальная в пределах контекста переменная.<br />
Я глобальная переменная.
Здесь мы ввели понятие контекста - это блок информации в шаблоне, который должен быть выполнен, прежде чем его можно вывести. Выполнили мы его с помощью функции block, установив в ней локальные для данного контекста переменные. Если бы мы не установили локальные для root переменные, т.е. вызвали бы таким образом…
$T->block(‘/root’);
…, то вывод был бы таким:
Я глобальная переменная.<br />
Я переменная.<br />
Я глобальная переменная.
Т.е. локальная в пределах шаблона переменная не действует в зоне контекста, а глобальная переменная действует везде.
Если бы мы не вызвали block (не выполнили бы контекст), вывод был бы таким:
Я глобальная переменная.
Вообще контексты в Blitz - очень мощная штука. С их помощью, по утверждению автора, можно формировать контент любой сложности. Еще бы! Ведь эти контексты могут быть вложенными:
example3.tpl
Я - главный контекст root.<br />
{{BEGIN child}}<br />
А я - дочерний контекст child.<br />
{{BEGIN grandchild}}<br />
Я - внук #{{$i}} контекста root.<br />
{{END}}<br />
{{END}}<br />
{{END}}
example3.php
header('Content-Type: text/plain');
$T = new Blitz('example3.tpl');
$T->block('/root');
$T->block('/root/child');
$T->block('/root/child/grandchild', Array('i' => 1));
$T->context('/root/child/grandchild');
$T->iterate();
$T->set(Array('i' => 2));
echo $T->parse();
?>
Вывод:
А я - дочерний контекст child.<br />
Я - внук #1 контекста root.<br />
Я - внук #2 контекста root.
Здесь мы два раза выполняем тело контекста grandchild с разными значениями переменной i. Причем делаем это двумя способами: уже знакомой нам функцией block и вызовами context-iterate-set. Вызов context() устанавливает текущий контекст, над которым будет потом выполнена итерация с помощью iterate() и которому будут присвоены локальные переменные с помощью set().
/root/child/grandchild - это, как Вы уже наверняка догадались, полный путь к контексту. Но можно задавать и относительные пути. Например:
$T->context(‘/root/child’);
$T->context(‘grandchild’);
Это эквивалентно
$T->context(‘/root/child/grandchild’);
Но я бы предпочел использовать более компактный вариант с block().
Для вызова определенного контекста без выполнения всего шаблона можно использовать fetch():
example4.php
header('Content-Type: text/plain');
$T = new Blitz('example3.tpl');
echo $T->fetch('/root/child/grandchild', Array('i' => 1));
?>
Этот пример выведет: Я - внук #1 контекста root.
А что делать, если нам нужно очистить текущий контекст от переменных и/или итераций? Для этого есть функция clean():
example5.php
header('Content-Type: text/plain');
$T = new Blitz('example3.tpl');
$T->block('/root/child/grandchild', Array('i' => 1));
echo $T->parse();
$T->clean('/root/child/grandchild');
echo $T->parse();
?>
Этот пример выведет:
А я - дочерний контекст child.<br />
Я - внук #1 контекста root.<br />
Я - главный контекст root.<br />
А я - дочерний контекст child.
Но стоит нам убрать вызов clean(), как вывод станет таким:
А я - дочерний контекст child.<br />
Я - внук #1 контекста root.<br />
Я - главный контекст root.<br />
А я - дочерний контекст child.<br />
Я - внук #1 контекста root.
Иногда бывают случаи, когда тело шаблона хранится в переменной (например, если шаблоны хранятся не в текстовых файлах, а в базе данных). Для этого случая есть функция load():
example6.php
header('Content-Type: text/plain');
$tpl = <<<TPL
hello, {{ \$world }}!
{{ bye('world') }}
TPL;
class View extends Blitz {
function bye($who) {
return "Bye, $who!";
}
}
$T = new View();
$T->load($tpl);
echo $T->parse(array('world' => 'world'));
?>
Здесь мы немного расширили класс Blitz и добавили в него свой метод, чтобы потом вызвать из шаблона. Метод, как видите, может принимать параметры прямо из шаблона.
Итоги
В общем, это все основные возможности шаблонизатора Blitz. Официальная документация у него небольшая (как я уже говорил - одна html-страница), ее можно найти здесь (на русском языке). Кстати, если Вы еще туда не заходили, по этой же ссылке приведены результаты тестов, по которым можно более-менее судить о быстродействии этого шаблонизатора. Еще замечание по поводу документации: ее английская версия гораздо актуальнее, поэтому я советовал бы всем обращаться именно к ней.
Теперь мы умеем отделять дизайн от кода, а это очень важно для больших проектов. Но это еще не все. Чтобы практически можно было осуществить это в реальности, я бы воспользовался более старым, медленным, но зато проверенным и мощным шаблонизатором Smarty, о котором расскажу в следущих статьях.
Скачать все примеры к этой статье.
Интересно было почитать, но чисто познавательно, то, что он работает как модуль начисто, имхо, отбивает все преимущества. Слишком узкая область применения, тем более при разработке для заказчика, а не для своего сервера с рутовым доступом. В общем при написании скриптов на заказ с нуля, имхо, два выбора оправданных шаблонизаторов: Smarty и XSLT, всё остальное от лукавого
Хах, наоборот это один из плюсов, а не минусов. Вы только почитайте, какие у него преимущества в скорости и удобстве использования. А то, что он подключается в качестве модулья, так это не проблема - напишите в тех.поддержку хостинга, и, если он у вас хороший (а для серьезных проектов, особенно для заказчика, по-другому быть не должно), вам его без проблем подключат.
Не сочтите за рекламу, но, к примеру, на РБК-хостинге это сделали без проблем.
2 mif: С плохим хостингом это конечно недостаток, но с хорошим - стоит попробовать.
привет, я - автор blitz
случайно наткнулся на текст, хочу поправить чуть-чуть
в example2.php нет бага - include “наследует” итерацию, это сделано как раз для больших любителей инклюдов, которым хочется поменьше делать телодвижений - один раз засетил и везде видно.
касательно ссылок - полезнее давать ссылку на английскую доку, русская устарела, и там не описано очень много. правда, и английская тоже немного устарела 😉
wbr,
fisher
Привет, Алексей!
Исправил статью. Спасибо за комментарий.
Мне не нравится Blitz, хотя работаю на нем уже пол года. Например,
нельзя сделать так {{ if($a>0,’plus’,’minus’) }}
нет конструкции {if elseif else}
Smarty - это хоть не так быстро, но зато гибко и удобно. А скорость для шаблонизатора не самое главное.
{{ if($a>0,’plus’,’minus’) }}
так можно сделать - просто пишете функцию в классе, который расширяет Blitz типа
function iftype($var_1, $type, $var_2, $comparison_ok=”, $otherwise=”, $eq=”) {
if ($type=='<‘) {
if ($var_1 $var_2) return $otherwise; else return $eq;
}
elseif ($type==’>’) {
if ($var_1 > $var_2) return $comparison_ok; else if ($var_1 < $var_2) return $otherwise; else return $eq;
}
elseif ($type==’==’) {
if ($var_1 == $var_2) return $comparison_ok; else return $otherwise;
}
}