Blitz Templates

Extremely fast and powerfull template engine for very big internet projects

Downloads: sourceforge, sources homepage, win32 binaries
Other languages: russian chinese

Blitz project is in a quite active development stage, new releases may appear once a week. If you want to get new release announcements - please subscribe at freashmeat or sourceforge file releases RSS. If you like this project ant want to help it - I'm always open for any constructive suggestions.

Document by: Alexey Rybak, Lioubov Vereschagina, Vladimir Krivosheev
Feedback: contact developers and users through the mailing list (blitz-php at googlegroups d0t com) or visit the BugTraker



!!!NEW!!! DOCS (ALMOST FINISHED) WITH NEW FEATURES LIKE PLUGINS OR IF/ELSE/ELSEIF STATEMENTS

Blitz has a very short GEEK TUTORIAL which can save your time.

IMPORTANT: the documentation is too old and should be updated. Here are some news not mentioned in docs:
1) variable scope extension
2) if/elseif/else constructs
3) plugins

Downloads: http://alexeyrybak.com/blitz/blitz-0.7.1.6-devel.tar.gz

Here come some details about these features

1)  Variable scope

You don't need to pass parameters from parent to child contexts
anymore. When blitz executor fails on finding corresponding parameter
in the current iteration data, it will go upper back to parent and
check if variable is there. If it fails again - upper check again. The
depth of "back" lookups is controlled by blitz.scope_lookup_limit,
which is set to zero by default (this meat that feature is disabled by
default).

2) IF/ELSEIF/ELSE constructs

It's a simple as {{ IF $a }} if-a {{ ELSEIF $b }} else-if-b {{ ELSE }}
default {{ END }}
{{ UNLESS $a }} will work instead of {{ IF $a }} as well , but no
"ELSEUNLESS" is provided.
Expressions are not supported yet (will be supported).

3) Plugins

We had a discussion with fixxxer a year ago (OMFG!) and we seemed to
find the final plugin design - see comments here
http://alexeyrybak.com/blitz/bt/view.php?id=100
But this design seems too complicated for me now. So firstly I did the
mostly simple thing. {{ fcall($a) }} is method-call
if this method exists, or just a php-function call if the method was
not found in the class function table. {{ SomePlugin::fcall($a) }}
works as well. That's all - as simple as possible.
This all means that now you are totally free to use any PHP function
calls from Blitz templates, create your custom plugins etc, like
{{ strtolower($some) }} (built-in PHP function) or {{
str::random($len); }} (calling "random" method from "str" plugin).
The only question is: does anybody want to have limiting functionality
like registerPlugin to enable just those functions/plugins
that were explicitly allowed. Please reply if you have any idea what's
more useful here.

Well, that's it! Have a lot of fun.

UPDATE (02 April 2010 - version 0.7.1.7-devel):
To make plugins smarter new hints were added: 
- {{ php::do() }} calls php-function
- {{ this::do() }} calls method through the current template object



TABLE OF CONTENTS

Note: depending on your religious views you may use either CamelCaps or under_bars coding style. For any doSomething blitz can do_something as well.

Introduction and quick start

The most probably question you ask, viewing this document for the first time is: "What? Yet another template engine? What for?". Here comes a very short explanation.

Blitz is a templating engine with three main features:

Most of templating strategies cannot prevent appearance of spaghetti-mess-of-code which is obviously quite unpleasant to dig in. The most important goal of blitz was to help building fast and clear presentation level code for very big applications with extremely complex presentation logic (with a lot of customized user pages, web 2.0 stuff and so on).

General Blitz concept is: view-level of the web application can be based on two components: view controllers, custom PHP-objects encapsulating most of view logic with no HTML-code inside, and weakly active templates, with all of the HTML-code, templating variables and weak logic, including user method calls, includes and simple conditional output. The idea of weak logic comes from a very simple approach, worked out from big internet systems development. To prevent a spaghetti mess and being able to maintain and make changes into you project not in death stress...

Additionally Blitz supports a sub-templating strategy based on contexts - nested templates inside one file, that helps a lot when working with very complex templates. In most of cases contexts help to create very fast applications as well.

To explain what this all mean - let's dig in examples just to illustrate in general what we are talking about. Imagine we have a very simple page, which has a default header, footer and a list of news where some news are marked as "new" depending on some attribute. We will show 2 examples of how this can be done using blitz - with contexts and without. The first example has no contexts. We will use one template controller and 4 templates: main template, item template, header and footer.

Our main template (main.tpl) is

<html>
<head><title>{{ $title }}</title></head>
<body>
{{ include('header.tpl') }}

{{ list_news() }}

{{ include('footer.tpl') }}
</body>
</html>

Here {{ $title }} is a trivial variable output and include is an include operation, available in templates. We will omit header and footer details - just note that they are parsed and included from our main template. list_news is a custom method provided by a template controller:

/* controller class */
class View extends Blitz {
    var $news = array();

    function View($tmpl_name) {
        return parent::Blitz($tmpl_name);	
    }
	
    function set_news($data) {
        $this->news = $data;
    }
	
    function list_news() {
        $result = '';
        foreach($this->news as $i_news) {
            $result .= $this->include('news_list_item.tpl', $i_news);
        }
        return $result;
    }
}

/* news data, in real applications comes from database or somewhere else */
$news = array(
    array('name' => 'My Bonnie lies over the ocean', 'is_new' => 0),
    array('name' => 'My Bonnie lies over the sea', 'is_new' => 1)
)

/* the output */
$T = new View('main.tpl');
$T->set_news($news);
$T->set(array('title' => 'Blitz Example'));
echo $T->parse();

Here View is main template controller class, derived from the parent class Blitz, defined in extension. We initialise controller, set the data and call parse method, provided by the parent. Main template has a user method call - {{ list_news() }}. This method is provided by a template controller. It builds a list of news by passing news data for each of the item simply joining the results. Note that you need to use {{ list_news() }} with brackets, {{ list_news }} is equivalent to {{ $list_news }}. The returned variable is a string of resulting HTML code to be put instead of {{ list_news() }} in main.tpl.The include method is provided by the parent.

Here is the item template, new_list_item.tpl:

<b>{{ $name }}</b>{{ if($is_new, '<div class="isNew">NEW!</div>') }}<br>

Here $name and $is_new are template variables, passed in $i_news associate array from list_news: $name is just put into HTML and $is_new is used in a conditional output. Thus we took a closer look into templates, controllers, method calls, includes and conditional output using this short example.

The second example shows how to deal with contexts. Let's rewrite our main template in the following form:

<html>
<head><title>{{ $title }}</title></head>
<body>
{{ include('header.tpl') }}

{{ BEGIN news_list_item}}<b>{{ $name }}</b>{{ if($is_new, '<div class="isNew">NEW!</div>') }}<br>{{ END }}

{{ include('footer.tpl') }}
</body>
</html>

We took the code from news_list_item.tpl and put it into the main template - into a context named news_list_item. Now we can rewrite our controller. We do not need any custom user methods here, that's why we can use just Blitz object:

/* news data, in real applications comes from database or somewhere else */
$news = array(
    array('name' => 'My Bonnie lies over the ocean', 'is_new' => 0),
    array('name' => 'My Bonnie lies over the sea', 'is_new' => 1)
)

/* the output */
$T = new Blitz('main.tpl');
$T->set(array('title' => 'Blitz Example'));

foreach($news as $i_news) {
    $T->block('/news_list_item', $i_news);
}

echo $T->parse();

Here we use block method to iterate contexts. The context is addressed by its relatively path '/news_list_item' and is iterated for each of the news array element. All the item data is passed to context in $i_news array and all other logic works quite the same as in prevoius example. In general you can put as many nested contexts as you like, the nesting level is unlimited. Theoretically it's possible to deal with any complex logic using contexts only - just switching from one context to another and iterating them from controller code. The detailed discussion of how to do that with blitz can be found below.

That's all for a quick start. There is no HTML-PHP-mess, HTML code is separated from PHP and there is simple and clear code in templates:

Actually these examples are too simple to claim we found the silver bullet (and there is no one as you probably heard). But when your project grows and you have to make lot of changes, when your logic becomes very complex - this kind of separation really helps. I wouldn’t like to follow the academic tradition and make a detailed research of other products here. Anyway if you managed to use pure PHP without any other stuff effectively in a big project — you are very lucky. You use the most effective way (from the perfomance view), and if it is handy enough and suits your needs — stay with it. Otherwise try Blitz. Perhaps you will be nicely surprised ;)

Warning

Blitz project status is half-experimental, but starting with 0.5.7 version it's quite stable and can be used in production code. However 100 % stability and backward compatibility are not guaranteed till version 1.0

Performance benchmarks

Unfortunately, I don’t know any simple, universal and really correct method to analyse template engine performance. The only one right way of doing that is to build your application using several template engines and measure the performance under the real conditions. Shall it be noted that nobody do that? On the other hand results of any artificial or synthetic tests should be used very carefully. Nevertheless, results of two synthetic tests are listed here.

The first is a classic one, measures the speed of cycle template iterations. This test is very simple and can be used only to classify engine as “normal”, “slow” or “grotty”. The number of iterations and variables was taken by default (9 variables, 50 iterations), the results of this test is listed in Table 1. It is easy to see that Blitz is not an outsider at least.

Table 1. Classic and very simple benchmarks

1	blitz                   0.000224 100%
2	php                     0.000351 157%
3	vsprintf                0.000706 315%
4	php_templates           0.00076  339%
5	smarty                  0.001485 663%
6	str_replace             0.001523 680%
7	phemplate               0.002603 1162%
8	fasttemplate            0.005027 2244%
9	xslt                    0.006355 2837%
10	Sigma                   0.007495 3346%
11	IT                      0.008885 3967%
12	vtemplate               0.009021 4027%
13	ultratemplate           0.012261 5474%
14	bugitemplate            0.017534 7828%
15	phplib                  0.018989 8477%
16	templatepower           0.020982 9367%
17	tinybutstrong           0.025323 11305%
18	xtemplate               0.0436   19464%

Server: Linux 2.6.22-vs2.3.0.28-gentoo #1 SMP Mon Oct 22 14:11:34 MSD
2007 x86_64 Intel(R) Xeon(R) CPU 5130 @ 2.00GHz GenuineIntel GNU/Linux

Thanx to Ilya Kantor who provided these tests

You can try on-line tests to verify that, or you can download the benchmarking code and obtain all the results by yourself. If you didn't find your favourite engine in the list - you can download the test, add your engine and contact Ilya to upgrade online test.

The second test is much closer to the real world conditions. A page of several non-trivial parts is built by a webserver, and the performance is benchmarked using the standard apache ab utility. The page simulates a some pseudo portal main page and contains:

The following methods were tested:

The results of this test can be found below.

Table 2. The much closer to the real world conditions test

Server: 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-modules, CFLAGS -g -O2
ab -n5000 -c30, ZPS on
methodrequests per second
PHP mess1080
blitz801
PHP includes737
php_templates610
Smarty600
Google cTemplate504
Sigma165
XTemplate130
FastTemplate84

ab -n5000 -c30, ZPS off
methodrequests per second
PHPmess630
blitz554
php_templates430
PHP includes260
Smarty 115
XTemplate91
Sigma65
FastTemplate67

software versions and settings:

PHP 4.3.10, ZPS 4.0.2
Sigma 1.1.5 (cache on)
Smarty 2.6.15 (tpl-compile on, output-cache off)
Blitz 0.4.3
FastTemplate 1.1.0
XTemplate 0.3.0
php_templates 1.7
cTemplate 0.8 (ctemplate 0.4, nothreads)

Unfortunately I had no opportunity to make tests for other engines, but the benchmark code is available to download and one can add benchmark code for any other template engine. That's why these results can be interpreted in a very generalized way. Native PHP-code is obviuosly the fastest. What is more important, "native" here means written by hands, not "compiled". If you ever looked at a "compiled" template you probably know that its code has a lot of nested constructions, quite hard to execute, much more complex than written by right hands code ;) One more important thing is obvious as well: the less files you have the most effective your code is, and Blitz context methods are faster than include. I used single file methods for both php_templates and Smarty to squeeze maximum from them, so i suppose Blitz to be visibly faster.

The difference between blitz and commonly used "php includes" method is not very big. In real world application any difference will be likely hidden against the background of other code especially database requests et cetera. Thus both of the methods could be considered as practically equal. You can notice that most of the tests were made using an accelerator from ZPS. Using accelerator is very significant for PHP code - but you possibly can obtain different results using other products like eAccelerator. In general, one should not to rely on the quoted results entirely. Make tests, play with the real projects and choose solutions, which give a profit under your own particular conditions.

Installation

Blitz is a PHP extension distributed in source code (Win32 users can download compiled DLL's).

To extract the code and build the extension follow these steps:

tar zxvf blitz.tar.gz
cd blitz
phpize
./configure
make
make install

To test blitz run the test script run-tests.sh.
NOTE! You need to edit this file to change the path to the standart PHP test routine run-tests which comes with PHP. Running the tests you will get something like this:

fisher@fisher:~/prj/blitz> ./run-tests.sh
=====================================================================
CWD         : /home/fisher/prj/blitz/tests
PHP         : /usr/local/bin/php
PHP_SAPI    : cli
PHP_VERSION : 4.3.10
ZEND_VERSION: 1.3.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  : /usr/local/lib/php.ini
More .INIs  :
Extra dirs  :
=====================================================================
Running selected tests.
PASS contexts [context.phpt]
PASS errors and warnings: syntax [errors1.phpt]
PASS errors and warnings: execution [errors2.phpt]
PASS fetch#1 [fetch1.phpt]
PASS fetch#2 [fetch2.phpt]
PASS fetch#3 [fetch3.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]

If Blitz was build as shared module you will probably need to edit your php.ini and include Blitz in extension list:

extension=blitz.so 

Blitz was tested on Linux 2.6 (i386) and Windows XP.

Configuration

Blitz has 3 configuration parameters to be changed via php.ini or ini_set() call:

Tiny example: to echo template variable $test you need to put the following code in your template: {{ $test }}

Additional config params are

Basics: variables, methods, contexts

Blitz is based on two things: templates and controllers. Templates are HTML files with blitz code. Controllers are PHP-classes which control templates, based on Blitz class, provided by extension.

Variables

Here is the detailed example how variables work
main.tpl
Variables test: {{ $a }} and {{ $b }}, iteration number: {{ $i }}
test.php
$T = new Blitz('main.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
        )
    );
}

Output

Variables test: var_0 and var_1, iteration number: 0
Variables test: var_2 and var_3, iteration number: 1
Variables test: var_4 and var_5, iteration number: 2
Variables test: var_6 and var_7, iteration number: 3
Variables test: var_8 and var_9, iteration number: 4
Variables test: var_10 and var_11, iteration number: 5
Variables test: var_12 and var_13, iteration number: 6
Variables test: var_14 and var_15, iteration number: 7
Variables test: var_16 and var_17, iteration number: 8
Variables test: var_18 and var_19, iteration number: 9

You can use complex variables in templates as well:

class DummyObjWithSubProperty {
   var $sub;
   function DummyObjWithSubProperty($val) {
       $this->sub = array('key' => $val);
   }
}

$data = array(
	'hash' => array(
		'subhash' => array(
			'key' => 'v1'
		),
	),
	'obj' => new DummyObjWithSubProperty('v2')
);

$T = new Blitz();
$T->load('Array: {{ $hash.subhash.key }}, Object: {{ $obj.sub.key }} ');
echo $T->parse($data);

Output

Array: v1, Object: v2 

User methods

User methods are just "callbacks". The idea of user methods is very simple: your controller class has some method, wich can be used in a template file. You need to extend basic Blitz class to do this - here is the example:

main.tpl
user method call test: {{ my_test(); }}
test.php
class View extends Blitz {
    function my_test() {
        return 'user method called ('.__CLASS__.','.__LINE__.')';
    }
}

$T = new View('main.tpl');
echo $T->parse();

Output

user method call test: user method called (View,5)
Note that {{ my_test }} code without brackets is equivalent to {{ $my_test }}, and my_test methow will not be called.

Contexts

To understand the power of Blitz you must understand contexts and iterations. It's not trivial, but it's very powerfull.

Contexts were added to Blitz v 0.4, the basis of contexts idea was taken from php_templates, but some context logic was revised. Here goes a brief explanation of what contexts are. Usually we have "plain" templates. These templates have variables or some operators or calls - but any block of programming code in template will be executed. Contexts are subtemplates, their code is used on demand. Having the php-code

variable: <?=$a?>, method: <?=b()?>
all of these pieces of code will be executed. Now look at the following Blitz code
variable: {{ $a }}, method: {{ b(); }}, context: {{ BEGIN ctx }} something inside {{ $something }} {{ END }}
Here "ctx" is context, which is not used by default - the output will end up with "context: " and that's all. To use a context the controller needs to iterate it:
$T = new Blitz('main.tpl');
$T->iterate('ctx');
echo $T->parse();

If the context is iterated once - the output contains the result of context code execution. If the context is iterated several times - the output contains the result of context code execution as many times as the context was iterated. Surely variables for each iteration can be different. Thus to build the simple list we just need to iterate the item context for each of the items with corresponding item data:

$T = new Blitz('main.tpl');
while($i++<10) {
    $T->iterate('ctx', array('something' => 'something_'.$i));
}
echo $T->parse();

The most powerfull feature is that contexts can be enclosed in each other. Every context is defined by its path - /ctx in the example above. Context with path "/root/node/item" is enclosed in context with path "/root/node" which is enclosed in "/root" context. You can iterate parents with some parameters, then children with other parameters, then go to parents again and back to children. Theoretically any page with any complex logic can be build by a single template with contexts. List is a set of context iterations. Sublists are iterations of subcontexts. And conditional output in template can be replaced by a conditional context iteration in controller.

There are two basic context operations: set the current working context and iterate context. Setting means that all calls will affect this context by default. There is a good analogy with working from the command promt: to set the current context to /root/node/item is actually something like to make a cd to /root/node/item directory. And to iterate is something like to "execute".

The context name is case sensitive. You can use context paths in two forms:

All of Blitz features like include, if or method call can be used inside context without any restriction.

Finally, here are several examples of context usage. The following code outputs the well-known greeting, hidden in three nested contexts.

main.tpl

{{ BEGIN root }}
  {{ BEGIN node }}
    {{ BEGIN item }}
        hello, world
    {{ END }}
  {{ END }}
{{ END }}

php code

$T = new Blitz('main.tpl');
$T->iterate('/root/node/item');
echo $T->parse();

output

hello, world

The next example shows how to build simple lists.

main.tpl

{{ BEGIN row }}row #{{ $i }}
{{ END }}

php code

$T = new Blitz('main.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();

output

row #0
row #1
row #2
row #3
row #4
row #0
row #1
row #2
row #3
row #4

The block() method is a usefull substitution of iterate() plus set() series, these both are used together very often. Parent context variables "are not seen" in children contexts by default, so you need to "pass" these variables to children contexts as well. Or you can use setGlobals method instead of set to have true global template varibles.

Next example shows how to build complex lists

main.tpl

{{ BEGIN list; }}
list #{{ $list_num }}
{{ BEGIN list_empty; }} this list is empty {{ END }}{{ BEGIN list_item; }} row #{{ $i_row; }}
{{ END }}
{{ END }}

php code

$T = new Blitz('main.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();

output

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

Single template with nested contexts can cover any view logic. Contexts are very powerfull and can suite all of the developer needs in complex projects.

NOTE. Most of context concept criticism is based on the following: "Why do we have to couple our PHP-code with templates? This model is wrong, all off the logic must be encapsulated in templates". IMHO, this point of view is incorrect. Let's return to the main question what must be separated. Your view logic must be separated from other code. Your HTML should not be mixed with PHP-code. Nobody said your logic must be in your templates. The only one disadvantage of contexts is that you can put a lot of nested contexts and your code will be a mess of their iterations. This should be avoided to keep the code simple and clear.

IF/UNLESS blocks ToDo - this feature needs to be documented in details. Header/footer/empty pattern example:

{{ IF list }}
<table>
{{ BEGIN list }}
<tr><td>{{ $some }}</td></tr>
{{ END }}
</table>
{{ END }}
{{ UNLESS list }}
Fuck it, Dude, let's go bowling
{{ END }}

Template API

The following internal methods are available in templates:

  • if — conditional output
  • include — include other template
  • escape — html output wrapper (works like htmlspeclalchars)
  • date — date output wrapper

    if

    (blitz >= 0.1)
    if — output one or another argument according to the predicate
    string if (string predicate , string output_true [, string output_false ] )

    main.tpl

    {{ $num }}. {{ $name }} {{ if($rip,'[R.I.P.]') }}
    

    php code

    $T = new Blitz('main.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]
           )
       );
    }
    

    output

    1. The Dude
    2. Walter Sobchak
    3. Donny [R.I.P.]
    4. Maude Lebowski
    5. The Big Lebowski
    6. Brandt
    7. Jesus Quintana
    
    Here the short form of if is used.

    include

    (blitz >= 0.1)
    include — include external template
    string include ( string template_name)

    1.tpl
    Where's the {{ include('2.tpl') }} Lebowski?

    2.tpl
    money

    php code

    $T = new Blitz('tpl');
    echo $T->parse();
    echo "\n";
    

    output

    Where's the money Lebowski?
    

    Variable scope policy is very simple: global variables are seen in included templates, local - not.

    escape

    (blitz >= 0.4.4)
    escape - html output wrapper

    php code

    $T = new Blitz();
    $T->load('{{ escape($a); }}
    {{ escape($b) }}
    {{ escape($c,"ENT_COMPAT") }}
    ');
    
    $T->set(
        array(
            'a' => "this is 'a' \"test\"",
            'b' => '<a href="null">',
            'c' => '<>@#$%^&*()_=\'"'
        )
    );
    
    echo $T->parse();
    
    

    output

    this is &#039;a&#039; &quot;test&quot;
    &lt;a href=&quot;null&quot;&gt;
    &lt;&gt;@#$%^&amp;*()_='&quot;
    

    date

    (blitz >= 0.4.11)
    date - date output wrapper
    string date(string format [, mixed arg])

    {{ date(FORMAT [, ARG]); }} will return a string formatted according to the given format string. When [ARG] is numerical, it is treated as UNIX timestamp integer. Otherwise [ARG] is parsed using internal PHP function "php_parse_date" which recognize a lot of date formats. When ARG is omitted - the current time is used. Format string has the same conversion specifiers as PHP function "strftime".

    php code

    $body = <<<BODY
    {{ date("%d %m %Y %H:%M:%S",\$time_num); }}
    {{ date("%d %m %Y %H:%M:%S",\$time_str); }}
    
    BODY;
    
    $T = new Blitz();
    $T->load($body);
    
    $time_num = mktime(11, 22, 33, 7, 22, 1976);
    $time_str = '1976-07-22 01:02:03';
    $T->set(array(
        'time_num' => $time_num,
        'time_str' => $time_str
    ));
    echo $T->parse();
    
    

    output

    22 07 1976 11:22:33
    22 07 1976 01:02:03
    

    Controller API

    Blitz class methods:

    block

    (blitz >= 0.4)
    block — set context and iterate it
    bool block ( string context_path [, array parameters ] )

    main.tpl

    {{BEGIN welcome}}
    	Hello, {{$object}}!
    {{END}}
    

    php code

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

    output

    Hello, world!	
    

    clean

    (blitz >= 0.4.10)
    clean — clean up context variables and iterations
    bool clean ([string context_path ], [bool warn_notfound])
    $Tpl->clean($path) cleans up context iterations and parameters. Parameter $path is "root" by default ('/'): $Tpl->clean() with no parameter will unset all template variables and iterations. Second parameter tells blitz to generate a PHP-warning when cleaning iteration is not found, is TRUE by default.

    php code

    $body = <<<BODY
    ================================
    context cleaning ({{ \$var }})
    ================================
    <!-- BEGIN a -->context (a)
        {{ \$va }}
        <!-- BEGIN b -->context (b)
            {{ \$vb }}
        <!-- END b -->
    <!-- END -->
    BODY;
    
    $T = new Blitz();
    $T->load($body);
    $T->set(array('var' => 'Hello, World!'));
    $T->block('/a', array('va' => 'va_value'));
    $T->block('/a/b', array('vb' => 'vb_value'));
    echo $T->parse();
    
    $T->clean('/a/b');
    echo $T->parse();
    
    $T->clean('/a');
    echo $T->parse();
    
    $T->block('/a', array('va' => 'va_value_new'));
    $T->iterate('/a/b');
    echo $T->parse();
    
    $T->clean();
    echo $T->parse();
    

    output

    ================================
    context cleaning (Hello, World!)
    ================================
    context (a)
        va_value
        context (b)
            vb_value
        
    ================================
    context cleaning (Hello, World!)
    ================================
    context (a)
        va_value
        
    ================================
    context cleaning (Hello, World!)
    ================================
    ================================
    context cleaning (Hello, World!)
    ================================
    context (a)
        va_value_new
        context (b)
            
        
    ================================
    context cleaning ()
    ================================
    

    context, getContext

    (blitz >= 0.4)
    context — set current context
    bool context (string context_path )
    getContext — get current context
    string getContext (void)

    main.tpl

    {{BEGIN user}}
    	{{BEGIN hello}}Hello, user!{{END}}
    	{{BEGIN goodbye}}Goodbye, user!{{END}}
    {{END}}
    {{BEGIN world}}Hello, world!{{END}}
    

    php code

    $Template = new Blitz('main.tpl');
    echo 'previous context was: '$Template->context('user')."\n";
    echo 'current context is: '$Template->getContext()."\n";
    $Template->block('hello');
    $Template->block('goodbye');
    $Template->block('../world');
    echo $Template->parse();
    

    output

    previous context was: /
    current context is: /user
    
    	Hello, user!
    	Goodbye, user!
    
    Hello, world!
    

    dumpIterations (dump_iterations)

    (blitz >= 0.4)
    dumpIterations(dump_iterations) — dump iteration, debug method
    bool dumpIterations

    main.tpl

    {{BEGIN counter}}
    	{{$i}},
    {{END}}
    

    php code

    $Template = new Blitz('main.tpl');
    for ($i = 0; $i < 3; $i++)
    {
    	$Template->block('context', array('i' => $i));
    }
    $Template->dumpIterations();
    

    output

    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
    

    dumpStruct (dump_struct)

    (blitz >= 0.4)
    dump_struct — dump template structure
    bool dumpStruct

    main.tpl

    {{BEGIN counter}}
    	{{$i}},
    {{END}}
    

    php code

    $Template = new Blitz('main.tpl');
    for ($i = 0; $i < 3; $i++)
    {
    	$Template->block('context', array('i' => $i));
    
    }
    $Template->dumpStruct();
    

    output

    == 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));
    

    fetch

    (blitz >= 0.4)
    fetch — fetch the result of context
    string fetch ( string context_path [, array parameters ])

    main.tpl

    {{ 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 }}
    

    php code

    
    $T = new Blitz('main.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";
    
    // 2 months ago
    $T->iterate('m');
    echo $T->fetch('offline', array('n' => 2))."\n";
    

    output

    online!
     away...
     was online 15 days ago
     was online 2 months ago
    

    hasContext (has_context)

    (blitz >= 0.4)
    hasContext (has_context) — check if context exists
    bool has_context (string context_path)

    main.tpl

    {{BEGIN foo}}{{END}}
    

    php code

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

    output

    bool(true)
    bool(false)
    

    include

    (blitz >= 0.1)
    include - include another template
    string include(string template_name, [array global_vars])

    php code

    class View extends Blitz {
        var $news = array();
    
        function View($tmpl_name) {
            return parent::Blitz($tmpl_name);	
        }
    	
        function set_news($data) {
            $this->news = $data;
        }
    	
        function list_news() {
            $result = '';
            foreach($this->news as $i_news) {
                $result .= $this->include('news_list_item.tpl', $i_news);
            }
            return $result;
        }
    }
    

    Here list_news method gets the result of news_list_item.tpl template execution, passing news data as global variables in included template.

    Blitz supports context iteration when you include template with contexts:

    php code

    $T = new Blitz();
    $T->load('{{ include("include_ctx.tpl") }}');
    
    $i = 0;
    while ($i<5) {
        $T->block('/test1', array('var' => $i), TRUE);
        $T->block('/test2', array('var' => $i + 1), TRUE);
        $i++;
    }
    
    echo $T->parse();
    

    include_ctx.tpl

    test1: {{ begin test1 }} v1={{ $var }} {{ end }}
    test2: {{ begin test2 }} v2={{ $var }} {{ end }}
    

    output

    test1:  v1=0  v1=1  v1=2  v1=3  v1=4 
    test2:  v2=1  v2=2  v2=3  v2=4  v2=5
    

    Note, that we used an additional 3rd parameter in block method. This parameter tells blitz to iterate /test1 and /test2 blocks without any check if corresponding context exists. Include is processed at the execution stage, that's why Blitz knows nothing about included templates structure when setting varibles or iterating contexts.

    iterate

    (blitz >= 0.4)
    iterate — iterate context
    bool iterate ( [ string context_path ] )

    main.tpl

    {{BEGIN hello}}
    	Hello, user!
    {{END}}
    {{BEGIN goodbye}}
    	Goodbye, user!
    {{END}}
    

    php code
    $Template = new Blitz('main.tpl'); $Template->iterate('hello'); $Template->context('goodbye'); $Template->iterate(); echo $Template->parse();

    output

    Hello, user! Goodbye, user!
    

    load

    (blitz >= 0.4)
    load — load template body from PHP string variable
    bool load ( string tpl )

    php code

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

    output

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

    parse

    (blitz >= 0.1)
    parse — build and return the result
    string parse ( [ array global_vars ] )

    main.tpl

    Hello, world!
    

    php code
    $Template = new Blitz('main.tpl'); echo $Template->parse();

    output

    Hello, world!
    

    If any variables were passed in parse call, they will be added to the template globals.

    set

    (blitz >= 0.1)
    set — set variables or iterations
    bool set ( array parameters )

    main.tpl

    Hello, {{$object}}!
    

    php code

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

    output

    Hello, world!
    
    You can use set to set template iterations as well.

    main.tpl

    
    {{BEGIN project}}
    	<project label="{{$url}}" data="{{$id}}"/>
    {{END}}
    
    

    php code

    $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();
    

    output

    <projects>
    
    	<project label="a" data="1"/>
    
    	<project label="b" data="2"/>
    
    	<project label="c" data="3"/>
    
    </projects>
    

    The $data array above is 3 iterations of context project with their local variables url and id. The data structure can be as complex as you like, just follow the rule: numerical keys for context iterations, string keys for context and variable names. You can prepare data like this and set it into the template instead of lot of context/iterate/set calls. Sometimes it is very useful (with PDO::fetchAll(PDO::FETCH_ASSOC) for example), and in most of the cases it's very fast (see blitz ctx arrays in benchmarks above).

    setGlobals (set_globals), getGlobals (get_globals)

    old names are: setGlobal/set_global - they still work
    (blitz >= 0.4)
    setGlobals — set global variables
    bool setGlobals (array parameters)
    getGlobals — get global variables as array
    array getGlobals (void)

    main.tpl

    I am {{$local}} variable.
    I am {{$global}} variable.
    {{BEGIN context}}
    	I am {{$local}} variable.
    	I am {{$global}} variable.
    {{END}}
    

    php code

    $Template = new Blitz('main.tpl');
    $Template->set(array('local' => 'local (root)'));
    $Template->set_globals(array('global' => 'global'));
    $Template->block('context', array('local' => 'local (context)'));
    echo $Template->parse();
    var_dump($Template->getGlobals());
    

    output

    I am local (root) variable.
    I am global variable.
    
    	I am local (context) variable.
    	I am global variable.
    array(1) {
      ["global"]=>
      string(6) "global"
    }
    
    

    getStruct (get_struct), getIterations(get_iterations)

    getStruct — get template structure as array
    array getStruct(void)
    getIterations — get template iterations as array
    array getIterations(void)

    php code

    $Template = new Blitz();
    $Template->load('{{ $var }} {{ BEGIN root }} {{ $root_var }} {{ END }}');
    $Template->set(array('var' => 'value'));
    $Template->iterate();
    $Template->set(
        array(
            'root' => array(
                'root_var' => 'root_value'
            )
        )
    );
    
    var_dump($Template->getStruct());
    var_dump($Template->getIterations());
    

    output

    array(3) {
      [0]=>
      string(4) "/var"
      [1]=>
      string(6) "/root/"
      [2]=>
      string(14) "/root/root_var"
    }
    array(2) {
      [0]=>
      array(1) {
        ["var"]=>
        string(5) "value"
      }
      [1]=>
      array(1) {
        ["root"]=>
        array(1) {
          ["root_var"]=>
          string(10) "root_value"
        }
      }
    }