PHP Templating Celebrity Deathmatch!

This is an old post!

This post is over 2 years old. Solutions referenced in this article may no longer be valid. Please consider this when utilizing any information referenced here.

Ladies and Gentleman! Welcome to the PHP Templating Celebrity Deathmatch!

I actually do like the idea behind templating. I know there are varying arguments about whether or not templating is appropriate for PHP, though those are not the focus of this entry.

The big idea behind templating is separation of concerns, that is, breaking a program into parts that are easily manageable and don’t overlap in functionality. In an ideal world, templating would provide the added advantage of allowing a programmer to be a programmer and not a web designer - and allowing a web designer to be a web designer and not a programmer - by keeping the logic underlying the presentation layer to a minimum. However, I have never found this to be true in any project I’ve worked on in my professional career.

One of the big benefits, as far as I see, is that it makes code much easier to read. This may not be true for everyone, but I would much rather be confronted with smooth, separated templated code rather than a jumbled PHP mess. It’s easier to read and far, far easier to adapt and change.

While I was attending OSCON a few weeks ago, I heard mention of a new PHP templating engine that was written in C and native compiled into a PHP extension. This would make it much, much faster than anything written in PHP itself - in theory. This project, called Blitz, was making some pretty grand claims on their website, so I wanted to put them to the test - at least a small timing test.

In this test, I am going to be comparing Smarty (the most widely used PHP templating engine and an official PHP project), Blitz (a new templating engine currently under very active development that is native compiled as a PHP extension), and standard PHP includes.

For the purposes of this test, I wrote a quick timing function that uses microtime() to record how much time has elapsed between each call of mark_time(). The code is available in the accompanying project.

A Note About The Tests

These are not meant to be exhastive tests by any means. These tests are just designed to give you 5,000 foot overview of the current state of PHP templating. They only evaluate page generation time and not other metrics such as CPU load, IO load, or memory usage. Furthermore, I selected three scenarios that I have commonly used in templating; there may be some scenarios that I haven’t tested where one method may outperform others. And, as with any benchmarking, they are dependent on my system - YMMV.

Test 1: Instantiation

This is a simple test that determines how much time it takes to power up the templating engine and get it loaded into memory for PHP to use. For the purposes of this test, we will just be comparing Smarty and Blitz, as there is no need for instantiation with a standard PHP include. We’ll start with Smarty first.

smarty_instantiation.php

<?php
echo mark_time()."<br>";
include "Smarty.class.php";
$smarty = new Smarty;
echo mark_time()."<br>";
?>

Smarty’s instantiation time was 0.0058109760284424 or 0.005 seconds in human terms.

blitz_instantiation.php:

<?php
echo mark_time()."<br>";
$blitz = new Blitz;
echo mark_time()."<br>";
?>

Blitz’s instantiation time was 3.0994415283203E-5, or 0.00003 seconds in human terms.

It may not seem like a big difference, but this is one area where having Blitz as a PHP extension makes a huge difference over Smarty being written in PHP and included. Because PHP must traverse the include_path to find Smarty.class before including it, it causes PHP to be slowed down before it can even instantate the Smarty object. To be fair, I decided to run a second test again with the include out of the timing mark.

smarty_instantiation2.php

<?php
echo mark_time()."<br>";
$smarty = new Smarty;
echo mark_time()."<br>";
?>

Even without having to search the include_path for Smarty, it still took 6.5088272094727E-5, or 0.00007 seconds to instantate the Smarty object - almost twice as long as it took to instantate the Blitz object. However, this is not a realistic scenario in any way - there is no way that PHP can have saved any time and still have access to the Smarty object!

Winner: Blitz

Test 2: Simple Template Rendering

In this test, we will be comparing simple template rendering in Blitz, Smarty and PHP includes. In this test we will create a simple HTML template with two variables that need to be replaced, then render and display them to the user using each engine or, in the case of PHP, straight PHP. So, let’s get started! We’ll run Blitz first, since it won the previous test.

blitz_simple_render.php

<?php
echo mark_time()."<br>";
$blitz = new Blitz('blitz_simple_render.tpl');
echo $blitz->parse(array(
'title' => "Blitz Test!",
'body' => "Blah foo! I'm a body!"
));
echo mark_time()."<br>";
?>

Blitz took an impressive 0.00011801719665527, or 0.0001 to render a simple HTML document with two replaces. Smarty’s next:

smarty_simple_render.php

<?php
echo mark_time()."<br>";
include "Smarty.class.php";
$smarty = new Smarty;
$smarty->assign('title',"Smarty Test!");
$smarty->assign('body',"Blah foo! I'm a body!");
$smarty->display('smarty_simple_render.tpl');
echo mark_time()."<br>";
?>

Because Smarty is a compiling engine (it compiles the templates to PHP and caches them), the first run is always the most costly - in this case, an atrocious 0.058284997940063 or 0.06. Even on subsequent runs, 0.0065691471099854 or 0.007, again much slower than Blitz. Finally, standard PHP includes:

php_simple_render.php

<?php
echo mark_time()."<br>";
$title="PHP Test!";
$body="Blah foo! I'm a body!";
include "php_simple_render.tpl.php";
echo mark_time()."<br>";
?>

Surprisingly, standard PHP includes took 0.00030016899108887, or 0.0003 seconds, much faster than Smarty, but three times as slow as Blitz. Once again, this likely has to do with PHP having to traverse the include_path before finding the appropriate file. If you specify the _absolute path on the filesystem _to the file above, the time took becomes 0.00010490417480469, or 0.0001, roughly equal to Blitz on any given run. However, because Blitz is able to parse the template with a minimum of fuss whereas I have to explicitly specify the filesystem path for PHP to get equal performance, this round also goes to Blitz.

Winner: Blitz

Test 3: Complex Templating

In this case, we are going to be doing complex templating. This test includes three template-based includes, one foreach loop over an array, and a large array of generated data. Just for the curious, the generation of the data is not going to be counted towards the timing. In this case, we have generated a 10,000 item array and are going to have each engine iterate over it.

blitz_complex_render.php

<?php
echo mark_time()."<br>";
$blitz = new Blitz('blitz_complex_render.tpl');
foreach($arr as $array) {
$blitz->block('master_loop',array(
'id' => $array['id'],
'id1' => $array['id+1']
));
}
echo $blitz->parse(array(
'title' => "Blitz Complex Render text"
));
echo mark_time()."<br>";
?>

Blitz ran the test in 0.072134971618652, or 0.07 seconds, not too shabby considering it had to iterate over a 10,000 item multidimensional array.

smarty_complex_render.php

<?php
echo mark_time()."<br>";
include "Smarty.class.php";
$smarty=new Smarty();
$smarty->assign('title',"Smarty Complex Render test");
$smarty->assign('arr',$arr);
$smarty->display('smarty_complex_render.tpl');
echo mark_time()."<br>";
?>

Again, because Smarty is a compiling engine, the first run is always the most expensive - in this case, a whopping 0.31642484664917, or 0.3 seconds. Subsequent runs fell in the range of 0.099456838607788, or 0.1 seconds, three times as fast as the first run but still slower than Blitz. Finally, standard PHP includes:

php_complex_render.php

<?php
echo mark_time()."<br>";
include "php_complex_render.tpl.php";
echo mark_time()."<br>";
?>

In this test, raw PHP includes came in at 0.055343866348267, or 0.06, the fastest of all and yet just a small bit faster than Blitz.

Winner: PHP

Conclusion

Blitz won two of the three tests and came in a close second in the last. Of course, one could argue that PHP “won” the first test since there was no need to be tested on instantiation.

Considering the short amount of time Blitz has been under active development, its sheer speed is rather amazing. From a templating standpoint, Blitz is the fastest unless you are willing to jump through lots of little hoops to make standard PHP includes work for you, and even at that point, the performance as far as total page generation time goes is roughly equal, though native PHP may have a slight advantage.

However, unfortunately, the very strength of Blitz (it being written in C and compiled into a PHP extension) is its greatest weakness. Because so many websites are served off shared hosts without the ability of users to use external extensions, most of the community will never have the ability to take advantage of Blitz. Only those with access to the machine, or more specifically the php.ini file, will have the ability to use Blitz unless it were to be merged into the PHP tree. Even in the best case, considering how many shared hosts are still running PHP4, I wouldn’t expect to see anything like this soon, if ever.

Perversely, the very weakness of Smarty (that it is written in PHP and included) is its strength, for the reasons above. Smarty is the slowest templating engine tested, however because it is just PHP, it can be included and run like any other PHP script - meaning all the people on shared hosting can make use of it with a minimum of fuss. And in Smarty’s defense, there are many features (such as template variable modifiers) Smarty has that are simply not available in Blitz. These features come with the tradeoff of a massive loss in speed. It was honestly surprising to me how slow it was.

Ultimately, it is the decision of the programmer as to what is the right method to use. If you want the advantages of templating as far as seperation of concerns and ease of maintenance and you have the ability, Blitz is probably a good choice for you. If you still want the ease of maintenance and separation of concerns provided by templating and are willing to make the tradeoff for a massive loss of speed, Smarty is a possibility also. If sheer pure speed is your primary concern and you’re not willing to make any kind of tradeoffs, going with raw PHP is probably your best option provided you fine tune it a bit to get the absolute best performance out of it.

Comments (0)

Interested in why you can't leave comments on my blog? Read the article about why comments are uniquely terrible and need to die. If you are still interested in commenting on this article, feel free to reach out to me directly and/or share it on social media.

Contact Me
Share It
PHP
Phinx is a really cool database migration package that allows you to write changes to your database as code. It keeps track of which changes have been applied and allows you the option of rolling back if you hit an issue. All the documentation on Phinx describes a typical setup where you would run the phinx command to do your migrations. And that is all fine and good in most projects. But what happens if you are integrating Phinx into an existing project that already has a lot of the usual scaffolding in place?
Read More
PHP
Let’s say you have a Laravel application that does some data processing, and you want to monitor a directory for incoming changes, that you can then process using queued jobs. There are a couple of ways you could do something like this. You could scan those directories on a schedule using a cronjob. It’s doable. But what happens if you want to monitor a few thousand directories for changes? You can use tools like incron. Also doable, but another dependency. But what if I told you you could do it all with PHP. And within Laravel, no less?
Read More
Release Announcements
Launched two new pieces of open source code in the last couple of months. PlayerControls PlayerControls is a macOS Cocoa framework that creates a View containing playback controls for media like videos or sounds. It is written in pure Swift 4 and has no dependencies. SearchParser SearchParser is a parser that converts a freeform query into an intermediate object, that can then be converted to query many backends (SQL, ElasticSearch, etc). It includes translators for SQL (using PDO) and Laravel Eloquent ORM. It supports a faceted language search as commonly found on many sites across the web. It is written in modern PHP. Both are licensed under the MIT license. Go check them out on Github.
Read More