Savant Template Engine

When we (as in my business partner and I) started developing the third version of our open source program (our first ever ‘commercial’ product), Olate Download, we had to decide how we were going to handle templates to allow the user to customise their site. At the time, the options available to us were to use Smarty (or find a different engine) or create our own template engine to handle it. We had a look at Smarty and decided it was too complex for our simple download management application. As such, we went ahead and created our own system.

Looking back at our engine now, it does its job, but it is limited. We added our own very basic control structure syntax and built in includes and even a simple language system. It did the job well.

Several months after we released that product, I started development on our latest product – a paid application to manage software sales. This too was to have a template system because it is easiest way to allow customisation of the product. I had recently read an article in PHP Magazine about an engine called Savant2. After playing around, I decided that this time, I didn’t want to create my own system, but I would use Savant to power the template system.

Side note: One of good things about using a 3rd party system rather than writing your own is that the developers of the product are usually dedicated to improving that product. They are not involved in your development so they work on their program, improving, bug fixing and updating. This means that part of your product improves without you needing to do much, except update your integration of it! You can work on other parts of your product whilst that bit is handled by someone else.

Comparing Savant to Smarty is difficult. Yes they are both template engines, but Savant does not compile its templates. Nor does it have its own built in scripting syntax. It uses PHP. Many people also consider Smarty to be ‘heavy’ with lots of complex features that aren’t necessarily needed in all situations. For example I didn’t need caching or compiling in my products and I also wanted to work with PHP syntax in the templates, not the custom syntax that Smarty has.

[quote="Savant Website"]In short, PHP is itself a template language, so in general there is no need for another template language on top of it. However, there are some specific cases where using customized markup is safer than PHP; for that reason, Savant allows you to hook in a custom compiler for your own purposes.[/quote]

(Also see articles by Harry Fuecks and Brian Lozier)

Savant2

The idea behind Savant is (what any PHP developer should be trying to do) to separate design from the application itself. i.e. separate the HTML from the PHP code that performs the majority of the functions. This is Smarty does 100% – you see no PHP in the templates. But in Savant, this is not the case. Whilst all the main code is in separate PHP files, there is some PHP in templates.

Savant always has 2 files. One of them is the PHP file which does most of the work and then other is a template file (usually with the .tpl.php extension).

To illustrate this, here is an example, where I have Savant2.php in the same directory:

require_once('Savant2.php');
$template = new Savant2(); // New Savant2 object

$title = 'Customers';
$list = array(array('name' => 'James', 'Country' => 'UK'), array('name' => 'Jill', 'Country' => 'Canada'));

$template->assign('title', $title); // Assigning a var to be used in the template
$template->assign('customers', $list);

$template->display('customers.tpl.php'); // Display the template now
?>

In the HTML template customers.tpl.php, I would have this:

... HTML ...

customers)): ?>

title; ?>

customers as $key => $value): ?>


Customer Country

There are no customers.

... HTML ...

This would echo the 2 customers I added to the $list array. Notice how the variables assigned from the $template->assign() call are class variables using $this->varname. Also notice that I’m using PHP control structures (the shortened versions) within the template.

Plugins

That is a very simple example and the real usefulness of Savant comes with its various plugins and filters.

[quote="Savant Website"]Template plugins are objects you can call from your template to automate repetitive tasks. Savant loads plugin objects dynamically as you call them, so you don’t have to pre-load them. However, if you want to pre-load a plugin, you can sometimes configure its behavior in advance; whether or not a plugin can be configured depends on the specific plugin.[/quote]

The plugins available include form generation, image/CSS/JS display, and general options like date formatting. Of these, one of the most useful is the option list plugin.

One of the problems with dynamically generated option lists is selecting the default value. This is made easy with the options plugin.

For example, when editing a database record, I could have this code:

// Status list menu options
$status_options = array(1 => 'Active', 0 => 'Disabled');
$template->assign('status_options', $status_options);

This sets the status drop menu options and assigns them to the template variable. Elsewhere in the code, I have already obtained the data from the record in the database.

In the template, I can then create the list, populate it with the 2 options and also set a default value based on the data returned from my database record:

Here, the plugin method is called. The first parameter is the name of the plugin, the second is the array of options and the third is the default value. This value could be at any point in the list, it only needs to reference the array key of the options you provide. The output might look like:

Another really useful plugin is the HTML checkbox. I’m sure everyone has encountered the problem of trying to determine the value of a ticked checkbox! Savant makes it easy with the checkbox plugin so it is easy to set a value when ticked. You can also just as easily have it set as a value when you’re editing a database record (for example) like with the option menu above.

Savant2 works with both PHP4 and PHP5 (Savant3 is written specifically for PHP5).

From working with Savant since around October last year, I have been able to do everything I want to do with it. I found that it has easily coped with a large application such as my own, and I expect it could easily scale both ways. It is not bloated, it is well coded (includes PHPDoc source documentation) and it does exactly what I need it to do.

There are plenty of engines around. It is all about finding one you like and that suits your project. You’ll find a useful list of engines at http://www.sitepoint.com/forums/showthread.php?threadid=123769

Holiday

This will be my last post for just over a week. I’m going on holiday to Canada tomorrow and will be back next week on 5th when I shall resume my regular (every other day) posts.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Alan Knowles

    Great way to introduce XSS attacks into your code…

    < ?php echo $value['name'] ?>

    Do you know where that came from? – is it safe?
    Never trust your own code here, that may be safe today, but one day you will make a change to the backend code, and forget it is used at output time.. -> opening the door to XXS attacks..

    This is why PHP style templates are just a bad idea.. – unless you copy and paste htmlspecialchars everywhere, in which case, you have to look through the trees to see the bugs…

    The output layer should default to escaping code if possible, and make it easy to find where escaping is not done, not the other way round.

    That’s without getting into the undocumented madness that smarty and savant use with $object->assign()…

  • http://www.lowter.com charmedlover

    Wow, quite a good post – and something very useful to me. I write software and this templating system looks quite useful, as I don’t want something as complex as Smarty.

    Although I have my own small templating system for Ottoman, but a future product I’m working on would be much easier to program and manage if I used Savant2.

    Again that’s for the post.

  • Lachlan

    In my work and projects I tend to use Brian Lozier’s Template class. It uses pretty much exactly the same mechanism that Savant uses, e.g basically a fancy wrapper for a function-scoped call to extract. Its light-weight, simple and does exactly what I need. For plugins I simply pass in view objects.

    One thing worth mentioning about templating in PHP is that it’s not about separating PHP code from template files, its about separating business logic from presentation. There is nothing inherently evil about having code in your template files, so long as its code which exists solely to service the presentational aspects of what you are trying to accomplish.

    This separation isn’t ever going to be solved entirely by the templating software, it’s something which has to be separated by the developer as part of a conscious design decision.

  • http://www.gammaworld.org/mutant/ mutant

    The template markup looks like PHP, but does it validate? I don’t think so:
    Parse error: syntax error, unexpected T_ENDFOREACH

    There is no “endforeach”, while the language looks the same, it is still a custom markup.

    With smarty, this:
    <img src=”{$image}/t1.jpg”/>

    is nicer than:

    <img src=”<?php echo $value['image']; ?>”>

    This isn’t a “smarty is better” comment, but a “why choose one over the other if they are the same?” comment.

  • zjcboy

    Hey,

    I think I have a simpler solution(simpler in syntax). Just a class

    Template.class.php
    ——————–

    I use the template engine of my own. the syntax in the template file will be cleaner. simple, yet it fits my neeeds.


    < ?PHP
    class Template
    {
    var $path;
    var $file;
    var $_vars = array();

    function Template()
    {
    require_once 'inc/html.php';
    }

    function set($k,$v)
    {
    $this->_vars[$k] = $v;
    }

    function get($k)
    {
    return $this->_vars[$k];
    }

    function parse()
    {
    //import vars into this namespace
    extract($this->_vars);

    //start buffering output
    ob_start();

    require $this->file;

    //get the output in buffer
    $output = ob_get_contents();

    //clean buffer
    ob_end_clean();

    return $output;
    }
    }
    ?>

    sample.tpl.php
    —————




    < ?=$body?>

    php script to use the template
    ——————————


    < ?PHP
    $tpl = new Template;
    $tpl->file = 'sample.tpl.php';
    $tpl->set('title', 'Hello');
    $tpl->set('body','Hello, world!');
    echo $tpl->parse();
    ?>

  • Lachlan

    There is no “endforeach”, while the language looks the same, it is still a custom markup.

    Actually, have a read of the manual page:

    PHP offers an alternative syntax for some of its control structures; namely, if, while, for, foreach, and switch. In each case, the basic form of the alternate syntax is to change the opening brace to a colon (:) and the closing brace to endif;, endwhile;, endfor;, endforeach;, or endswitch;, respectively.

    Alternatively you could have looked at the list of PHP parser tokens. Either place lists endforeach as valid PHP.

  • Lachlan

    This is why PHP style templates are just a bad idea.. – unless you copy and paste htmlspecialchars everywhere, in which case, you have to look through the trees to see the bugs…

    The output layer should default to escaping code if possible, and make it easy to find where escaping is not done, not the other way round.

    That’s without getting into the undocumented madness that smarty and savant use with $object->assign()…

    I’m not sure I follow, isn’t having a presentation detail like calls to htmlspecialchars located in the presentation template a good thing? If you have your calls to htmlspecialchars sprinkled throughout your business logic layers how are you going to prevent double escaping?

    I tend to work on a Programming By contract method, whereby my templating layer (the view) counts on the fact that it is being passed unescaped data. The templates job is to then format the data provided for presentation, if that presentation language is html then it gets escaped.

  • http://www.olate.co.uk Olate

    There is currently discussion on the Savant mailing list about a new function in the 2.4 release which will handle all the escaping for you. So instead of using echo, which you rightly said might cause XSS problems, you would use the built in Savant function and then any necessary escaping would be done for you.

  • http://aplosmedia.com/ Eric.Coleman

    I still like smarty :/

  • Ian Eure

    For my purposes, Savant is a perfect fit. It’s quick, lightweight, easy to understand, use, and extend. Smarty is huge, slow, complex, hard to set up and debug. Having a few more characters for template tags is a completely acceptable trade-off.

    Alan, regarding your comments:
    - Any PHP templating system, and PHP itself, is going to be vulnerable to XSS unless you’re careful about checking your data. Savant is not unique in this regard, and Smarty is just as vulnerable (unless you drop |escape:”html” into every template tag, which seems just as bad as your comment about htmlspecialchars().)
    - Take a look at assign() and assignRef() in Savant2. assign() is somewhat clumsy, but assignRef() is completely straightforward, and they’re both well-documented.

  • http://boyohazard.net Octal

    Thanks for the post David. I am researching PHP template engines and this post (and the links to the articles) couldn’t have been better timed.

  • http://www.shref1.blogspot.com shref

    iam using the template engines just to make the life easier for the designer
    as i can’t force him to learn coding with php and there is some customers loves to play with the html in the scripts to fit thier needs so .i love to work with smarty or any better TmpEngine

  • erinea

    A very mature and fine templating system that does a lot of the heavy lifting is http://www.tinybutstrong.com . It is distinguishable from the large majority of ‘open’ software by its exceptional manual and brilliant examples. If you want to have an exceptionally popular project check out out the examples page to see how you should do it – While the system is exceptionally powerful and flexible the examples are so good (and so cleverly implemented) I reckon you could have a working knowledge of the system in less than an hour. The authors’ support forum is also superb. Tiny has consistently cut my development time by 40% and made it much more maintainable as well. It doesnt get better than that.

    Oh that all projects had such good documentation. I reckon the principle failing of PEAR is the lack of decent working examples of the libraries in action.

  • http://www.olate.co.uk Olate

    shref: Your comment also applies to Smarty, or any engine that uses a a scripting language. If they don’t learn a bit of PHP to manipulate the templates then they have to learn the scripting syntax of that particular engine. At least if you’re learning a bit of PHP, it isn’t just limited to the templates – you could use it anywhere.

  • http://aplosmedia.com/ Eric.Coleman

    Ian Eure: I don’t understand how you can say smarty is hard to debug? you add $smarty->debug = true; and you get a console of the assigned variables and templates… how can that be hard?

    As for being hard to setup…. it takes about 45 seconds, I hardly call that hard.

  • http://paul-m-jones.com/ pmjones

    Alan Knowles mentioned “undocumented madness” regarding the assign() method of Savant2. Alan, to what are you referring? The assign() method is pretty thoroughly documented at:

    http://phpsavant.com/yawiki/index.php?area=Savant2&page=Savant2Assign

    If there’s a specific issue that hasn’t been addressed, I’d be very happy to hear about it so I can correct the problem.

  • http://paul-m-jones.com/ pmjones

    Hi, zjcboy,

    You have described the core of Savant quite nicely with the example of your own template system. Savant works exactly the same way. However, Savant also extends that a bit to allow a very few extra features that, while simple, are very powerful.

    For example, Savant provides path-management tools. These makes it easier to “skin” an application — if a user wants to replace one template with another, instead of editing the business logic of your code, the user can point to any number replacement directories with the alternative templates in it).

    Similarly, Savant provides a system for writing and auto-loading convenience code (“plugins”) for common output needs. You write a plugin class, and Savant finds and loads it for your the first time you call it, instead of you having to load up every function and class you might possibly use in advance, whether or not you actually use it in a given template.

    Hope this begins to explain why Savant is functionally identical to your system, and why the vew few additional features are so handy. :-)

  • http://www.casualcode.com eXplosive

    I’ve been using PHP Savant for a while now. I must look in to using some more of the plugins.

  • Alan Knowles

    Undocumented madness is the fact that there is no location where you can natrually document the variables used in the template (it’s just a random array that you sent into assign().
    It’s a bit like writing a class, and putting everything into $this->variables[] and expecting someone to understand your code in the future.. (I’ve worked in detail with someone elses smarty templates, and this is a constant nightmare…) – when we converted to flexy, we just used standard PHP document comments and hey presto the code is readable..

    Using tags for template engines, that by default use htmlspecialchars() – when you do {givemeavalue} and force you to request the raw data {givemeraw:h} enables you got quickly grep a directory of templates for potential problems ‘grep :h} * -r’ – then backtrace to check if they are really issues.

    using php or any other method, involves you looking (and probably printing out every single html file) – as some lines may be longer than 80 characters and looking for any variable that is output without escaping!.. (let alone the backtracking to check if it was safe or deliberatly left unescaped)
    ** or become a regex king? ;)

    Beginning to get an idea? – design in security early, dont try and kludge it on later…

  • http://www.gammaworld.org/mutant/ mutant

    Actually, have a read of the manual page:

    … huh…

    Alternatively you could have looked at the list of PHP parser tokens. Either place lists endforeach as valid PHP.

    … double huh …

    I tried out some code with these, and it didn’t parse. Mabey I was just doing it wrong. I also swear google didn’t return anything when I site searched php.net. Thnx for pointing me at this.

  • http://paul-m-jones.com/ pmjones

    Alan said: “Undocumented madness is the fact that there is no location where you can natrually document the variables used in the template (it’s just a random array that you sent into assign().”

    Ah yes, I see your point. One of the benefits of PHP-based templates is that you can put phpDoc comments in them directly, but generally I have not actually done that in practice. Looks like I need to start, and to recommend the practice. Thanks for the pointer.

    As far as grepping for non-escaped output, with the new 2.4.0 version (upcoming) you can grep for any instance of echo or print; using $this->_() to escape-and-echo is going to become the recommended practice.

    (BTW: Funny to see how easily you pwnd planet-php like that. :-)

  • http://dysfunksion.co.uk neobuddah

    Why not just use XSL? Its fast, produces standard-compliant code, has a massive user-base so it’s constantly being improved, and its quick and easy to learn! Its also standalone, so doesn’t require PHP to be used, meaning you can swap out your business logic if needed (eg. migrating from PHP4 to PHP5, switching to java/coldfusion/asp…).

  • yosoyminero

    But why bothering so much with templating engines and such? I think it’s much easier just to create a pure PHP file in charge of the creation and display of the interface for the site. This file can contain all the validation/escaping code and interface drawing functions required, thus separating business logic from presentation logic.

    That is: just put every part in a separate file in order to separate different layers of the MVC model!

  • http://www.saumendra.com saumendra

    Well the savant2 templating engine is quite strain=ght forward and easy to implement but it all depends on the nature and robustness of the web application you are developing. In our projects we tend to use our own template engine, more like savant2. We have three level of template files i.e For Simple page,For Table and for the Rows.

    So putting evry thing in separate part makes it much more flexible and we can use the ideas of both Smarty and Savant2 templates in our projects.

    Singing Off !!

  • Chris

    I agree with mutant. To me it lookes like you removed the “Smarty shell” from Smarty, found the exposed guts of php and somehow thought that that looked more appealing.

    Your “template” system may be good for the php developer but now designers need to learn it php to get any work done.

  • http://paul-m-jones.com/ pmjones

    Hi Chris — Regarding designers who need to learn a new language to get work done: either they get stuck learning the Smarty markup language, or they get stuck learning a minimal set of PHP commands. If they’re not a security threat, I’d say PHP itself is both easier and more flexible.

  • _Psih

    To me it better for designer to learn basic PHP commands once, than to learn once Smarty, other time some other template language. At our office we came to the resolution, that Savant like template engines are more flexable. If designer needs something special, we always can write a plugin! We are developers by the way! If you write a quite big aplication, usualy it has it’s own unique plugins, which make life much more easier. Some of them are universal, some not. In any case, I worked whith smarty… when i came to the table building whith colspans and rowspans at once, I started to hate it, because I had to write a lot of PHP code in aplication core, so that smatry could handle that table generation. Savant would make it much more easier, because you could write complex PHP code in template if you need it. And that was that thing that i needed in my case.