Extremely fast and powerfull template engine for very big internet projects
Downloads: sourceforge, sources homepage, win32 binaries 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
|
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
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:
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...
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 wouldnt 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 ;)
Unfortunately, I dont 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 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 | |
---|---|
method | requests per second |
PHP mess | 1080 |
blitz | 801 |
PHP includes | 737 |
php_templates | 610 |
Smarty | 600 |
Google cTemplate | 504 |
Sigma | 165 |
XTemplate | 130 |
FastTemplate | 84 |
ab -n5000 -c30, ZPS off | |
---|---|
method | requests per second |
PHPmess | 630 |
blitz | 554 |
php_templates | 430 |
PHP includes | 260 |
Smarty | 115 |
XTemplate | 91 |
Sigma | 65 |
FastTemplate | 67 |
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.
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.
Tiny example: to echo template variable $test you need to put the following code in your template: {{ $test }}
Additional config params are
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 test: {{ $a }} and {{ $b }}, iteration number: {{ $i }}
$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 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:
user method call test: {{ my_test(); }}
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)
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()?>
variable: {{ $a }}, method: {{ b(); }}, context: {{ BEGIN ctx }} something inside {{ $something }} {{ END }}
$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 }}
The following internal methods are available in templates:
(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
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.
(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 'a' "test" <a href="null"> <>@#$%^&*()_='"
(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
Blitz class methods:
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!
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 () ================================
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!
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
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));
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
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)
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.
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!
php code
$body = <<load($body); echo $T->parse(array('who' => 'world'));
output
hello, world! Have a lot of fun!...
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.
main.tpl
Hello, {{$object}}!
php code
$Template = new Blitz('tpl'); echo $Template->parse(array('object' => 'world'));
output
Hello, world!
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).
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" }
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" } } }