Содержание
Список таблиц
Список примеров
Blitz родился весьма неоригинально, for fun. Однако, поигравшись с ним немного, мне показалось, что скорость, с которой он работает, и удобства, которые он предоставляет разработчику — стоят того, чтобы дать его поиграться коллегам. Основных «фишек» у Blitz три:
написан как PHP-модуль на Си, и является одним из самых быстрых движков;
имеет простой и интуитивно понятный синтаксис;
позволяет структурировать код удобным и легко читаемым образом.
Blitz поддерживает разделение и скрытие функционально различных частей шаблонов с помощью простого механизма: текст шаблона может содержать вызов пользовательского метода объекта, который этим шаблоном управляет. Таким образом, достигается основная цель: шаблон не содержит большого количества блоков и контекстов, часто мешающих разобраться, что к чему. Напротив, даже в проекте со сложной логикой представления при правильном подходе шаблоны будут давать разработчику своеобразную «карту» всего проекта. Blitz также позволяет включать одни шаблоны в другие (аналог include) и поддерживает условный вывод переменных (аналог if).
Начиная с версии 0.4 в Blitz добавлен функционал движка php_templates. Теперь в Blitz можно создавать множество вложенных шаблонов внутри одного файла, это сильно упрощает жизнь для очень сложных шаблонов с одной стороны и улучшает показатели по производительности для ряда задач с другой. Также с версии 0.4 есть экспериментальная поддержка работы с набором «упакованных» шаблонов (blitzpack), когда несколько шаблонов сначала единожды «компилируются» в бинарный формат, упаковываются в один файл, и затем работа ведется только с этой «бинарной пачкой» шаблонов, экономя время на файловых операциях и разборе шаблонов.
Мне бы не хотелось здесь следовать академической традиции и проводить подробный анализ других проектов. В любом случае, если вы научились эффективно использовать сам PHP в качестве шаблонного движка, обходясь без сторонних продуктов и библиотек — вы счастливый человек. Вы используете самый эффективный с точки зрения производительности подход, и если он вам удобен — придерживайтесь его. Если нет — попробуйте Blitz. Возможно, он вас приятно удивит ;)
Статус проекта — полуэкспериментальный, но я считаю текущую версию вполне работоспособной. В силу параноидального отношения к качеству проекта, 100%-я стабильность и обратная совместимость до версии 1.0 не гарантируется.
К сожалению, мне неизвестна ни одна простая, универсальная и по-настоящему корректная методика анализа производительности шаблонных движков. А результатами любых искусственных, или как их ещё принято называть, синтетических, тестов пользоваться нужно максимально осторожно. Тем не менее, здесь приводятся результаты двух тестов. Первый тест — классический, измеряющий скорость выполнения циклических итераций одного и того же шаблона. Тест крайне простой, но позволяющий достаточно условно разделить группы движков на «нормальные», «медленные» и «никуда не годные». Число итераций и переменных в блоке было взято по умолчанию (9 переменных, 50 итераций), результаты этого теста приведены в таблице 1. Как легко видеть, Blitz, по крайней мере, аутсайдером не является.
Таблица 2.1. Классический тест производительности
| Тестовая машина (A): сервер XEON*2 2,4GHz (HT on) 2GB; linux php-4.3.10(fgci) zps nginx blitz, php_templates: so-модули, CFLAGS: -g3 -O2 | |||
|---|---|---|---|
| № | Engine name | Time | Percentage |
| 1 | php | 0.000544 | 100% |
| 2 | blitz | 0.001008 | 185% |
| 3 | php_templates | 0.001812 | 333% |
| 4 | smarty | 0.002006 | 369% |
| 5 | str_replace | 0.003713 | 683% |
| 6 | phemplate | 0.004514 | 830% |
| 7 | fasttemplate | 0.006835 | 1256% |
| 8 | vtemplate | 0.009565 | 1758% |
| 9 | ultratemplate | 0.012993 | 2388% |
| 10 | templatepower | 0.017056 | 3135% |
| 11 | bugitemplate | 0.019989 | 3674% |
| 12 | phplib | 0.028053 | 5157% |
| 13 | profTemplate | 0.043104 | 7924% |
| 14 | xtemplate | 0.048799 | 8970% |
| Тестовая машина (B): PC PIV 2,8GHz (HT off) 1GB; linux-2.6.8 php-4.3.10 (Apache/1.3.33 static) zps blitz, php_templates: so-модули, CFLAGS -g -O2 | |||
|---|---|---|---|
| № | Engine name | Time | Percentage |
| 1 | php | 0.00045 | 100% |
| 2 | blitz | 0.000834 | 185% |
| 3 | php_templates | 0.001595 | 354% |
| 4 | smarty | 0.001694 | 376% |
| 5 | str_replace | 0.00373 | 829% |
| 6 | phemplate | 0.004215 | 937% |
| 7 | fasttemplate | 0.006139 | 1364% |
| 8 | vtemplate | 0.008755 | 1946% |
| 9 | ultratemplate | 0.012747 | 2833% |
| 10 | templatepower | 0.018678 | 4151% |
| 11 | bugitemplate | 0.019286 | 4286% |
| 12 | phplib | 0.025478 | 5662% |
| 13 | profTemplate | 0.045148 | 10033% |
| 14 | xtemplate | 0.048137 | 10697% |
Столь сильное отличие php от прочих движков объясняется тем, что весь код php-теста упакован в один файл, в то время для остальных есть два файла — php-файл контроллера и шаблонный файл, который парсится котроллером.
В результаты этого теста не включены некоторые известные шаблонные движки, такие как madtemplate, PEAR::Sigma и PEAR::HTML_Template_IT по простой причине: они не были установлены на тестовых машинах. Однако, насколько мне известно, эти проекты не являются кандидатами на попадание в пятерку лидеров. В этом можно убедиться, например, проведя онлайн-тесты самостоятельно, или скачав тестирующую программу.
Второй тест более приближен к полевым условиям. Он представляет собой тестирование некоторой динамической страницы, подготовленной с использованием разных движков, при помощи стандартной утилиты ab. Итак, у нас есть страница какого-то псевдопортала, содержащая:
ротирующиеся рекламные уши (3 шт.);
«полосатую» навигацию (~10 разделов);
горячие новости (~10 шт.);
список пользователи онлайн (~20 шт.);
голосовалка с вариантами ответов (3 ответа);
прочие переменные на странице (~5 шт).
Для тестирования было выбрано 4 подхода:
ugly php mess: используется только php, причем весь код полностью упакован в один файл, представляя собой эдакую «кашу». Такой прием практически никогда не встречается в реальных больших проектах, но включен в тесты исключительно ради интереса, поскольку очевидно является самым быстрым.
php includes: используется только php, функционально разные блоки (элементы списков) вынесены в отдельные файлы.
php_templates: один шаблон, на каждый функциональный блок — контекст.
blitz includes: разные шаблоны на каждый функциональный блок, подключаемые через include.
blitz ctx: один шаблон, на каждый функциональный блок — контекст.
blitz ctx arrays: один шаблон, на каждый функциональный блок — контекст, один вызов установки массива итераций.
blitzpack: разные шаблоны на каждый функциональный блок, упакованые в один blitzpack, подключаемые через include.
smarty: один шаблон, на функциональные блоки — циклы внутри шаблона.
Все данные упакованы в структуру в отдельном файле, который инклюдится во всех тестовых вариантах. Числа запросов в секунду, которое выполняет сервер для каждого из методов, представлены в таблице 2.
Таблица 2.2. Тест производительности приближенный к полевым условиям
| Тестовая машина(B), см. таблицу 1 | |
|---|---|
| ab -n20000 -c100, ZPS on | |
| ugly php mess | 1150 |
| blitz ctx arrays | 890 |
| blitz ctx | 825 |
| php includes | 770 |
| blitzpack | 725 |
| blitz incudes | 680 |
| smarty | 620 |
| php_templates | 615 |
| Тестовая машина(B), см. таблицу 1 | |
|---|---|
| ab -n20000 -c100, ZPS off | |
| ugly php mess | 660 |
| blitz ctx arrays | 590 |
| blitz ctx | 560 |
| php_templates | 450 |
| blitzpack | 440 |
| blitz incudes | 430 |
| smarty | 285 |
| php includes | 125 |
К сожалению, я не имел возможности провести тесты для других шаблонных движков (впрочем, код для этого теста доступен, вы можете добавить в него решения исходной задачи с использованием любых других средств). Поэтому ограничусь обобщенной интерпретацией этих результатов. То, что native PHP-код вместе с акселератором всегда будут быстрее прочих решений — очевидно. Правда, следует особенно подчеркнуть, что native в этом смысле — именно написанный программистом самим, а не «скомпилированный». В этом легко убедиться, заглянув внутрь любого «скомпилированного» шаблона: как правило, их код состоит из многомерных, довольно сложных для выполнения конструкций, значительно сложнее, чем написанный правильными руками код ;) Поскольку разница между blitz и «правильным» методом php includes не является кардинальной, а все синтетические тесты позволяют лишь выявить группы приблизительно равных, можно с определенной долей уверенности считать методы разработки с использованием php, blitz и php_templates примерно одинаковыми по производительности.
Следует также принять во внимание, что в реальном проекте разница в скорости между различными методами, скорее всего, будет ещё меньше. Во-первых, это связано с тем, что значительное время будет тратиться на работу с источниками данных (СУБД, различные сервисы и проч.). Во-вторых, отношение «количества» кода, относящегося к уровню представления, и прочего кода будет совершенно иным. Грубо говоря, view_code = full_code для тестов и пусть выигрыш на синтетическом тесте составляет даже десятки процентов. Но в реальном проекте часто выполняется соотношение view_code >> full_code, и поэтому выигрыш на уровне представления уже почти ничего не даст. Как вы могли заметить, почти все тесты были проведены с использованием акселератора из ZPS. Вряд ли сейчас можно представить крупный проект, в котором не используется акселератор, однако, акселератор акселератору рознь. И вполне возможно вы получите совершенно иные результаты при использовании, например, eAccelerator'a. В-общем, призываю вас не полагаться полностью на приведенные результаты. Скачивайте тесты, экспериментируйте на реальных задачах, и выбирайте те решения, которые дают выигрыш в вашем проекте.
Содержание
Blitz — расширение PHP, поставляемое пока исключительно в исходных кодах, поэтому его установка состоит из обычных шагов по сборке расширения:
tar zxvf blitz.tar.gz
cd blitz
phpize
make
./configure
make install
Начиная с версии 0.4 в дистрибутив входит несколько тестов:
bash>./run-tests.sh ===================================================================== CWD : /distr/php-5.2.0/ext/blitz/tests PHP : /home/php-5.2.0/bin/php PHP_SAPI : cli PHP_VERSION : 5.2.0 ZEND_VERSION: 2.2.0 PHP_OS : Linux - Linux fisher 2.6.11.4-20a-smp #1 SMP Wed Mar 23 21:52:37 UTC 2005 i686 INI actual : /home/php-5.2.0/lib/php.ini More .INIs : Extra dirs : ===================================================================== Running selected tests. PASS contexts [context.phpt] PASS errors and warnings [errors.phpt] PASS fetch [fetch.phpt] PASS complex fetch [fetch_cmplx.phpt] PASS has context [has_context.phpt] PASS predefined methods: if [if.phpt] PASS predefined methods: include [include.phpt] PASS ini-values settings test [ini.phpt] PASS user-defined methods [method.phpt] PASS method call from inner include [mfi.phpt] PASS mix #1 [mix1.phpt] PASS mix #2 [mix2.phpt] PASS mix #3 [mix3.phpt] PASS mix #4 [mix4.phpt] PASS mix #5 [mix5.phpt] PASS mix #6 [mix6.phpt] PASS expect regexp test [regex.phpt] PASS returning non-strings from user methods [return_non_string.phpt] PASS set and get [set_and_get.phpt] PASS variables [var.phpt] ===================================================================== Number of tests : 20 20 Tests skipped : 0 ( 0.0%) -------- Tests warned : 0 ( 0.0%) ( 0.0%) Tests failed : 0 ( 0.0%) ( 0.0%) Tests passed : 20 (100.0%) (100.0%) --------------------------------------------------------------------- Time taken : 0 seconds =====================================================================
После этого вы, возможно, захотите отредактировать свой php.ini, включив blitz в список расширений:
extension=blitz.so
Сборка blitz тестировалась на Linux 2.6 (i386) и Windows XP. Пользователи Windows могут воспользоваться готовыми Win32-бинарниками.
Содержание
Как и во многих других шаблонных движках двумя базовыми сущностями проекта, которые использует Blitz, являются собственно шаблон и объект, управляющий выполнением этого шаблона (контроллер шаблона).
Класс Blitz — внутренний класс расширения, управляющий шаблоном. Первый и единственный аргумент конструктора класса — имя шаблона.
Пример 5.1. Переменные
Это некоторый тест для двух переменных: {{ $a }} и {{ $b }}, номер итерации: {{ $i }}<?
$T = new Blitz('tpl');
$i = 0;
$i_max = 10;
for ($i = 0; $i<$i_max; $i++) {
echo $T->parse(
array(
'a' => 'var_'.(2*$i),
'b' => 'var_'.(2*$i+1),
'i' => $i
)
);
}
?>Это некоторый тест для двух переменных: var_0 и var_1, номер итерации: 0 Это некоторый тест для двух переменных: var_2 и var_3, номер итерации: 1 Это некоторый тест для двух переменных: var_4 и var_5, номер итерации: 2 Это некоторый тест для двух переменных: var_6 и var_7, номер итерации: 3 Это некоторый тест для двух переменных: var_8 и var_9, номер итерации: 4 Это некоторый тест для двух переменных: var_10 и var_11, номер итерации: 5 Это некоторый тест для двух переменных: var_12 и var_13, номер итерации: 6 Это некоторый тест для двух переменных: var_14 и var_15, номер итерации: 7 Это некоторый тест для двух переменных: var_16 и var_17, номер итерации: 8 Это некоторый тест для двух переменных: var_18 и var_19, номер итерации: 9
Возможность включать в шаблон пользовательские методы - самая интересная с точки зрения организации хорошего и удобно читаемого кода. До сих пор в примерах использовался стандартный класс Blitz, никакими новыми методами не обладающий. Однако если создать объект класса-наследника Blitz, который предоставляет некоторый метод my_test, в шаблоне можно использовать вызов этого метода ровно с таким же названием.
Пример 6.1. Вызов пользовательского метода
пример вызова пользовательского метода: {{ my_test }}<?
class BlitzTemplate extends Blitz {
function my_test() {
return 'user method called ('.__CLASS__.','.__LINE__.')';
}
}
$T = new BlitzTemplate('tpl');
echo $T->parse();
?>пример вызова пользовательского метода: user method called (blitztemplate,5)
Все, что возвращает пользовательский метод, будет конвертировано в строку и подставлено вместо вызова. Если вызов метода в шаблоне есть, но самого метода нет - будет подставлена пустая строка. Вообще, действует обычное правило: никаких исходных вызовов никогда не присутствует в конечном результате, независимо от существования переменной, метода и проч.
Внутри пользовательского метода также можно включать другие шаблоны. Конечно, никто не запрещает вам написать что-нибудь вроде:
class BlitzTemplate extends Blitz {
var $data;
var $TItem;
function BlitzTemplate($t,$titem) {
parent::Blitz($t);
$TItem = new Blitz($titem);
}
function set_data() {
// some code
}
function my_test() {
$result = '';
foreach ($this->data as $i_data) {
$result .= $TItem->parse($i_data);
}
return $result;
}
}
$T = new BlitzTemplate('main.tpl','item.tpl');
// $bla_bla = ...
$T->set_data($blabla);
echo $T->parse();
Этот метод будет работать, но не очень хорош по двум причинам. Во-первых, $TItem является совершенно отдельным объектом, никак не связанным с $T. Blitzу несколько сложнее переключаться с одного объекта на другой, нежели выполнять все операции через один и тот же объект. Во-вторых, $TItem не будет наследовать установленные переменные из $T, их при необходимости нужно будет протягивать самостоятельно, а также внутри $TItem нельзя использовать методы $T. Поэтому более правильным будет использование встроенного метода include().
Начиная с версии 0.3 в Blitz поддерживается передача параметров из шаблона в пользовательский метод.
Пример 6.2. Передача параметров из шаблона в пользовательский метод
calling template with arguments: {{ my_test(134,$x,"hello,world!",$dummy) }}<?
class BlitzTemplate extends Blitz {
var $titem;
function BlitzTemplate($t) {
parent::Blitz($t);
$this->set(array('x' => 1234));
}
function my_test($p1,$p2,$p3,$p4) {
$result = 'user method called ('.__CLASS__.','.__LINE__.')'."\n";
$result .= 'parameters are:'."\n";
$result .= '1:'.var_export($p1,TRUE)."\n";
$result .= '2:'.var_export($p2,TRUE)."\n";
$result .= '3:'.var_export($p3,TRUE)."\n";
$result .= '4:'.var_export($p4,TRUE)."\n";
return $result;
}
}
$T = new BlitzTemplate('tpl');
echo $T->parse();
?>calling template with arguments: user method called (blitztemplate,12) parameters are: 1:134 2:1234 3:'hello,world!' 4:NULL
Начиная с версии 0.4 в Blitz практически без изменений была добавлена функциональность движка php_templates. Суть контекстов заключается в следующем. Обычно мы имеем дело с «плоскими» шаблонами, в них есть либо переменные, либо какие-то операторы или вызовы, которые обязательно исполняются. Контексты - это подшаблоны, которые не используются до тех пор, пока контроллер шаблона явно это не укажет. Например, если у нас есть php-код
переменная: <?=$a?>, метод: <?=b()?>
, то оба куска этого кода будут всегда исполнены. Никакой иерархии нет, все php-шаблоны плоские. Теперь рассмотрим некий псевдокод с контекстами
переменная : {{ $a }}, контекст {{ BEGIN b }} что-то внутри {{ END }}
Здесь b — это контекст, который по умолчанию не будет использован (чаще используют термин «итерирован») - и вместо кода от BEGIN до END ничего не будет. Если контекст итерирован один раз, то в шаблоне появится результат исполнения внутренней части контекста. Если проитерировать дважды — результат исполнения внутренней части контекста два раза. Параметры у каждой итерации, разумеется, могут быть разными. Таким образом, для отображения списков достаточно просто итерировать контекст, описывающий элемент списка, для каждого элемента. Самое удобное заключается в том, что контексты могут быть вложены друг в друга. Каждый контекст однозначно определён своим путем — /root/node/item означает, что есть контекст root, внутри которого контекст node внутри которого item. Если итерировать родительский контекст с одними параметрами, потом вложенные контексты, потом снова родительские контексты с другими параметрами, а потом снова вложенные, то можно при помощи одного единственного шаблона сделать страницу абсолютно любой сложности. Есть базовые операции с контекстами — установить текущий контекст и итерировать контекст. Установка означает, что все вызовы по умолчанию работают с этим контекстом, тут есть хорошая аналогия с работой командной оболочке — установить текущий контекст в /root/node/item по смыслу то же самое что сделать cd в /root/node/item. А итерировать фактически означает «исполнить».
Передавая параметр context_path в любую из функций, вы можете передавать его в двух формах:
абсолютной
/context1/context2/context3
относительной
context2/context3
../context3
В Blitz внутри контекстов также доступны такие приятные мелочи как if(), include() и вызов пользовательского метода.
Следующий код выводит ужасно доставшее всех знакомое всем приветствие, запрятанное в трех вложенных контекстах.
Пример 7.1. Ужасно доставшее всех знакомое всем приветствие, запрятанное в трех вложенных контекстах
{{ BEGIN root }}
{{ BEGIN node }}
{{ BEGIN item }}
hello, world
{{ END }}
{{ END }}
{{ END }}<?
$T = new Blitz('tpl');
$T->iterate('/root/node/item');
echo $T->parse();
?>hello, world
Пример 7.2. Работа с простыми списками
{{ BEGIN row }}row #{{ $i }}
{{ END }}<?
$T = new Blitz('tpl');
$max_num_list = 5;
// use context & iterate
$T->context('row');
for($i=0; $i<$max_num_list; $i++) {
$T->iterate();
$T->set(array('i' => $i));
}
// or just use block
for($i=0; $i<$max_num_list; $i++) {
$T->block('/row',array('i' => $i));
}
echo $T->parse();
?>row #0 row #1 row #2 row #3 row #4 row #0 row #1 row #2 row #3 row #4
Метод block() — удобная замена последовательным iterate() и set(), которые встречаются в коде очень часто именно вместе. Разумеется, в Blitz внутри контекста можно использовать и include(), и if(), и пользовательские методы. Переменные, установленные в родительских контекстах, «не видны» в дочерних. Если есть необходимость в глобальных переменных, которые будут «видны» в любом месте шаблона - можно использовать метод set_global() вместо set().
Пример 7.3. Как при помощи вложенных контекстов строить более сложные списки
complex list example
{{ BEGIN list; }}
list #{{ $list_num }}
{{ BEGIN list_empty; }} this list is empty {{ END }}{{ BEGIN list_item; }} row #{{ $i_row; }}
{{ END }}
{{ END }}<?
$T = new Blitz('tpl');
$max_num_list = 5;
$max_num_item = 5;
$T->context('/list');
for($i=0; $i<$max_num_list; $i++) {
$T->block('',array('list_num' => $i));
$is_empty = $i%2; // emulate empty sub-lists
if($is_empty) {
$T->block('list_empty');
} else {
for($j=0; $j<$max_num_item; $j++) {
$T->block('list_item',array('i_row' => $i.':'.$j));
}
}
}
echo $T->parse();
?>complex list example list #0 row #0:0 row #0:1 row #0:2 row #0:3 row #0:4 list #1 this list is empty list #2 row #2:0 row #2:1 row #2:2 row #2:3 row #2:4 list #3 this list is empty list #4 row #4:0 row #4:1 row #4:2 row #4:3 row #4:4
Фактически на этом простом механизме попеременных итераций вложенных контекстов реализуется абсолютно любая логика даже в одном единственном шаблоне.
В-общем, функционал контекстов достаточно мощный для того, что бы использовать шаблонный движок для сколь угодно сложных проектов.
Содержание
Содержание
(blitz >= 0.4)
context — установка контекстаПример 8. context
{{BEGIN user}}
{{BEGIN hello}}
Hello, user!
{{END}}
{{BEGIN goodbye}}
Goodbye, user!
{{END}}
{{END}}
{{BEGIN world}}
Hello, world!
{{END}}<?php
$Template = new Blitz('tpl');
$Template->context('user');
$Template->block('hello');
$Template->block('goodbye');
$Template->block('../world');
echo $Template->parse();
?>Hello, user! Goodbye, user! Hello, world!
См. также block().
(blitz >= 0.4)
dump_iterations — дамп итерацийПример 9. dump_iterations
{{BEGIN counter}}
{{$i}},
{{END}}<?php
$Template = new Blitz('tpl');
for ($i = 0; $i < 3; $i++)
{
$Template->block('context', array('i' => $i));
}
$Template->dump_iterations();
?>ITERATION DUMP (4 parts)
(1) iterations:
array(1) {
[0]=>
array(1) {
["context"]=>
array(1) {
[0]=>
array(1) {
["i"]=>
int(0)
}
}
}
}
(2) current path is: /
(3) current node data (current_iteration_parent) is:
array(1) {
[0]=>
array(1) {
["context"]=>
array(1) {
[0]=>
array(1) {
["i"]=>
int(0)
}
}
}
}
(4) current node item data (current_iteration) is:
empty
ITERATION DUMP (4 parts)
(1) iterations:
array(1) {
[0]=>
array(1) {
["context"]=>
array(2) {
[0]=>
array(1) {
["i"]=>
int(0)
}
[1]=>
array(1) {
["i"]=>
int(1)
}
}
}
}
(2) current path is: /
(3) current node data (current_iteration_parent) is:
array(1) {
[0]=>
array(1) {
["context"]=>
array(2) {
[0]=>
array(1) {
["i"]=>
int(0)
}
[1]=>
array(1) {
["i"]=>
int(1)
}
}
}
}
(4) current node item data (current_iteration) is:
empty
ITERATION DUMP (4 parts)
(1) iterations:
array(1) {
[0]=>
array(1) {
["context"]=>
array(3) {
[0]=>
array(1) {
["i"]=>
int(0)
}
[1]=>
array(1) {
["i"]=>
int(1)
}
[2]=>
array(1) {
["i"]=>
int(2)
}
}
}
}
(2) current path is: /
(3) current node data (current_iteration_parent) is:
array(1) {
[0]=>
array(1) {
["context"]=>
array(3) {
[0]=>
array(1) {
["i"]=>
int(0)
}
[1]=>
array(1) {
["i"]=>
int(1)
}
[2]=>
array(1) {
["i"]=>
int(2)
}
}
}
}
(4) current node item data (current_iteration) is:
empty
См. также dump_struct().
(blitz >= 0.4)
dump_struct — дамп структурыПример 10. dump_struct
{{BEGIN counter}}
{{$i}},
{{END}}<?php
$Template = new Blitz('tpl');
for ($i = 0; $i < 3; $i++)
{
$Template->block('context', array('i' => $i));
}
$Template->dump_struct();
?>== TREE STRUCT (2 nodes): ^-begin[22] (0(17), 32(27)); ARGS(1): counter(0); CHILDREN(1): ^-i[1] (19(0), 23(0)); == PLAIN STRUCT (2 nodes): begin[22] (0(17), 32(27)); ARGS(1): counter(0); CHILDREN(1): i[1] (19(0), 23(0));
См. также dump_iterations().
(blitz >= 0.4)
fetch — использовать контент одного шаблона при работе с другим шаблоном или тело одного контекста в одном шаблоне внутри другогоПример 11. fetch
{{ BEGIN online }} online! {{ END }}
{{ BEGIN offline }} was online {{ $n }} {{ BEGIN h }}hours{{ END }}{{ BEGIN d }}days{{ END }}{{ BEGIN m }}months{{ END }} ago {{ END }}
{{ BEGIN away }} away... {{ END }}<?
$T = new Blitz('tpl');
// online
echo $T->fetch('online')."\n";
// away
echo $T->fetch('away')."\n";
$T->context('offline');
// 15 days ago
$T->iterate('d');
echo $T->fetch('offline', array('n' => 15))."\n";
$T->iterate(); // create next iteration for offline block
// 2 months ago
$T->iterate('m');
echo $T->fetch('offline', array('n' => 2))."\n";
?>online! away... was online 15 days ago was online 2 months ago
Вместо того чтобы «очистить» каким-либо образом предыдущую итерацию контекста offline (такой функционал будет добавлен в Blitz в самое ближайшее время), создается новая итерация. Метод fetch получает результат исполнения последней итерации контекста.
(blitz >= 0.4)
iterate — итерация контекстаПример 13. iterate
{{BEGIN hello}}
Hello, user!
{{END}}
{{BEGIN goodbye}}
Goodbye, user!
{{END}}<?php
$Template = new Blitz('tpl');
$Template->iterate('hello');
$Template->context('goodbye');
$Template->iterate();
echo $Template->parse();
?>Hello, user! Goodbye, user!
(blitz >= 0.1)
parse — разбор шаблонаПример 15. parse без установки переменных
Hello, world!
<?php
$Template = new Blitz('tpl');
echo $Template->parse();
?>Hello, world!
Пример 16. parse с установкой переменных
Hello, {{$object}}!<?php
$Template = new Blitz('tpl');
echo $Template->parse(array('object' => 'world'));
?>Hello, world!
(blitz >= 0.1)
set — установка переменныхПример 17. set
Hello, {{$object}}!<?php
$Template = new Blitz('tpl');
$Template->set(array('object' => 'world'));
echo $Template->parse();
?>Hello, world!
Пример 18. set как «быстрый» способ установить целый массив итераций
<projects>
{{BEGIN project}}
<project label="{{$url}}" data="{{$id}}"/>
{{END}}
</projects><?php
$data = array (
'project' => array(
0 => array('url' => 'a', 'id' => '1'),
1 => array('url' => 'b', 'id' => '2'),
2 => array('url' => 'c', 'id' => '3'),
)
);
$Template = new Blitz('tpl');
$Template->set($data);
echo $Template->parse();
?><projects> <project label="a" data="1"/> <project label="b" data="2"/> <project label="c" data="3"/> </projects>
Один такой здоровый массив описывает N итераций, причем массивы могут быть любого уровня вложенности — короче можно не вызывать context/iterate/set, а сначала «приготовить» такую вот структуру данных, а потом одним махом засадить эти итерации в шаблон — иногда это удобно (к примеру, вместе с PDO::fetchAll(PDO::FETCH_ASSOC)) и вообще говоря, это работает очень быстро (blitz ctx arrays в benchmarks).
См. также set_global(), block().
(blitz >= 0.4)
set_global — установка глобальных переменныхПример 19. set_global
I am {{$local}} variable.
I am {{$global}} variable.
{{BEGIN context}}
I am {{$local}} variable.
I am {{$global}} variable.
{{END}}<?php
$Template = new Blitz('tpl');
$Template->set(array('local' => 'local (root)'));
$Template->set_global(array('global' => 'global'));
$Template->block('context', array('local' => 'local (context)'));
echo $Template->parse();
?>I am local (root) variable. I am global variable. I am local (context) variable. I am global variable.
См. также set().
Содержание
(blitz >= 0.1)
if — отобразить в зависимости от истинности предиката либо один аргумент, либо другойПример 20. if
{{ $num }}. {{ $name }} {{ if($rip,'[R.I.P.]') }}<?
$T = new Blitz('tpl');
$character = array(
array(1,'The Dude',0),
array(2,'Walter Sobchak',0),
array(3,'Donny',1),
array(4,'Maude Lebowski',0),
array(5,'The Big Lebowski',0),
array(6,'Brandt',0),
array(7,'Jesus Quintana',0),
);
foreach ($character as $i => $data) {
echo $T->parse(
array(
'num' => $data[0],
'name' => $data[1],
'rip' => $data[2]
)
);
}
?>1. The Dude 2. Walter Sobchak 3. Donny [R.I.P.] 4. Maude Lebowski 5. The Big Lebowski 6. Brandt 7. Jesus Quintana
В данном примере использована укороченная форма if.
(blitz >= 0.1)
include — подключить шаблонПример 21. include
1.tpl
Мама {{ include('other.tpl') }} раму2.tpl
мыла
<?
$T = new Blitz('tpl');
echo $T->parse();
echo "\n";
?>Мама мыла раму
Если вы включаете один шаблон в другой, то включаемый шаблон наследует все переменные внешнего шаблона.
Пример 22. include: наследование всех переменных внешнего шаблона
1.tpl
переменная a = {{ $a }}
внутренний шаблон: {{ include('other.tpl') }}
переменная b = {{ $b }}2.tpl
/* переменная a = {{ $a }}, переменная b = {{ $b }} */<?
$T = new Blitz('tpl');
$T->set(array('a' => 'a_value', 'b' => 'b_value'));
echo $T->parse();
echo "\n";
?>переменная a = a_value внутренний шаблон: /* переменная a = a_value, переменная b = b_value */ переменная b = b_value
Пример 23. Иcпользование встроенного метода include лучше создания наследников Blitz лишь ради include
1.tpl
parent value: {{ $parent_val }}
child_value: {{ $child_val }}
===========================================================
{{ test_include }}
===========================================================
parent value: {{ $parent_val }}
child_value: {{ $child_val }}2.tpl
parent method: {{ my_test }}
child value: {{ $child_val }}
parent value: {{ $parent_val }}<?
class BlitzTemplate extends Blitz {
var $titem;
function BlitzTemplate($t,$titem) {
parent::Blitz($t);
$this->set(array('parent_val' => 'some_parent_val'));
$this->titem = $titem;
}
function my_test() {
return 'user method called ('.__CLASS__.','.__LINE__.')';
}
function test_include() {
$result = '';
while($i++<3) {
$result .= $this->include($this->titem,array(
'child_val' => 'i_'.$i
));
}
return $result;
}
}
$T = new BlitzTemplate('tpl','other.tpl');
echo $T->parse();
?>parent value: some_parent_val child_value: =========================================================== parent method: user method called (blitztemplate,13) child value: i_1 parent value: some_parent_val parent method: user method called (blitztemplate,13) child value: i_2 parent value: some_parent_val parent method: user method called (blitztemplate,13) child value: i_3 parent value: some_parent_val =========================================================== parent value: some_parent_val child_value: i_3
При первой обработке шаблона структура всех его тэгов сохраняется, поэтому при последующих вызовах шаблон снова не анализируется. Обратите внимание на то, что до выполнения метода test_include переменная child_value пуста и не «видна» в шаблоне, но после выполнения видна и содержит последнее установленное значение. Это поведение аналогично тому, что происходит при выполнении php-кода, если бы вместо test_include у нас был include некоторого php-файла, внутри которого бы инициализировалась новая переменная. Внутри внешнего кода до include она имела бы неопределенное значение, но после - уже нет. На самом деле при вызове include сначала все параметры вызова include добавляются к уже установленным параметрам шаблона, и уже после этого происходит выполнение кода, поэтому ничего удивительного в таком поведении нет. Эту особенность следует иметь в виду, чтобы случайно не «затереть» ранее установленную переменную.