Blitz Templates

Alexey Rybak

Vladimir Krivosheev


Содержание

I. Введение
1. Зачем еще один парсер шаблонов?
2. Некоторые результаты тестов производительности
II. Установка и настройка
3. Установка
I. Настройка
blitz.tag_open — строка, открывающая тег
blitz.tag_close — строка, закрывающая тег
blitz.var_prefix — префикс для переменных
III. Справочник языка
4. Основы синтаксиса
5. Переменные
6. Пользовательские методы
7. Контексты
IV. Методы
II. Контроллер
block — итерация и установка контекста
context — установка контекста
dump_iterations — дамп итераций
dump_struct — дамп структуры
fetch — использовать контент одного шаблона при работе с другим шаблоном или тело одного контекста в одном шаблоне внутри другого
has_context — проверка существования контекста
iterate — итерация контекста
load — загрузить тело шаблона из переменной PHP
parse — разбор шаблона
set — установка переменных
set_global — установка глобальных переменных
III. Шаблон
if — отобразить в зависимости от истинности предиката либо один аргумент, либо другой
include — подключить шаблон

Список таблиц

2.1. Классический тест производительности
2.2. Тест производительности приближенный к полевым условиям

Список примеров

5.1. Переменные
6.1. Вызов пользовательского метода
6.2. Передача параметров из шаблона в пользовательский метод
7.1. Ужасно доставшее всех знакомое всем приветствие, запрятанное в трех вложенных контекстах
7.2. Работа с простыми списками
7.3. Как при помощи вложенных контекстов строить более сложные списки
7. block
8. context
9. dump_iterations
10. dump_struct
11. fetch
12. has_context
13. iterate
14. load
15. parse без установки переменных
16. parse с установкой переменных
17. set
18. set как «быстрый» способ установить целый массив итераций
19. set_global
20. if
21. include
22. include: наследование всех переменных внешнего шаблона
23. Иcпользование встроенного метода include лучше создания наследников Blitz лишь ради include

Часть I. Введение

Глава 1. Зачем еще один парсер шаблонов?

Blitz родился весьма неоригинально, for fun. Однако, поигравшись с ним немного, мне показалось, что скорость, с которой он работает, и удобства, которые он предоставляет разработчику — стоят того, чтобы дать его поиграться коллегам. Основных «фишек» у Blitz три:

  • написан как PHP-модуль на Си, и является одним из самых быстрых движков;

  • имеет простой и интуитивно понятный синтаксис;

  • позволяет структурировать код удобным и легко читаемым образом.

Blitz поддерживает разделение и скрытие функционально различных частей шаблонов с помощью простого механизма: текст шаблона может содержать вызов пользовательского метода объекта, который этим шаблоном управляет. Таким образом, достигается основная цель: шаблон не содержит большого количества блоков и контекстов, часто мешающих разобраться, что к чему. Напротив, даже в проекте со сложной логикой представления при правильном подходе шаблоны будут давать разработчику своеобразную «карту» всего проекта. Blitz также позволяет включать одни шаблоны в другие (аналог include) и поддерживает условный вывод переменных (аналог if).

Начиная с версии 0.4 в Blitz добавлен функционал движка php_templates. Теперь в Blitz можно создавать множество вложенных шаблонов внутри одного файла, это сильно упрощает жизнь для очень сложных шаблонов с одной стороны и улучшает показатели по производительности для ряда задач с другой. Также с версии 0.4 есть экспериментальная поддержка работы с набором «упакованных» шаблонов (blitzpack), когда несколько шаблонов сначала единожды «компилируются» в бинарный формат, упаковываются в один файл, и затем работа ведется только с этой «бинарной пачкой» шаблонов, экономя время на файловых операциях и разборе шаблонов.

Мне бы не хотелось здесь следовать академической традиции и проводить подробный анализ других проектов. В любом случае, если вы научились эффективно использовать сам PHP в качестве шаблонного движка, обходясь без сторонних продуктов и библиотек — вы счастливый человек. Вы используете самый эффективный с точки зрения производительности подход, и если он вам удобен — придерживайтесь его. Если нет — попробуйте Blitz. Возможно, он вас приятно удивит ;)

Внимание

Статус проекта — полуэкспериментальный, но я считаю текущую версию вполне работоспособной. В силу параноидального отношения к качеству проекта, 100%-я стабильность и обратная совместимость до версии 1.0 не гарантируется.

Глава 2. Некоторые результаты тестов производительности

К сожалению, мне неизвестна ни одна простая, универсальная и по-настоящему корректная методика анализа производительности шаблонных движков. А результатами любых искусственных, или как их ещё принято называть, синтетических, тестов пользоваться нужно максимально осторожно. Тем не менее, здесь приводятся результаты двух тестов. Первый тест — классический, измеряющий скорость выполнения циклических итераций одного и того же шаблона. Тест крайне простой, но позволяющий достаточно условно разделить группы движков на «нормальные», «медленные» и «никуда не годные». Число итераций и переменных в блоке было взято по умолчанию (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 nameTimePercentage
1php0.000544100%
2blitz0.001008185%
3php_templates0.001812333%
4smarty0.002006369%
5str_replace0.003713683%
6phemplate0.004514830%
7fasttemplate0.0068351256%
8vtemplate0.0095651758%
9ultratemplate0.0129932388%
10templatepower0.0170563135%
11bugitemplate0.0199893674%
12phplib0.0280535157%
13profTemplate0.0431047924%
14xtemplate0.0487998970%
Тестовая машина (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 nameTimePercentage
1php0.00045100%
2blitz0.000834185%
3php_templates0.001595354%
4smarty0.001694376%
5str_replace0.00373829%
6phemplate0.004215937%
7fasttemplate0.0061391364%
8vtemplate0.0087551946%
9ultratemplate0.0127472833%
10templatepower0.0186784151%
11bugitemplate0.0192864286%
12phplib0.0254785662%
13profTemplate0.04514810033%
14xtemplate0.04813710697%

Столь сильное отличие 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 mess1150
blitz ctx arrays890
blitz ctx825
php includes770
blitzpack725
blitz incudes680
smarty620
php_templates615
Тестовая машина(B), см. таблицу 1
ab -n20000 -c100, ZPS off
ugly php mess660
blitz ctx arrays590
blitz ctx560
php_templates450
blitzpack440
blitz incudes430
smarty285
php includes125

К сожалению, я не имел возможности провести тесты для других шаблонных движков (впрочем, код для этого теста доступен, вы можете добавить в него решения исходной задачи с использованием любых других средств). Поэтому ограничусь обобщенной интерпретацией этих результатов. То, что native PHP-код вместе с акселератором всегда будут быстрее прочих решений — очевидно. Правда, следует особенно подчеркнуть, что native в этом смысле — именно написанный программистом самим, а не «скомпилированный». В этом легко убедиться, заглянув внутрь любого «скомпилированного» шаблона: как правило, их код состоит из многомерных, довольно сложных для выполнения конструкций, значительно сложнее, чем написанный правильными руками код ;) Поскольку разница между blitz и «правильным» методом php includes не является кардинальной, а все синтетические тесты позволяют лишь выявить группы приблизительно равных, можно с определенной долей уверенности считать методы разработки с использованием php, blitz и php_templates примерно одинаковыми по производительности.

Следует также принять во внимание, что в реальном проекте разница в скорости между различными методами, скорее всего, будет ещё меньше. Во-первых, это связано с тем, что значительное время будет тратиться на работу с источниками данных (СУБД, различные сервисы и проч.). Во-вторых, отношение «количества» кода, относящегося к уровню представления, и прочего кода будет совершенно иным. Грубо говоря, view_code = full_code для тестов и пусть выигрыш на синтетическом тесте составляет даже десятки процентов. Но в реальном проекте часто выполняется соотношение view_code >> full_code, и поэтому выигрыш на уровне представления уже почти ничего не даст. Как вы могли заметить, почти все тесты были проведены с использованием акселератора из ZPS. Вряд ли сейчас можно представить крупный проект, в котором не используется акселератор, однако, акселератор акселератору рознь. И вполне возможно вы получите совершенно иные результаты при использовании, например, eAccelerator'a. В-общем, призываю вас не полагаться полностью на приведенные результаты. Скачивайте тесты, экспериментируйте на реальных задачах, и выбирайте те решения, которые дают выигрыш в вашем проекте.

Часть II. Установка и настройка

Содержание

3. Установка
I. Настройка
blitz.tag_open — строка, открывающая тег
blitz.tag_close — строка, закрывающая тег
blitz.var_prefix — префикс для переменных

Глава 3. Установка

Blitz — расширение PHP, поставляемое пока исключительно в исходных кодах, поэтому его установка состоит из обычных шагов по сборке расширения:

  1. tar zxvf blitz.tar.gz

  2. cd blitz

  3. phpize

  4. make

  5. ./configure

  6. 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.tag_open — строка, открывающая тег
blitz.tag_close — строка, закрывающая тег
blitz.var_prefix — префикс для переменных

blitz.tag_open

(blitz >= 0.1)

blitz.tag_open — строка, открывающая тег

blitz.tag_open = "{{"

PHP_INI_ALL

См. также blitz.tag_close.

blitz.tag_close

(blitz >= 0.1)

blitz.tag_close — строка, закрывающая тег

blitz.tag_close = "}}"

PHP_INI_ALL

См. также blitz.tag_open.

blitz.var_prefix

(blitz >= 0.1)

blitz.var_prefix — префикс для переменных

blitz.var_prefix = "$"

PHP_INI_ALL

Часть III. Справочник языка

Глава 4. Основы синтаксиса

Как и во многих других шаблонных движках двумя базовыми сущностями проекта, которые использует Blitz, являются собственно шаблон и объект, управляющий выполнением этого шаблона (контроллер шаблона).

Класс Blitz — внутренний класс расширения, управляющий шаблоном. Первый и единственный аргумент конструктора класса — имя шаблона.

Глава 5. Переменные

Пример 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

Глава 6. Пользовательские методы

Возможность включать в шаблон пользовательские методы - самая интересная с точки зрения организации хорошего и удобно читаемого кода. До сих пор в примерах использовался стандартный класс 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

Глава 7. Контексты

Начиная с версии 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

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

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

Часть IV. Методы

Содержание

II. Контроллер
block — итерация и установка контекста
context — установка контекста
dump_iterations — дамп итераций
dump_struct — дамп структуры
fetch — использовать контент одного шаблона при работе с другим шаблоном или тело одного контекста в одном шаблоне внутри другого
has_context — проверка существования контекста
iterate — итерация контекста
load — загрузить тело шаблона из переменной PHP
parse — разбор шаблона
set — установка переменных
set_global — установка глобальных переменных
III. Шаблон
if — отобразить в зависимости от истинности предиката либо один аргумент, либо другой
include — подключить шаблон

Контроллер

Содержание

block — итерация и установка контекста
context — установка контекста
dump_iterations — дамп итераций
dump_struct — дамп структуры
fetch — использовать контент одного шаблона при работе с другим шаблоном или тело одного контекста в одном шаблоне внутри другого
has_context — проверка существования контекста
iterate — итерация контекста
load — загрузить тело шаблона из переменной PHP
parse — разбор шаблона
set — установка переменных
set_global — установка глобальных переменных

block

(blitz >= 0.4)

block — итерация и установка контекста

bool block ( string context_path [, array parameters ] )

Пример 7. block

{{BEGIN welcome}}
	Hello, {{$object}}!
{{END}}
<?php

$Template = new Blitz('tpl');
$Template->block('welcome', array('object' => 'world'));
echo $Template->parse();

?>
Hello, world!

См. также context(), iterate(), set().

context

(blitz >= 0.4)

context — установка контекста

bool context ( string context_path )

Пример 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().

dump_iterations

(blitz >= 0.4)

dump_iterations — дамп итераций

bool 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().

dump_struct

(blitz >= 0.4)

dump_struct — дамп структуры

bool 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().

fetch

(blitz >= 0.4)

fetch — использовать контент одного шаблона при работе с другим шаблоном или тело одного контекста в одном шаблоне внутри другого

string fetch ( string name [, array parameters ] )

Пример 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 получает результат исполнения последней итерации контекста.

has_context

(blitz >= 0.4)

has_context — проверка существования контекста

bool has_context ( string context_path )

Пример 12. has_context

{{BEGIN foo}}{{END}}
<?php

$Template = new Blitz('tpl');
var_dump($Template->has_context('foo'));
var_dump($Template->has_context('bar'));

?>
bool(true)
bool(false)

iterate

(blitz >= 0.4)

iterate — итерация контекста

bool iterate ( [ string context_path ] )

Пример 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!

См. также block(), context().

load

(blitz >= 0.4)

load — загрузить тело шаблона из переменной PHP

bool load ( string tpl )

Пример 14. load

<?

$body = <<<BODY
hello, {{ \$who }}!
{{ bye; }}

BODY;

class View extends Blitz {
    function bye() {
        return "Have a lot of fun!...";
    }
}

$T = new View();
$T->load($body);
echo $T->parse(array('who' => 'world'));

?>
hello, world!
Have a lot of fun!...

parse

(blitz >= 0.1)

parse — разбор шаблона

string parse ( [ array parameters ] )

Пример 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!

set

(blitz >= 0.1)

set — установка переменных

bool set ( array parameters )

Пример 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().

set_global

(blitz >= 0.4)

set_global — установка глобальных переменных

bool set ( array parameters )

Пример 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().

Шаблон

Содержание

if — отобразить в зависимости от истинности предиката либо один аргумент, либо другой
include — подключить шаблон

if

(blitz >= 0.1)

if — отобразить в зависимости от истинности предиката либо один аргумент, либо другой

string if ( string a , string b [, string c ] )

Пример 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.

include

(blitz >= 0.1)

include — подключить шаблон

string include ( string tpl )

Пример 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 добавляются к уже установленным параметрам шаблона, и уже после этого происходит выполнение кода, поэтому ничего удивительного в таком поведении нет. Эту особенность следует иметь в виду, чтобы случайно не «затереть» ранее установленную переменную.