Publishing System Settings Logout Login Register
Simple template engine - Find and replace variables
TutorialCommentsThe AuthorReport Tutorial
Tutorial Avatar
Rating
Add to Favorites
Posted on June 1st, 2007
15530 views
PHP Coding
Simple Template Engine Tutorial

One of the many questions coders will encounter at one point is how commercial software such as phpBB, vB and other forum software allow users to use templates to customise or radically modify the GUI of an application.

During this rather slowly constructed tutorial I'll just be going through the basic theory/processes behind creating a basic template engine. You may find that if you are looking for a complete "ready to use solution you may want to look into a package such as Smarty. If you are looking to perhaps expand your understanding then carry on reading.

Lets get started.

First we need to go through what we are going to be doing. The idea of a basic template engine such as this is to separate what is behind the GUI (i.e. the HTML, CSS and other client side languages) from the server side code which "makes it work".

We do this to, in my mind, achieve two things. The first and most obvious reason is that by separating the layout of the application you are making it much easier on yourself to make major modifications to the backend without having to adapt or completely rewrite the layout code. The second reason that I do this is simply to keep my pre-processed code separate and therefore more organised, but perhaps that's just me.
In this tutorial you will notice that i am using a class based method of coding (otherwise known as OOP). While it has become "fashionable" within coders to use this style, i prefer it both for organisational reasons and because for certain things such as this (where variables will be constantly used and worked on) i find it easier to use. The speed debate is exactly that; arguable, however i consider any speed disadvantages caused from using smaller classes, is easily compensated by time spent coding.


So what are we going to do here? Well, as i mentioned earlier the idea of a template engine is to separate the GUI of an application from the hard coding of it. In a language such as PHP, a fundamental aspect of this is being able to take a portion of html (for example) and parse the required variables through it.

For example: We shall call this file example_template.tpl
Welcome, <strong>{username}</strong>.


When outputted to the client, we of course want this to be replaced by:
Welcome, Fred.
Our users name is Fred purely for example

But how do we do this? Well lets start by setting up the class in which we will work around. Remember to check comments within my code as small things such as what variables will be used for, i tend not to mention elsewhere.

<?php

class template
{
   
    /*     Variables we will be using in this class
         template_dir - Simply for organisational reasons, the directory of out template files.
        file_ext - For organisational reasons, the templae file extensions
        buffer - The contents of the given template file that we work with.
    */
   
    var $template_dir = 'templates/';
    var $file_ext     = '.tpl';
    var $buffer;


So now that we have that sorted, lets start.

So what do we need to do? Well the first and most obvious step is to grab the contents of the desired template file. We can expand this by using functions such as file_exists to check if the template file exists before trying to working with it.

As we are wanting to incorporate this into a class, we need to create a function to do this (well, we do not need to, but as the template engine will usually handle more than one file per instance, we need to).

Lets name this function something appropriate as it will be buffering the file.
function buff_template ($file) {}


The $file variable will obviously be used to store the file name of the template file the class will work with.
So lets continue with the code.

function buff_template ($file) 
{
    if( file_exists( $this -> template_dir . $file . $this -> file_ext ) )
    {
        // Lets leave this for a while
    }
    else
    {
        echo $this -> template_dir . $file . $this -> file_ext . ' does not exist';
    }
}


In essence what we are doing here is checking if the file exists and if not, outputting that error (or rather warning) to the user. You can add an optional language construct (just call them functions) to quit right here if you wish (e.g. die, exit...)

In case this line is confusing you:
$this -> template_dir . $file . $this -> file_ext


Remember to refer back to the comments when we first started this class. All we are doing here is appending the template directory, the file name and extension into the first argument of the function. So for example this would be returned to the function:

templates/header.tplNow that we have got past that, lets revisit what we do if the file does exist (as it usually would).

All we are going to do is use a function to grab the contents of the file ready for later use. This is all very simple and you should be following what we are doing with no problems so far.

$this -> buffer = file_get_contents( $this -> template_dir . $file . $this -> file_ext );


file_get_contents was introduced in PHP 4.3.0. If you are using an earlier version i would suggest you update or change web host, however there is an alternative to the function.

By using file and join, we can first gather the file contents using the first of then two functions however file returns this into an array (with elements split by new lines). We can merge the array elements and process the file contents into one string recreating the effect of file_get_contents by using join().

[MY_P2L_AD]

$this -> buffer = join('', file( $this -> template_dir . $file . $this -> file_ext ));

Finally we need to make the function return this content so we can use the class without running functions in a line (i.e. strlen(trim())). In other words, we are also able to keep this data in a string outside the class.
return $this -> buffer;




By creating this function we have achieved a very simple but essential process. The function checks for the files existence, and if it is found it returns the files contents into the "buffer" variable.
Next we need to sort out just how exactly this is going to work. As you may have noticed earlier we are going to be using a format similar to most template engines (phpBB being a prime example) by encasing variable names in curly braces.

Example: In out template file
Welcome, {username}.


As you may have guessed, again, here the theory is simple. We 'scan' though the buffered content of the template file for any keywords encased in curly braces and check this against a list of variables (in this case, handled by an array).

Lets start the new function off, again, named descriptively.
function parse_variables ($input, $array) {}


$input is the data from the template file to be parsed.
$array is, obviously enough, the array in which are variable values and contained.

The first thing we need to do, is search though the data for occurrences of "{.*?}" (curly braces with anything inside). We don't need to know where these are as the regex functions will handle this for us, but we do need to know how many of them there are. Something that luckily preg_match_all will also do for us.

For the scope of this tutorial we only need to know the 3 basic arguments of preg_match_all.

preg_match_all(string, subject, array);

string - The pattern to match for
subject - The data to search in
array - The array to populate with matched occurrences.

As mentioned earlier the pattern we want is {.*?}, so lets apply that:

$search = preg_match_all('/{.*?}/', $input, $matches);


As you can see I've stored the number of occurrences found in the search in the $search array, this could have been found by using count (or sizeof) on the newly populated array however the regex function will give this to us without doing so.

These next two stages are a little more complicated, but nothing that you shouldn't be able to handle with basic knowledge of arrays and by thinking it through a bit. We basically need to take the matched string (which will be something like {username} and grab just the username part so we can associate it with an array element. The crudest way of doing this is simply by removing the braces from the string.

for($i = 0; $i < $search; $i++)
{
    $matches[0][$i] = str_replace(array('{', '}'), null, $matches[0][$i]);

}


Here we have just used a simple loop (the length of the array) and used str_replace to remove the braces. This is basically just a simple way of modifying each element in the array, it is definately not the best way to apply an action to all elements however for the purpose of this tutorial it will do.

In the next stage we are basically going to loop the array again however this timewe are modifying the inputted data (the template file) by replacing the {username} with what would be an array element. $array['username'].

You may have noticed although i have kept mentioning this array of values, i have yet to construct it. This will follow don't worry!

This is again just a very simple str_replace of the {username} pattern against an array element with the name of the pattern (i.e. 'username').

foreach($matches[0] as $value)
{
    $input = str_replace('{' . $value . '}', $array[$value], $input);
}


At this stage the variables have been replaced, but the data is now just being retained in the $input variable - so lets simply return it from the function.

return $input;


So here is out final code for the class:
<?php

class template
{
   
    /*     Variables we will be using in this class
         template_dir - Simply for organisational reasons, the directory of out template files.
        file_ext - For organisational reasons, the templae file extensions
        buffer - The contents of the given template file that we work with.
    */
   
    var $template_dir     = '.templates/';
    var $file_ext         = '.tpl';
    var $buffer;
   
    function buff_template ($file)
    {
        if( file_exists( $this -> template_dir . $file . $this -> file_ext ) )
        {
            $this -> buffer = file_get_contents( $this -> template_dir . $file . $this -> file_ext );
           
            // For < PHP 4.3.0
            // $this -> buffer = join('', file( $this -> template_dir . $file . $this -> file_ext ));
           
            return $this -> buffer;
        }
        else
        {
            echo $this -> template_dir . $file . $this -> file_ext . ' does not exist';
        }
    }
   
    function parse_variables($input, $array)
    {   
            $search = preg_match_all('/{.*?}/', $input, $matches);
                       
            for($i = 0; $i < $search; $i++)
            {
                $matches[0][$i] = str_replace(array('{', '}'), null, $matches[0][$i]);
           
            }
           
            foreach($matches[0] as $value)
            {
                $input = str_replace('{' . $value . '}', $array[$value], $input);
            }
                
       
            return $input;
    }

}
?>
You may now be asking how we use this. Well, here, finally is where we cover the array containing the values.

As with all class (OOP) coding, we need to create a new instance of the class before use.
Example:

<?php
include 'template_class.php'; // This is where the class is contained

$engine = new template;


Now we need to remember the two functions we built. buff_template, and parse_variables. As you might expect the order in which we use these is exact the order in which we would read them as.

We need to buffer the template file and then parse the variables.

The first function is straight forward. Assuming the template directory is edited correctly in the class varables, you need only enter the file name of the template file to work with (without the extension).

For example:
$page = $engine -> buff_template('body');
// This would grab content for "templates/body.tpl" with current configuration


We then need to set the variables we want to replace with their values. To do this i have used a simple array to keep things tidy.

For example:

$array = array('username' => 'Fred'
               'message'  => 'Another Value');

// At more elements as you wish


Obviously it is up to you how these variables/elements are gathered. It would depend on your coding.

You then simply push these variables into the two arguemnts of your second function, parse_variables.

parse_variables( data, variables )


So for example to output the completed page:

echo $engine -> parse_variables($page, $array);



The End
That about sums it up for this tutorial, hopefully you have learnt something from the process i have tried to explain. All feedback is welcomed and of course if you have any problems or questions feel free to post in the forums.

Attached is a complete example of the class and it being used as i know there are those who read tutorials merely to gather code, but in my time of learning php myself....i have discovered this is not always a bad thing and you can learn simply by modifying a script you know little to nothing about.

Remember - Break it, find out why, fix it. Remember it.

File Download: Example Files
Dig this tutorial?
Thank the author by sending him a few P2L credits!

Send
Matthew.

This author is too busy writing tutorials instead of writing a personal profile!
View Full Profile Add as Friend Send PM
Pixel2Life Home Advanced Search Search Tutorial Index Publish Tutorials Community Forums Web Hosting P2L On Facebook P2L On Twitter P2L Feeds Tutorial Index Publish Tutorials Community Forums Web Hosting P2L On Facebook P2L On Twitter P2L Feeds Pixel2life Homepage Submit a Tutorial Publish a Tutorial Join our Forums P2L Marketplace Advertise on P2L P2L Website Hosting Help and FAQ Topsites Link Exchange P2L RSS Feeds P2L Sitemap Contact Us Privacy Statement Legal P2L Facebook Fanpage Follow us on Twitter P2L Studios Portal P2L Website Hosting Back to Top