PHP Template Engine Comparison


I’m going to face a project using a template engine with PHP. Because of that I’m will perform a small benchmark test of several PHP template engines. That’s not an exhaustive performance test. It’s only my personal test. Template engines has a lot of features but I normally only use a few of them and the other features very seldom. In this performance test I will check the same features under different template engines to see the syntax differences and the performance. The template engines selected for the test are Smarty, Twig and Haanga. Let’s start:

Smarty. v3.0.6
It’s probably the most famous template engine. It’s a mature project. For years it was “the” template engine and the others were the “alternatives”. It was famous because of the speed.

Twig. v1.0.0-RC1-8
It’s a new template engine developed by Fabien Potencier, the creator of the symfony framework. One of the PHP’s rock stars nowadays. It’s going to be an important part of the new symfony 2.0 framework. Twig borrows the template syntax from Django (probably the main web framework if we work with Python)

Haanga. v1.0.4-14
It’s another new template engine using the Django style. It was developed for Menéame by César Rodas.

I’ve decided to create two tests. One with a simple template and another using template Inheritance. The both cases renders one html page using one variable, filters and for loop to create an HTML table. Basically I’ve created those test because they’re the things I normally use. I will run the test with an HTML table of 50 rows and 1000 rows

Simple template

Smarty

{* Smarty. indexFull.tpl*}
<html>
    <head>
        <title>{$title}</title>
    </head>
    <body>
        <h2>An example with {$title|capitalize}</h2>
        <b>Table with {$number|escape} rows</b>
        <table>
{foreach $table as $row}
            <tr bgcolor="{cycle values="#aaaaaa,#ffffff"}">
                <td>{$row.id}</td>
                <td>{$row.name}</td>
            </tr>
{foreachelse}
            <tr><td>No items were found</td></tr>
{/foreach}
        </table>
    </body>
</html>

And the PHP conde:

// index.php
$time = microtime(TRUE);
$mem = memory_get_usage();

define('BASE_DIR', dirname(__file__));
require(BASE_DIR . '/include/smarty/Smarty.class.php');

$smarty = new Smarty();

$smarty->setTemplateDir(BASE_DIR . '/smarty/templates');
$smarty->setCompileDir(BASE_DIR . '/smarty/templates_c');
$smarty->setCacheDir(BASE_DIR . '/smarty/cache');
$smarty->setConfigDir(BASE_DIR .'/smarty/configs');

$smarty->assign('title', "smarty");

$rows = 1000;
$data = array();
for ($i=0; $i<$rows; $i++ ) {
    $data[] = array('id' => $i, 'name' => "name {$i}");
}
$smarty->assign('table', $data);
$smarty->assign('number', $rows);
$smarty->display('indexFull.tpl');

print_r(array('memory' => (memory_get_usage() - $mem) / (1024 * 1024), 'seconds' => microtime(TRUE) - $time));

Twig

{#  Twig. indexFull.html #} 
<html>
    <head>
        <title>{{ title }}</title>
    </head>
    <body>
        <h2>An example with {{ title|title }}</h2>
        <b>Table with {{ number|escape}} rows</b>
        <table>
            {% for row in table %}
            <tr bgcolor="{{ cycle(['#aaaaaa', '#ffffff'], row.id) }}">
                <td>{{ row.id }}</td>
                <td>{{ row.name }}</td>
            </tr>
            {% endfor %}
        </table>
    </body>
</html>

And the PHP code:

// index.php
$time = microtime(TRUE);
$mem = memory_get_usage();

define('BASE_DIR', dirname(__file__));
require_once BASE_DIR . '/include/Twig/Autoloader.php';
Twig_Autoloader::register();

$loader = new Twig_Loader_Filesystem(BASE_DIR . '/twig/templates');
$twig = new Twig_Environment($loader, array(
            'cache' => BASE_DIR . '/twig/compiled',
            'auto_reload' => true
        ));
$template = $twig->loadTemplate('indexFull.html');

$rows = 1000;
$data = array();
for ($i = 0; $i < $rows; $i++) {
    $data[] = array('id' => $i, 'name' => "name {$i}");
}

$template->display(array(
    'number' => $rows,
    'title'  => 'twig',
    'table'  => $data
));

print_r(array('memory' => (memory_get_usage() - $mem) / (1024 * 1024), 'seconds' => microtime(TRUE) - $time));

Haanga

{#  Haanga. indexFull.html #} 
<html>
    <head>
        <title>{{ title }}</title>
    </head>
    <body>
        <h2>An example with {{ title|title }}</h2>
        <b>Table with {{ number|escape}} rows</b>
        <table>
            {% for row in table %}
            <tr bgcolor="{% cycle '#aaaaaa' '#ffffff' %}">
                <td>{{ row.id }}</td>
                <td>{{ row.name }}</td>
            </tr>
            {% endfor %}
        </table>
    </body>
</html>

And the PHP code:

// index.php
$time = microtime(TRUE);
$mem = memory_get_usage();

define('BASE_DIR', dirname(__file__));
require(BASE_DIR . '/include/Haanga.php');

Haanga::configure(array(
    'template_dir' => BASE_DIR . '/haanga/templates',
    'cache_dir' => BASE_DIR . '/haanga/compiled',
));

$rows = 1000;
$data = array();
for ($i=0; $i<$rows; $i++ ) {
    $data[] = array('id' => $i, 'name' => "name {$i}");
}
Haanga::Load('indexFull.html', array(
    'number' => $rows,
    'title'  => 'haanga',
    'table'  => $data
    ));

print_r(array('memory' => (memory_get_usage() - $mem) / (1024 * 1024), 'seconds' => microtime(TRUE) - $time));

With template Inheritance

With this test I use the same php file, changing template name from indexFull to index.

Smarty

{* Smarty. index.tpl*}
{extends file="layout.tpl"}
{block name=table}
<table>
{foreach $table as $row}
    <tr bgcolor="{cycle values="#aaaaaa,#ffffff"}">
        <td>{$row.id}</td>
        <td>{$row.name}</td>
    </tr>
{foreachelse}
    <tr><td>No items were found</td></tr>
{/foreach}
</table>
{/block}
{* Smarty. layout.tpl*}
<html>
    <head>
        <title>{$title}</title>
    </head>
    <body>
        <h2>An example with {$title|capitalize}</h2>
        <b>Table with {$number|escape} rows</b>
        {block name=table}{/block}
    </body>
</html>

Twig

{#  Twig. index.html #} 
{% extends "layout.html" %}
{% block table %}
<table>
    {% for row in table %}
    <tr bgcolor="{{ cycle(['#aaaaaa', '#ffffff'], row.id) }}">
        <td>{{ row.id }}</td>
        <td>{{ row.name }}</td>
    </tr>
    {% else %}
    <tr><td>No items were found</td></tr>
    {% endfor %}
</table>
{% endblock %}
{#  Twig. layout.html #} 
<html>
    <head>
        <title>{{ title }}</title>
    </head>
    <body>
        <h2>An example with {{ title|title }}</h2>
        <b>Table with {{ number|escape}} rows</b>
        {% block table %}{% endblock %}
    </body>
</html>

Haanga

{% extends "layout.html" %}
{#  Haanga. index.html #} 
{% block table %}
<table>
    {% for row in table %}
    <tr bgcolor="{% cycle '#aaaaaa' '#ffffff' %}">
        <td>{{ row.id }}</td>
        <td>{{ row.name }}</td>
    </tr>
    {% endfor %}
</table>
{% endblock %}
{#  Haanga. layout.html #} 
<html>
    <head>
        <title>{{ title }}</title>
    </head>
    <body>
        <h2>An example with {{ title|title }}</h2>
        <b>Table with {{ number|escape}} rows</b>
        {% block table %}{% endblock %}
    </body>
</html>

Outcomes of the tests:

(50 rows) Smarty Twig Haanga
Simple template Memory: 0.684497
Time: 0.023710
Memory: 0.598434
Time: 0.025444
Memory: 0.124019
Time:  0.004004
Template Inheritance Memory: 0.685134
Time: 0.023761
Memory: 0.619461
Time: 0.028100
Memory: 0.133472
Time: 0.005005
(1000 rows) Smarty Twig Haanga
Simple template Memory: 1.222743
Time: 0.094762
Memory: 1.033226
Time: 0.196187
Memory: 0.558811
Time: 0.043151
Template Inheritance Memory: 1.194095
Time: 0.090528
Memory: 1.054237
Time: 0.191694
Memory: 0.646381
Time: 0.044402

Haanga really rocks in the test. It’s the fastest in all cases and it’s the best using memory. The main problem I’ve seen with Haanga is the lack of documentation. When I wanted to use the cycle filter (to create the zebra style in the HTML table) I didn’t find anything about it. I had to browse the source code and finally I found it in one tests. Whereas Smarty documentation is brilliant and Twig is good enough.

The HTML template syntax is almost the same with Twig and Haanga (in fact both of them are Django style). Smarty is a bit different but is very similar. The PHP part in Smarty looks like a bit old fashioned compared with Haanga and Twig, but it’s really easy to use.

The performance of Twig and Smarty are similar. Twig is slightly better. But with simple templates i’ts almost the same.

38 thoughts on “PHP Template Engine Comparison

    1. I’ve done all test without catching because I suppose when catching the performance will be the same with all template engines. I wanted to test only the “compiled” templates. I also omit the performance when “compiling” (the first time)

    2. Haanga doesn’t support a cache mechanism at all, if you need that you should do it in another layer.

      Anyway, the output of the compilation is saved in a temporary folder. Is this what you meant?

      1. Another engines support cache. But at least for me it’s good decission not to implement cache inside the template engine and use another layer for catching. In fact if we test the performance of “cached” templates we are testing the performance of the cache engine, not the performance of the templating engine.

    1. Yes. As I said It’s not a exhaustive performance test. It’s my personal test. I wanted to test the features that I normally use, not all the features. I’ve done it without cacthing because I only wanted to compare them with the “compiled” templates

  1. I’ve read on the Haanga website that it supports a “secure” mode and a “fast” one.

    Since Smarty works in a similar “secure” mode by default, shouldn’t you test Haanga in that mode as well (or in both)?

    I don’t know about Twig.

    Also worth remembering that the smarty developers clearly stated that, since Smarty 3 is essentially a huge rewrite of the original codebase, they didn’t start to work on the speed and memory usage yet and improvements are to be expected in the next releases.

    1. Speed is not the most important thing. I know(but it’s easily measurable 😉 ). The differences are minimal. Memory is a bit more important. When you pay for a hosting, you normally pay for memory usage. Efficient memory usage means efficient costs. But for me the most important characteristic is the documentation. Haanga is cool but I need 30 minutes to find how to perform cycle filter. I normally work with smarty I know how to it with smarty but I take 2 minutes to find how to do it with Twig. That’s also means efficiency. Smarty rocks when we speak about documentation

      The test aimed to be a “out of the box” text. A good library (for me) is a library thats works efficiently “out of the box” and has a good docummentation to learn more things if I need it. There’re too many technologies in our dayly work. It would be great to be a master in all of them but it’s impossible (at least for me)

      Where did you find the “fast mode” in Haanga. I did’t find anything about it?

      1. It’s advertised in the left sidebar of the website. I don’t know about the documentation.

      2. Yes but it says that it’s “Fast” and “Secure” but as far I know there isn’t any way to choose beetwen them. I will investigate

      3. Oh, reading it again, I think you are right. I have misunderstand the meaning of it.
        Sorry for the confusion.

  2. Hey there,

    I wasn’t aware that Haanga was that good ;-), just kidding. I promise I will work on the documentation this weekend, to make it awesome 🙂

    I believe Haanga performs well because it generates self-contained PHP code from the template, that means that filters and tags *must* generate PHP code. Being self-contained is really cool, you can copy a generated code, move to another machine without Haanga installed on it, and it will work like charm :). This feature also means that just a tiny part of Haanga is needed on normal execution, a static method which compares the tpl file and the generated php.

    You tested raw PHP code vs tons of classes in PHP, thank goodness PHP was faster 🙂

    FYI, in the “secure” mode (enabled by default) all variables are escaped, exactly as in django.

    Feel free to fork the project or the documentation at github or drop me an e-mail with feedbacks (crodas@php.net)

    Kudos!

    1. self-contained PHP code. Great!. That explains why the performance is almost the same than plain PHP templates. That’s really a good job. Congratulations.

  3. wouldn’t it be more fun to use template-inheritance for the table-rows and not for the whole table?

    interesting benchmark anyway. i would like to do compare them with my own engine (still in development). what dependencies do i have for haanga and twig?

    1. I think that using it for rows or full table depends on your personal decission. That’s only an example. I preffer to have one template for table and another for the layout here, but it’s only an example.

      you only need to download the library and add them to the include dir. (it’s documented and it’s pretty straightforward).

    1. The idea of generate self-contained PHP within template files is realy cool. When I created the benchmark I get surprised with the outcomes. My first impression was something was wrong in my test but the performance is really good. A template engine with the same performace of plain PHP. cool

  4. Impressive article, Thanks!!

    I am happy to read this article, because I am a newbie who is just starting to look for my first template engine.

    What I am confused in this article is, you’ve used cache path in all of the three template engines, such as:
    $smarty->setCacheDir(BASE_DIR . ‘/smarty/cache’);
    ‘cache’ => BASE_DIR . ‘/twig/compiled’,
    ‘cache_dir’ => BASE_DIR . ‘/haanga/compiled’,

    but you mentioned
    ” I’ve done all test without catching because I suppose when catching the performance….”

    So, what does your cache mechanism mean here? Thank you in advance.

  5. I’ve use the template engines for the test “out of the box”. I wanted the default configuration. Maybe the names I’ve used are a bit confusing ;). Let me explain that. Normally template engines implement some kind of template language to create templates. Smarty has it’s own one, and Twig and Haanga uses the same syntax than Django (the most famous we framework in python world). Our server knows PHP, not the template engine’s language. That’s means the library will “compile” the template syntax into PHP code. If you see the code generated within the compile directory you will see PHP code (auto-generated PHP code).

    Imagine you are generating a page with data from the database and you will display the information using a template engine. In some cases we don’t need to create the page in every request. you can save the HTML code into a cache and show it instead of generating again and again (without connecting to the database and and executing the template). That’s cache. So compiled templated are the PHP representation of our templates and cached templates are the saved output of the compiled templated.

    Some template engines implement some kind of cache (because of that we have a cache dir). Haanga for example doesn’t implement that.

    I’ve decided not to use cache in the test because I wanted to test the performance of the templating engine not the cache engine. Obviously the performance of a cached template is better than compiled one.

  6. Sounds good. I will check it. But when I see the Macro website I found the same problem than haanga: the lack of good documentation (full list of features and usage examples for all). Smarty for example has a great one. Probably the performance is not the best but finding features is trivial. I know the open source developers normally. Btw I will have a look to limp3 (I didn’t knew it)

    1. I think when you are speaking about catching you are speaking about compiled templates. All PHP’s template engines are compiled into PHP code indeed (our server only executes PHP code). When I said I have done the test without catching, that’s means I’m testing the performance about the compiled templates. Some template engines has an extra feature to cache the template’s output (generally html code) to improve performance (we don’t use PHP compiler). Because of that I’ve done the test without catching (only testing the compiled templates). http://www.smarty.net/docsv2/en/variable.caching.tpl

  7. Here’s a really simple one with cache + no fancy template markup – for a guy like me with little experience, it does a perfect job!

  8. Pingback: medi | Pearltrees

Leave a reply to Han Chung Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.