fast and powerfull template engine for very big internet projects

becamein our traffic rating (after- which I just don't count as it's obvious - but leaving Ukraine far below!). Thanks a lot, folks! Also many thanks to users from Ukraine, Belarus, Netherlands, Kazakhstan, UK, USA, Brazil, Spain, France and Poland.

Developers guide

0. Quick geek tutorial

If you are experienced developer who just needs to start working with Blitz very quickly - read this "Quick Geek Tutorial". It's quite old though - please refer to the Template or View API for new methods and features.

1. Introduction

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 two main features:

Fast. Blitz is written in C and built as PHP-extension which makes it one of the fastest template engines (you may see the benchmarks section below)

Clear. Blitz has quite simple and clear syntax and makes developer to build compact and easy-to-read-and-support code even for applications with very complex presentation logic

2. Installation

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

fisher@fisher:~/prj/blitz> ./run-tests.sh ===================================================================== PHP : /usr/local/bin/php5 PHP_SAPI : cli PHP_VERSION : 5.3.2-dev ZEND_VERSION: 2.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 : /local/php_5_3_fpm/lib/php-cli.ini More .INIs : CWD : /home/fisher/prj/blitz Extra dirs : VALGRIND : Not used ===================================================================== Running selected tests. PASS block [tests/block.phpt] PASS Bug #83 (double free on include()) [tests/bug83.phpt] PASS clean [tests/clean.phpt] PASS comments [tests/comments.phpt] PASS contexts [tests/context.phpt] PASS date output wrapper [tests/date.phpt] PASS double Blitz object initialization [tests/double_init.phpt] PASS report duplicate contexts [tests/duplicates_001.phpt] PASS broken templates 001 [tests/error_001.phpt] PASS broken templates 002 [tests/error_002.phpt] PASS errors and warnings: syntax [tests/errors1.phpt] PASS errors and warnings: execution [tests/errors2.phpt] PASS escape output wrapper [tests/escape.phpt] PASS fetch#1 [tests/fetch1.phpt] PASS fetch#2 [tests/fetch2.phpt] PASS fetch#3 [tests/fetch3.phpt] PASS get context [tests/get_context.phpt] PASS has context [tests/has_context.phpt] PASS if/unless predefined methods [tests/if.phpt] PASS conditional contexts (multi-line conditions): if/end and unless/end [tests/if_context.phpt] PASS controller include method [tests/include-method.phpt] PASS predefined methods: include [tests/include.phpt] PASS include with context iteration [tests/include_ctx.phpt] PASS multiple include cache test [tests/include_multi.phpt] PASS ini-values settings test [tests/ini.phpt] PASS nonexistant path iteration [tests/iterate_nonexistant.phpt] PASS user-defined methods [tests/method.phpt] PASS method call from inner include [tests/mfi.phpt] PASS mix #1 [tests/mix1.phpt] PASS mix #2 [tests/mix2.phpt] PASS mix #3 [tests/mix3.phpt] PASS mix #4 [tests/mix4.phpt] PASS mix #5 [tests/mix5.phpt] PASS mix #6 [tests/mix6.phpt] PASS numerical and non-numerical keys in the iteration set [tests/non_num_iter.phpt] PASS parse with iteration set [tests/parse_with_iterations.phpt] PASS relative path (blitz.path) [tests/path.phpt] PASS partial php_templates syntax compability [tests/phpt_compability.phpt] PASS plugins [tests/plugins.phpt] PASS predefined loop variables: $_total, $_num, $_even, $_odd, $_first, $_last [tests/predefined.phpt] PASS returning non-strings from user methods [tests/return_non_string.phpt] PASS scope lookup test #1 [tests/scope.phpt] PASS scope lookup test #2 [tests/scope2.phpt] PASS set and get [tests/set_and_get.phpt] PASS mixed set [tests/set_mixed.phpt] PASS remove empty spaces around context tags [tests/spaces.phpt] PASS variables [tests/var.phpt] PASS unprefixed variables syntax [tests/var_no_prefix.phpt] PASS {{ $hash.sub.key }} syntax [tests/var_path.phpt] PASS iterate after wrong previous iterations [tests/wrong_iterations.phpt] ===================================================================== Number of tests : 50 50 Tests skipped : 0 ( 0.0%) -------- Tests warned : 0 ( 0.0%) ( 0.0%) Tests failed : 0 ( 0.0%) ( 0.0%) Expected fail : 0 ( 0.0%) ( 0.0%) Tests passed : 50 (100.0%) (100.0%) --------------------------------------------------------------------- Time taken : 1 seconds =====================================================================

extension=blitz.so

3. Configuration

blitz.var_prefix - variable prefix, default is "$"

blitz.tag_open - open tag, default is "{{" (double brackets are used not to confuse with single CSS brackets)

blitz.tag_close - close tag, default is "}}"

blitz.tag_open_alt - alternative open tag, default is "<!-- " (Note: 5 symbols with space in the end)

blitz.tag_close_alt - alternative close tag, default is " -->" (Note: 4 symbols with space in the beginning)

blitz.comment_open - open comments tag, default is "/*"

blitz.comment_close - close comment tag, default is "*/"

blitz.enable_alternative_tags - use 0/1 to disable/enable alternative tags, "1" by default

blitz.enable_comments - use 1/0 to enable/disable comments, "0" by default

blitz.path - prefix filenames when they don't start with '/' (UNIX/Linux) or '[A-Z]:\' (Windows), default is ""

blitz.disable_include - disable includes for true paranoid, "0" by default

blitz.remove_spaces_around_context_tags - remove annoying spaces and linebreaks around context tags, "1" by default

blitz.warn_context_duplicates - warn if there are contexts with the same name, "0" by default

blitz.check_recursion - check recursion, 0/1, "1" by default

blitz.charset - charset for "escape" method (used in internal php_escape_html_entities call), "" by default

blitz.scope_lookup_limit - the depth of "upper stack" lookups when variable is not found in current context scope, default is "0"



Please note some differences with old 0.6.* versions:





blitz.tag_open_alt and blitz.tag_close_alt were called blitz.phpt_ctx_left and blitz.phpt_ctx_right correspondingly

blitz.remove_spaces_around_context_tags was switched off by default

blitz.comment_open, blitz.comment_close, blitz.enable_alternative_tags, blitz.enable_comments, blitz.charset, blitz.scope_lookup_limit were added in 0.7.* only.



4. Basics: variables, contexts, methods 4.1. Variables The "view" component of your application will be built by templates and views("views" are also referred as template controllers. Let's start with the following example: $View = new Blitz(); $View->load('Where is the {{ $what }}, Lebowski?'); $View->display(array('what' => 'money')); template loaded as string and a template controller $View.



Normally you'll load templates from files: $View = new Blitz('some.tpl');



Template is an HTML file with some very simple syntax. No complex code mixed with HTML. Controller is a template logic master, an instance of Blitz class which operates template from opening to the final result. Template controller is not your application controller which stands for "C" in MVC. No HTML code inside template controller is needed.



4.2. Contexts The reason why Blitz objects are called "template controllers" is simple. From the very early days template language in Blitz was designed to be as simple ("non-programming") as possible. For example, there is still no "for" or "foreach" statement in Blitz. This surely doesn't mean you can't do any looping :) But you have to loop from your PHP-code, this is called "passive" templates (in Blitz you can do a lot of "active" templating as well - conditions, callbacks, plugins - but loops like "foreach", complex expressions, all these "programming" statements are still under the law).



To make a loop you need to use blocks in your templates. Block, or context, is a part of template marked by {{ BEGIN }} and {{ END }} tags. Blocks can be populated on demand but hidden by default: $View = new Blitz(); $View->load('hello {{ BEGIN block }} {{ $name }} {{ END }}'); $T->display(array('block' => array('name' => 'Dude')));



To make the code simpler to read one can put a block name after END statement: {{ END block }}.



There is a special 'block' method that will perobably make your code a bit easier to read: $View = new Blitz(); $View->load('hello {{ BEGIN block }} {{ $name }} {{ END block }}'); $View->block('/block', array('name' => 'Dude')); $View->display(); foreach (array('Dude', 'Donny', 'Sobchak') as $i_name) { $T->block('/block', array('name' => $i_name); } "Hello Dude Donny Sobchak "



Every block is identified by it's path like "/block". A block placed inside another has path /parent/child. Even things like '../../some/path' work.



As you could see there can be two ways of "controlling" template blocks. The first is to use "block" methods (there are also additional methods to set up active context, to iterate the current context and so on). The second is just to set up complex data structures with keys named equal to block names. In previous examples we set up a hash for 'block'. To produce a list you just need this hash to be an array of hashes: $View = new Blitz(); $View->load('hello {{ BEGIN block }} {{ $name }} {{ END }}'); $T->display( array('block' => array( array('name' => 'Dude'), array('name' => 'Donny'), array('name' => 'Sobchak'), )) );



Honestly, using blocks as "if" substitution is crazy :) See what you have to do if you just need a simple comma separated list in the previous example. At first you have to add a special separator block: hello {{ BEGIN block }}{{ BEGIN comma }},{{ END }} {{ $name }} {{ END }} $need_comma = FALSE; foreach (array('Dude', 'Sobchak', 'Donny') as $i_name) { if ($need_comma) { $T->block('/block/comma'); } else $need_comma = TRUE; } $T->block('/block', array('name' => $i_name); } hello {{ BEGIN block }}{{ if($_first,'',',') }} {{ $name }} {{ END }}. foreach (array('Dude', 'Donny', 'Sobchak') as $i_name) { $T->block('/block', array('name' => $i_name); }



Here we used a short form of "if" as a "method". You can use condition blocks as well: hello {{ BEGIN block }} {{ UNLESS $_first }}, {{ END }} {{ $name }} {{ END }}.



4.3. Variable scope and contexts Let's finally take a complex list example $body = <<<BODY {{ BEGIN list }} ================================================== list #{{ \$_num }}, x = {{ \$x }} {{ UNLESS sublist }} empty {{ ELSE }} -------------------------------------------------- {{ BEGIN sublist }} row #{{ \$_num; }} v = {{ \$v }}, x = {{ \$x }} {{ END }} {{ END }} {{ END }} BODY; $T = new Blitz(); $T->load($body); $data = array( 'list' => array( 0 => array( 'x' => 'first' ), 1 => array( 'x' => 'second', 'sublist' => array( 0 => array('v' => 'a'), 1 => array('v' => 'b'), ) ) ) ); $T->display($data); ================================================== list #1, x = first empty ================================================== list #2, x = second -------------------------------------------------- row #1 v = a, x = row #2 v = b, x =

pass it via params manually for every context iteration

pass it as global once

force Blitz to make "upper" lookups when parameter was not found



Globals are set by setGlobals() method: // body remains the same ... $T = new Blitz(); $T->load($body); // data remains the same ... $T->setGlobals(array('x' => 'global x')); $T->display($data); ================================================== list #1, x = first empty ================================================== list #2, x = second -------------------------------------------------- row #1 v = a, x = global x row #2 v = b, x = global x ini_set('blitz.scope_lookup_limit', 1); ================================================== list #1, x = first empty ================================================== list #2, x = second -------------------------------------------------- row #1 v = a, x = second row #2 v = b, x = second



4.4. Includes In previous section you could see an example of so called "internal method" - if(). These are statements like function calls in the template code. Blitz has several internal methods like this - and one of them is include(). Just say: {{ inlcude("some.tpl") }} or {{ include($file) }} - that's it. Works same way as PHP itself. How does variable scope work with include? Right the same way as if the include statement was replaced by the content from the included file. Let's see the example. Included template represents the list of names: Cast: {{ BEGIN cast }}{{ UNLESS $_first }}, {{ END }}{{ $name }}{{ END }} $T = new Blitz(); $T->load('{{ include("include_ctx.tpl") }}'); $data = array( array('name' => 'Jeff Bridges'), array('name' => 'John Goodman'), array('name' => 'Julianne Moore') array('name' => 'Steve Buscemi') ); $T->set(array('cast' => $data)); $T->display(); Cast: Jeff Bridges, John Goodman, Julianne Moore, Steve Buscemi Cast: {{ BEGIN cast }}{{ UNLESS $_first }}, {{ END }}{{ $name }}{{ END }} $T = new Blitz(); $T->load('{{ include("include_ctx.tpl") }}'); $list_names = array('Jeff Bridges', 'John Goodman', 'Julianne Moore', 'Steve Buscemi'); foreach ($list_names as $i_name) { $T->block('/cast', array('name' => $i_name), TRUE); } $T->display(); Cast: Jeff Bridges, John Goodman, Julianne Moore, Steve Buscemi Cast:

4.5. Debugging Internal representation of a template stage in Blitz is just a complex data structure with key names equal to variable or block names. This data is saved in template internally and can be dumped using method getIterations() when debugging. In the next example we iterate blocks sequentially using block() method, set some additional data with set() method, get the iteration data with getIterations() method, clean all the iterations and display the template using previously dumpled data: $T = new Blitz(); $T->load('{{ include("test.tpl") }}'); $list_names = array('Jeff Bridges', 'John Goodman', 'Julianne Moore', 'Steve Buscemi'); foreach ($list_names as $i_name) { $T->block('/cast', array('name' => $i_name), TRUE); } $T->set(array('film' => 'The Big Lebowski')); $data = $T->getIterations(); var_dump($data); $T->clean(); var_dump($T->getIterations()); $T->display($data); array(1) { [0]=> array(2) { ["cast"]=> array(4) { [0]=> array(1) { ["name"]=> string(12) "Jeff Bridges" } [1]=> array(1) { ["name"]=> string(12) "John Goodman" } [2]=> array(1) { ["name"]=> string(14) "Julianne Moore" } [3]=> array(1) { ["name"]=> string(13) "Steve Buscemi" } } ["film"]=> string(16) "The Big Lebowski" } } array(0) { } The Big Lebowski cast: Jeff Bridges, John Goodman, Julianne Moore, Steve Buscemi

Sometimes you have correct iterations but your code doesn't work properly still. Use getStruct()/dumpStruct() methods to check if there is everything OK with your template. See View API for examples.



4.6. Fetch When working with large templates you oftenly want to get a part of template. This is done by fetch() method: $body =<<<BODY {{ BEGIN test }} Hello, {{ $name }}! {{ END }} BODY; $T = new Blitz(); $T->load($body); echo $T->fetch('test', array('name' => 'world!')); Hello, world! $body =<<<BODY {{ fetch('/test') }} {{ BEGIN test }}Hello, world!{{ END }} BODY; $T = new Blitz(); $T->load($body); $T->display();



4.7. Other internal methods Basic internal methods are: if(), include(), date() and escape(). Two first methods were mentioned above, and two last are new. Let's cover them briefly.



Method date(format, argument) used to format date/time values just from your template. It works as follows: when argument is integer, date() treats it as UNIX timestamp. Otherwise it's parsed with 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".



Method escape(string) is equal to htmlspecialchars(string, ENT_COMPAT). You can use additional second parameter to specify quoting style which is passed as string, named as the corresponding PHP constant: escape(string, "ENT_QUOTES") or escape(string, "ENT_NOQUOTES") etc. Blitz supports all three quote styles: "ENT_COMPAT", "ENT_QUOTES" and "ENT_NOQUOTES".



5. Callbacks and plugins Blitz supports callbacks like {{ Some::doSomething($params) }} or {{ doSomething($param) }}. With this you can add any function calls to your template. Basically you can:

1) extend Blitz and use your custom template methods in the template code

2) call any other PHP function like do($something) or Plugin::do($something) Callbacks were added in 0.7.1.5 but changed in older versions. Current callback API (writing this for 0.7.1.14) works this way: {{ this::method }} calls class method through the template object

{{ php::method }} calls PHP function "method"

{{ helper::method }} calls static PHP-method

{{ method }} calls class method first and PHP-method after (if not found) and vice versa - according to blitz.php_callbacks_first setting. This was added in 0.7.1.14 and by default it's 1 so PHP-method is called first. This can be changed in next versions according to the community reaction. See the following example:



fisher@fisher:~/prj/blitz/tests> cat calls.php <? ini_set("blitz.php_callbacks_first", 1); $body = <<<BODY {{ php::date("Y/m/d H:i") }} self-call: {{ this::doSomething() }} php-call: {{ php::doSomething() }} this+php call: {{ doSomething() }} this+php call: {{ doSomething2() }} BODY; function doSomething() { return 'PHP :: did comething'; } class View extends Blitz { function doSomething() { return 'THIS :: did something'; } function __call($a, $b) { return '__call handler: '.var_export($a, true).', '. str_replace(array("

","\r"), "", var_export($b, true)); } } $T = new View(); $T->load($body); $T->display(); ?> fisher@fisher:~/prj/blitz/tests> php5 calls.php 2010/04/02 23:57 this-call: THIS :: did something php-call: PHP :: did comething this+php call: PHP :: did comething this+php call: __call handler: 'dosomething2', array ()

When there is a namespace and a '::' before function, Blitz checks if the namespace is a "hint". "Hints" are reserved namespaces: {{ php::do($params) }}, or {{ this::do($params) }} When namespace is a "hint" ("php" or "this"). Having {{ php::do($params) }} Blitz makes a static php-function call, having {{ this::do($param) }} do() method is called through the current template object. When namespace is not a "hint", Blitz makes a static PHP-call namespace::do($params). If this namespace and function exists - the function will be called.

When there was just a function call with no namespace like {{ do($params) }} Blitz works according to priority settings. When "blitz.php_callbacks_first" is set to 1 Blitz first tries to make a static PHP-function call. If this function doesn't exist - Blitz calls this method through your template controller. When "blitz.php_callbacks_first" is set to 0, vice versa.

Now we can go back to the example: {{ php::date("Y/m/d H:i") }} - is a PHP-function call

{{ this::doSomething() }} - is a "hinted" doSomething() method call through the View object $T

{{ php::doSomething() }} - is a "hinted" doSomething() function call from the current function scope

{{ doSomething() }} - is a function call from the current function scope because blitz.php_callbacks_first was set to 1

{{ doSomething2() }} - Blitz tries to find doSomething2() function in the current scope because blitz.php_callbacks_first was set to 1. There's no such function, so then Blitz makes a method call through the View object $T. This call is handled by __call() because doSomething2() method doesn't exist.

6. Performance notes 6.1. Benchmarks Unfortunately, I don't know any simple, universal and really correct method to analyse template engine performance. The only one right way to do this is to build your application using several template engines and measure the performance under the real conditions. Should it be noted that nobody is going to do this? That's why we always deal with artificial tests which results should be used very carefully. Nevertheless, results of one "synthetic" test are listed here.



This test is much closer to real world conditions than most of tests you can find in the Internet (like "wow, we have printed this variable 200 times!!!11"). First we have a page of several non-trivial parts. The code is written using different template engines. This page 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: adverts (3 items)

news list (5 items, 5 vars each)

striped navigation (7 sections)

list of users online(~20 items)

vote with 3 possible answers

other variables (~10 items)

Tests include lot of different template engines. Leaders are: PHP mess: PHP and HTML are messed in a single file. This method is used in the real big projects in very specific cases only, but included into the tests just because it is obviously the fastest one.

blitz: single template, every functional block has it's own context

PHP includes: PHP and HTML are messed but functionally different blocks(list elements) are separated and included from the main code.

php_templates: single template, every functional block has it's own context

smarty: single template, compiled, output cache is off

The results of this test can be found below.





Software versions and settings were:

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)

These measurements were made quite long time ago (PHP 4.3.* with Zend Performance Suite, and old versions of template engines). Now we need to make the same test with modern PHP 5.3.* with APC. If you can help with this - please drop me an email. I still need benchmark tests for some popular systems like CTPP, ClearSilver, Twig, Lapa, Quicky, Macro/WACT or possibly many others. If you just send me how to implement the test with these systems - I'll test it and add the results here. The benchmark code is available to download and one can add benchmark code for any other template engine.



Results above should 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 into a "compiled" template you probably know that its code has a lot of nested constructions, quite hard to execute (with a lot of unnecessary hash lookups, for instance), 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 APC/XCache/eAccelerator/whatever. In general, one should not to rely on the quoted results entirely. Make tests, play with real projects and choose solutions, which give a profit under your own particular conditions.



6.2. Slowdowns Blitz performance receipes are the same as for any web-applications with CPU bottleneck. Following things in Blitz may slow down your application:

lot of includes (all the data will seat in VFS cache almost immediately, but includes always add some kernel syscalls and need CPU to parse/initialize objects).

lot of hash lookups to resolve path-variables $obj.smth.blabla, performance downgrades proportionally to the product number_of_variables*average_path_length

lot of hash lookups back through the scope stack

lot of block()/iterate() calls instead of preparing set data and just calling $View->display($data)

lot of non-built-in Blitz methods in templates (user defined callbacks or PHP-functions)

7. Template API [IF|UNLESS]/ELSEIF/ELSE statments

if(predicate, output_true [, output_false])

include(filename)

escape(html [, "ENT_QUOTES"|"ENT_NOQUOTES"|"ENT_COMPAT")

date(format [, arg) 7.1 [IF|UNLESS]/ELSEIF/ELSE statments 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. Simple expressions are supported too: $a >= $b, 2 < $a, $a == "bla-bla".



The most usual example of these statements is a list which can be empty: {{ IF $list }} some html code before the list {{ BEGIN list }} {{ $data }} {{ END list }} some html code after the list {{ ELSE }} some html code for empty list {{ END if-list }}

7.2 if(predicate, output_true [, output_false]) If() method is a short version for predicates. If() outputs arguments according to the first argument: $T = new Blitz(); $T->load("{{ $num }}. {{ $name }} {{ if($rip,'[R.I.P.]') }}"); $character = array( array(1, 'The Dude', 0), array(2, 'Walter Sobchak', 0), array(3, 'Donny', 1), // RIP, Donny array(4, 'Maude Lebowski', 0), array(5, 'The Big Lebowski', 0), array(6, 'Brandt', 0), array(7, 'Jesus Quintana', 0), ); foreach ($character as $i => $i_data) { echo $T->parse( array( 'num' => $i_data[0], 'name' => $i_data[1], 'rip' => $i_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

7.3 include(filename) Include() method includes external template.



1.tpl: {{ $what }} $T = new Blitz(); $T->load("Where is the {{ include('1.tpl') }}, Lebowski?

"); $T->display(array('what' => 'money')); Where's the money, Lebowski?

7.4 escape(html [, "ENT_QUOTES"|"ENT_NOQUOTES"|"ENT_COMPAT") Escape() is a very simple html output wrapper, works like htmlspecialchars() function in PHP. There is a short alias: q(). Without second argument escape uses ENT_QUOTES as default and escapes both </> and both single and double quotes: $T = new Blitz(); $T->load('{{ q($a); }} {{ q($a, "ENT_COMPAT") }} {{ q($a, "ENT_QUOTES") }} {{ q($a, "ENT_NOQUOTES") }} '); $T->display(array('a' => "here's a \"test\" <>\'\"")); here's a "test" <>\'" here's a "test" <>\'" here's a "test" <>\'" here's a "test" <>\'"

7.5 date(format [, arg) Date() function formats a date. When "arg" is numerical, it is treated as UNIX timestamp integer. Otherwise it's parsed using internal PHP function "php_parse_date" which recognizes 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".



Consider the following 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->display(array( 'time_num' => $time_num, 'time_str' => $time_str )); 22 07 1976 11:22:33 22 07 1976 01:02:03



8. View API load($body) - load template body from variable

set($vars) - set iterations/variables

parse([$vars]) - set $vars and return rendered template as string

display([$vars]) - set $vars and output rendered template as string

block($path, $vars [, $iterate_nonexistant]) - iterate $path with $vars

fetch($path, $vars) - output context $path rendered as string with $vars

include($filename, $vars) - load template $filename and return rendered with $vars as string

clean() - clean internal iteration data

cleanGlobals() - clean globals

getGlobals() - return globals as hash

setGlobals($global_vars) - set globals

hasContext($path) - check if context $path exists in the template

context($path) - set current context to $path

iterate($path [, $iterate_nonexistant]) - create new $path iteration with empty data

getContext() - get current working context

getStruct() - return template structure as an array of paths.

dumpStruct() - dump template structure in some internal

getIterations() - get internal iteration data representing the current template state

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. depending on your religious views you may use eitherorcoding style. For anyBlitz canas well.

8.1. load($body) Load() just loads template body from variable: $T = new blitz(); $T->load('Have a lot of {{ $what}}!'); $T->display(array('what' => 'fun'));

8.2. set($vars) Set variables or complex iteration data into the current context. Set accepts mixed $vars that represent internal iteration state: when it's array - it's interpreted as list. $T = new Blitz(); $T->load("{{ BEGIN characters }} {{ \$_num }}. {{ \$name }} {{ if(\$rip,'[R.I.P.]') }}

{{ END characters}}"); $data = array( 'characters' => array( array('name' => 'The Dude'), array('name' => 'Walter Sobchak'), array('name' => 'Donny', 'rip' => TRUE), // RIP, Donny array('name' => 'Maude Lebowski'), array('name' => 'The Big Lebowski'), array('name' => 'Brandt'), array('name' => 'Jesus Quintana'), ) ); $T->set($data); $T->display(); 1. The Dude 2. Walter Sobchak 3. Donny [R.I.P.] 4. Maude Lebowski 5. The Big Lebowski 6. Brandt 7. Jesus Quintana T = new Blitz(); $T->load("Hello {{ \$name }}!

"); $T->set( array( 0 => array('name' => 'dude'), 1 => array('name' => 'world') ) ); $T->display(); Hello dude! Hello world!

8.3. parse([$vars]) Parse() method sets $vars and returns rendered template as string. Vars structure has the same semantics as for set(). You can just say "echo $T->parse($vars)" instead of $T->set($vars) and $T->display() in previous example.



8.4. display([$vars]) Display() method sets $vars and outputs rendered template. Vars structure has the same semantics as for set(). You can just say $T->display($vars) instead of $T->set($vars) and $T->display() in previous example.



8.5. block($path, $vars [, $iterate_nonexistant = FALSE]) Block() populates context $path with $vars. When $iterate_nonexistant is TRUE block() will not check if this $path is valid (this may be useful for complex templates with includes). main.tpl: {{ $question }} {{ BEGIN answers }} - {{ $text }} {{ END}} $T = new Blitz('ex3.tpl'); $T->display( array( 'question' => 'Do I make myself clear, Lebowski?', 'answers' => array( 0 => array('text' => 'Yes'), 1 => array('text' => 'No'), 2 => array('text' => 'Sorry, I wasn\'t listening'), ) ) ); Do I make myself clear, Lebowski? - Yes - No - Sorry, I wasn't listening

8.6. fetch($path, $vars) Fetch() method outputs context $path rendered with $vars. Example



8.7. include($filename, $vars) In some cases you need to include a template from your PHP-code. This happens quite rarely and basically it's a bad style cause this increases template controller/code coupling, but if you need to do this without creating Blitz instance - you can use include() method. Consider this php code: class View extends Blitz { var $news = array(); function View($tmpl_name) { return parent::Blitz($tmpl_name); } function setNews($data) { $this->news = $data; } function showNewsList() { $result = ''; foreach ($this->news as $i_news) { $result .= $this->include('news_list_item.tpl', $i_news); } return $result; } } $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(); test1: {{ begin test1 }} v1={{ $var }} {{ end }} test2: {{ begin test2 }} v2={{ $var }} {{ end }} test1: v1=0 v1=1 v1=2 v1=3 v1=4 test2: v2=1 v2=2 v2=3 v2=4 v2=5



8.8. clean(), cleanGlobals() Clean() method just cleans-up all template iterations and sets the template in the initial stage as if it was just created: $T = new Blitz(); $T->load('{{ IF question }} {{ BEGIN question }}Who the fuck are the {{ $name }}? {{ END question }} {{ ELSE }} empty {{ END }}'); $T->set(array('question' => array('name' => 'Knutsens'))); $T->display(); var_dump($T->getIterations()); $T->clean(); var_dump($T->getIterations()); $T->display(); Who the fuck are the Knutsens? array(1) { [0]=> array(1) { ["question"]=> array(1) { ["name"]=> string(8) "Knutsens" } } } array(0) { } empty

CleanGlobals() is the same but cleans globals, not iteration data.

8.9. setGlobals($global_vars), getGlobals() getGlobals() returns globals hash array, setGlobals($global_vars) - adds $global_vars to the global variables hash: $body = ' It\'s like what {{ $local }} said... It\'s like what {{ $global }} said... {{ BEGIN context }} It\'s like what {{ $local }} said... It\'s like what {{ $global }} said... {{ END context }} '; $T = new Blitz(); $T->load($body); $T->set(array('local' => 'Lennon')); $T->setGlobals(array('global' => 'Lenin')); $T->block('context', array('local' => 'Lenin')); $T->display(); var_dump($T->getGlobals()); var_dump($T->getIterations()); It's like what Lennon said... It's like what Lenin said... It's like what Lenin said... It's like what Lenin said... array(1) { ["global"]=> string(5) "Lenin" } array(1) { [0]=> array(2) { ["local"]=> string(6) "Lennon" ["context"]=> array(1) { [0]=> array(1) { ["local"]=> string(5) "Lenin" } } } }

Please remember that usually you don't need to pass local variables to every block - you can just use "scope lookups" with blitz.scope_lookup_limit and varibles from parent blocks will be visible in inner blocks. Here $local is passed to "/context" just because we needed this variable to have different values in the root "/" and "/context".



8.12. hasContext($path) This method just checks if the context exists or not and returns TRUE/FALSE correspondingly. $body = '{{ BEGIN walter }} I don\'t roll on Shabbos {{ END }}'; $T = new Blitz(); $T->load($body); var_dump($T->hasContext('walter')); var_dump($T->hasContext('donny')); bool(true) bool(false)

8.13. context($path)

8.14. iterate($path [, $iterate_nonexistant]) 8.15. getContext()

8.16. getStruct() getStruct() - returns template structure as an array of available paths.

8.17. dumpStruct() 8.18. getIterations() getIterations() - get internal iteration data representing the current template state. Example

These are my own development values which were formed through my own experience with large-scale internet projects. Note, this all comes mostly from "management", not "programming", and has influence on large projects particularly. Let me explain this all briefly.Is "fast" important? Talking about the speed I'm sure 99,9% of projects are not CPU-bound. But large projects are. Most of top projects have thousands frontends serving billions requests per day (top of the top has even much more). Performance management is a field of special importance here. Usually storage/application services are tuned as much as possible and CPU usage on frontend boxes is, well, not bottleneck, but one of very important degrees of freedom. Crooked solutions which scale badly will significantly increase your total cost. From my personal experience presentation code may eat up to 30-50%% CPU for the dynamic page generation in a "good" project and up to 100% in "bad". Think of that: your frontend CPU spends most of the time just building result HTML! It is very important to decrease this value. With Blitz you won't spend much CPU on presentation level (see benchmark results below).Is "clear" important? Talking about the simplicity of presentation code I'm absolutely sure 99,9% of projects can be written in plain PHP and happily supported through years. Plain PHP is probably the best solution from the performance point of view as well. There's one problem though. Supporting the mess of PHP and HTML in a big project is usually just a nightmare. Large projects usually have large codebase and a lot of problems appear while trying to manage PHP+HTML spaghetti in presentation code. This happens because modern, personalized applications have very complex presentation logic and this all tend to change intensively. This logic should be written, owned and supported by developer (I also suppose here that HTML/CSS competence is much different from programming so developers better don't touch this stuff at all - and vice versa). But when you have PHP code together with HTML and Javascript in templates - the ownership and the responsibility is too fuzzy. That's why I always wanted HTML/JS guys to keep their hands off the presentation logic. I do know what happens otherwise when this part of application is "shared". That's why I wanted to maximally separate HTML and view logic. I wanted to see the code, with no HTML/Smarty/PHP/Whatever spaghetti mess. I wanted developers to work with the code, with no HTML or JS around. Well, that is a some kind of management philosophy. You just feel this "right" - or not :)))I started this project in 2005 as a replacement of php_templates by Maxim Poltarak, a very fast PHP template engine which was popular in ealy 200x mostly among developers in former USSR countries. This engine was unjustly rejected by PHP team long time ago (they didn't let it into PECL for absolutely childish reasons) so the project was frozen and got no further developments. I took most of "design" concepts from php_templates and thank Maxim for his great project.Blitz is a PHP extension distributed in source code (Win32 users can download compiled DLL's).To extract the code and build the extension just follow these simple steps:To test blitz run the test script run-tests.sh.. Running the tests you will get something like this:If Blitz was build as shared module you will probably need to edit your php.ini and include Blitz in extension list:This list is based on 0.7.* versions. See the difference in old 0.6.* below.