How to make a simple HTML template engine in PHP
We show how one can make a very simple straightforward template engine in PHP.
Introduction - What is a template engine?
A template engine is used to separate the presentation from the business logic. A good developer knows this is very important - not only it allows for delegating responsibilities (the designer works on the presentation layer while the programmer works on the business logic), but it also provides a more easier maintenance.
There are a lot (and I mean A LOT) of template engines for PHP. A very popular example is Smarty. Most of these template engines have a lot of advanced options and require the user to learn a new syntax for building the template files.
What if you just want some easy to understand and simple to use template engine? Why not build your own? In this tutorial we'll do just that - we'll create a very simple template engine in PHP that anyone can use without having to spend time reading manuals.
Our template files will be written in pure HTML with some extra tags for easy replacement. We'll put the tags where we want our content to be - the engine will basically act as a replacement feature, but it could be updated for more advanced operations.
In the next picture I provide an overview of the working of this simple PHP template engine.

A simple HTML template
First, let's start with our HTML template file. We must define the tags' format we're going to use. Most templates use curly brackets surrounding the tags, e.g. {tag}, but I like to use a different syntax: [@tag]. Feel free to define your own conventions.
Imagine a typical case of building a user's profile page. Let's assume we need to display the user's photo, username, real name, age and location. An example HTML is provided below.
<h1>[@username] profile</h1> <img src="[@photoURL]" class="photo" alt="[@name]" /> <b>Name:</b> [@name]<br /> <b>Age:</b> [@age]<br /> <b>Location:</b> [@location]<br />
Create your template file and save it. I like to use the tpl extension. In this case, let's call this template file user_profile.tpl.
Now we just need to load it in our PHP script and replace those tags with real values.
Template engine class
For easier use and portability we'll need a class - it will be called Template. This class will only need two member variables - one for storing the filename of the template and the other to store the values that will be used for replacing the tags in the template.
Let's start with this. We'll define our class and its constructor. I provide the code for this bellow.
class Template { protected $file; protected $values = array(); public function __construct($file) { $this->file = $file; } }
I'll refrain from putting the comments in the code snippets I provide here, because they are lengthy, but the source code for this tutorial is well documented with comments.
At least two more methods are needed: one for setting the values for each tag and the other for outputting the final result.
We'll store our values in a PHP array where each value is stored with a key that matches the tag it is supposed to replace. Our method for defining the key/value pairs will be called set.
The method for outputting the content is a little more advanced (called output). We'll start by trying to verify if the template file exists. If we're unable to do that we'll return an error message. If the template exists we'll load its contents into a variable. We then loop through our values array and replace each tag with its matching value.
The code for these two methods is provided below. Put them in the Template class.
public function set($key, $value) { $this->values[$key] = $value; } public function output() { if (!file_exists($this->file)) { return "Error loading template file ($this->file).<br />"; } $output = file_get_contents($this->file); foreach ($this->values as $key => $value) { $tagToReplace = "[@$key]"; $output = str_replace($tagToReplace, $value, $output); } return $output; }
Putting the engine to use
Now we can finally test our first iteration of the template engine. We'll create a simple PHP file (named user_profile.php) which loads the template with test data and outputs its result.
We'll start by including the file with the definition for our Template class (I called mine template.class.php). We then make a new Template object and we define each value in the template. In the end we want to write its output.
include("template.class.php"); $profile = new Template("user_profile.tpl"); $profile->set("username", "monk3y"); $profile->set("photoURL", "photo.jpg"); $profile->set("name", "Monkey man"); $profile->set("age", "23"); $profile->set("location", "Portugal"); echo $profile->output();
You can see that this is very simple to use. In a real world example those values would be previously loaded from a database or file.
Something is missing though. If we would output the code above it would output incorrect HTML code because we only defined the main part of our content in the template file (no <html> tag, etc.). What we're missing is our layout. So we need a layout template!
The code bellow is the structure of what could be a layout for a simple website. It contains an header, menu, content area and footer. You could define multiple tags throughout the file (remember, this is just a template!). In this case I put a title tag for setting the title of the page and a content tag where we'll put our main content.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>[@title]</title> <link rel="stylesheet" type="text/css" href="stylesheet.css" /> </head> <body> <div id="header"> <a href="http://www.broculos.net"><img src="broculo_small.gif" class="logo" alt="Broculos.net" /></a> <h1><a href="http://www.broculos.net">Broculos.net</a></h1> <h2>Simple PHP Template Engine</h2> </div> <div id="menu"> <h1>Navigation</h1> <ul> <li><a href="user_profile.php">User profile</a> - example of a user profile page</li> <li><a href="list_users.php">List users</a> - example table with listing of users</li> </ul> </div> <div id="content"> [@content] </div> <div id="footer"> Example usage of a simple PHP Template Engine.<br /> Search <a href="http://www.broculos.net">Broculos.net</a> for more tutorials. </div> </body> </html>
You can get the stylesheet for this file here. I also used our site's logo in this test page. All these files are include in this tutorial's files.
Now we'll use our user profile template for replacing the content tag in the layout template. This nesting of templates provides the needed flexibility.
So our user_profile.php should be like this now:
include("template.class.php"); $profile = new Template("user_profile.tpl"); $profile->set("username", "monk3y"); $profile->set("photoURL", "photo.jpg"); $profile->set("name", "Monkey man"); $profile->set("age", "23"); $profile->set("location", "Portugal"); $layout = new Template("layout.tpl"); $layout->set("title", "User profile"); $layout->set("content", $profile->output()); echo $layout->output();
First we load the profile template. Then we load the layout template and put the content of the profile template in it. Finally we output the HTML.
More advanced template manipulation
If you looked carefully to the menu area in our layout template you noticed the link to the lists_users.php page. This will be the topic for this chapter.
We want a page that lists the registered users. So, how do we go about doing this? My approach is to split this in two templates: one for the main content (i.e. the needed description/title and the definition of the table) and another for each user's listing (each row on the table).
Let's start by creating our list_users.tpl (the main template for the users' list page):
<h1>Users</h1> <table> <thead> <tr> <th>Username</th> <th>Location</th> </tr> </thead> <tbody> [@users] </tbody> </table>
Just a title and the definition of the users' table. We need a template for each row of the table. Let's call it list_users_row.tpl.
<tr>
<td>[@username]</td>
<td>[@location]</td>
</tr>
Now we'll combine these two into one. Because each user/row will be represented by a different template we'll make a function that will allow us to merge these different templates. This is needed because this merged value will then be used to replace the users tag on the main template (list_users.tpl).
static public function merge($templates, $separator = "n") { $output = ""; foreach ($templates as $template) { $content = (get_class($template) !== "Template") ? "Error, incorrect type - expected Template." : $template->output(); $output .= $content . $separator; } return $output; }
Add the above method to the Template class. This method loops through each Template object, concatenates its output and puts a separator between them (the default separator is a line break for more easier to read HTML code).
The merge method expects an array of Template objects. If there's another type included in the array an error message will be appended to the output (I think this is preferred than ending the script with an abrupt error).
Now we only need to make our list_users.php page. In this page we'll create a few mock-up users, we'll loop through the array and create a template for each user. Then we'll merge the templates for inclusion in the list_users.tpl template. We'll also use the same layout defined previously.
include("template.class.php"); $users = array( array("username" => "monk3y", "location" => "Portugal") , array("username" => "Sailor", "location" => "Moon") , array("username" => "Treix!", "location" => "Caribbean Islands") ); foreach ($users as $user) { $row = new Template("list_users_row.tpl"); foreach ($user as $key => $value) { $row->set($key, $value); } $usersTemplates[] = $row; } $usersContents = Template::merge($usersTemplates); $usersList = new Template("list_users.tpl"); $usersList->set("users", $usersContents); $layout = new Template("layout.tpl"); $layout->set("title", "Users"); $layout->set("content", $usersList->output()); echo $layout->output();
This is it! We finally did it. You can see the final result in action here: simple PHP template engine. Use the navigation for accessing the different pages.
Conclusions - Limitations and thoughts
This is just one approach for structuring your files. A more advanced separation of presentation and business logic is still possible: our files are still filled with a lot of code that its only purpose is processing the content for presentation. One approach is to further include more complex syntax and code in our templates: through a custom template language or using pure PHP mixed with HTML in the template.
Personally I like my HTML files clean without any extra code. An alternative is the use of classes for presentation. These classes could inherit from the Template class. Let's assume that a user is defined through an object of the class User and a VUser class is used for the users' template.
class User { var $username; // etc. } class VUser extends Template { public function __construct(User $user) { $this->file = "user_profile.tpl"; $this->values["username"] = $user->username; // etc. } }
In our PHP page we would only need to do the following:
$user = loadUser(); $tpl = new VUser($user); echo $tpl->output();
Much cleaner, uh?
Our template engine is still very limited. We could use more advanced features like caching for example and other techniques to improve performance.
Resources
Comments
10:24 10-04-2008josemota
Fantástico. Para quem nunca mexeu em templates, dá uma noção essencial sobre separação de lógica e apresentação. Parabéns pelo site e pela iniciativa.13:20 29-05-2008eMSi
exactly what I was looking for. nice one17:49 05-09-2008Scott
Great article. Would love to see a version with caching!21:02 15-10-2008Daen
Thanks-- been looking for something like this-- it'll fit just what I need.16:39 25-11-2008Prasanna_Fadnis
Thank you for providing such a great resources.. I have been searching for this for a long time to provide user based custom templates in my free educational services www.vieke.com, I think with your kind efforts I have understood how it is done.. Thank you again and again.. keep the good work.. may god bless you and wish you great successful career and life.Thank You.13:40 30-11-2008nunof
Thanks for your comments.12:33 19-12-2008sitlo
So that is it all about17:35 25-12-2008MarcTowler
This is a very very informative and useful tutorial. I have been looking for something like this that doesn't use PEAR for a long time01:27 05-10-2009Dave
This is the best and yet simple template engine i have ever seen.I started to use this but i have only one problem.
I have a array like this.
$category_array = array
(
array('a','b'),
array('a','b'),
array('a','b'),
array('a','b'),
array('a','b'),
array('a','b'),
array('a','b'),
array('a','b'),
array('a','b')
);
And this is the template.
[@category][0][1]
[@category][1][1]
[@category][2][1]
[@category][3][1]
[@category][4][1]
[@category][5][1]
[@category][6][1]
[@category][7][1]
[@category][8][1]
code:
//homepage
$tmp= new Template('homepage.tpl');
$tmp->set('category',$category_array);
But it's not working and says:
Notice: Array to string conversion in C:\xampp\htdocs\x\template.class.php on line 64
08:48 05-10-2009nunof
Unfortunately, the template engine is not able to handle such scenario, but you can easily change the source code to do what you want.21:32 05-10-2009Dave
Please add this feature. I do not know how to do it.22:01 05-10-2009nunof
I cannot add features on request.The purpose of this tutorial is to be simple and the basis for more complex systems, not to offer a usable template engine solution, but only the knowledge to start building your own, accommodated to your needs.
If you know PHP, then you should be able to follow this tutorial and do the changes you want. You have to update the foreach in the output() method.
Make a recursive method that tests if the value is an array, the replacement string will have to be updated in these cases, and so on.
08:53 29-10-2009Lord-Nikon
Excellent!Step 1 : Foundation - COMPLETE!
Thank you.
12:21 10-11-2009Stivi
Great simple engine with one big disadvantage. Two templates for table (one for structure and one for rows) is not really good solution. Can you fix that or at least guide me how to fix that? Tnx13:49 10-11-2009nunof
Hi Stivi,Thanks for your comment.
This is not intended as a product to be used, but rather as a learning point to something greater.
For including rows you could build a similar solution of how Smarty works: http://www.smarty.net/crashcourse.php
See the example on tables and multiple rows.
Remember that PHP itself is also a template engine.
You could build separate PHP files for your templates, with the powerful advantages of the PHP language itself (foreach, etc.) and use short syntax for variables, i.e. <?=$something?>, without having to learn a template engine's custom language. Short example: http://codeigniter.com/user_guide/general/alternative_php.html
Using short tags is not advisable, though, so you should process them before serving them.
16:47 12-01-2010tommy
hi this good tutail but every time i add a comment systeme.g
function comments($command2)
{
while($result2 = mysql_fetch_array($command2))
{
$start = "Comments";
$val = "";
if($result2[mainpath] == "")
{
$comment = " $result2[username]";
}
else
{
$comment = " $result2[username]";
}
$vali = "";
$result2[message] = wordwrap($result2[message], 40, "\n", 1);
$result2[message] = BBCode($result2[message]);
$result2[message] = BBCode($result2[message]);
$message = "$result2[message]";
}
$end = "";
$ret = "$start $val $comment $vali $message $end";
return $ret;
}
$profile->set("wall_post_", "".comments($command2)."");
only outputs 1 array is there sumthin i have to do ?
14:59 13-01-2010Paddy
This is a very simple example of how a PHP template system works, just what I was looking for.Thank you for this tutorial.
19:34 18-01-2010Gabesz
Nice tutorial!Thanks!
14:25 23-01-2010Dom_
Great! Thanks my friend :-)What about licence/using your code?
14:41 23-01-2010nunof
You can use it freely for whatever purpose. Good luck!20:13 26-01-2010Xand
A good example of how template engines should be built, I use a similar construction myself, only my merge method is a little more complicated, so that I can use only one template file...14:02 12-02-2010qwer
nunof thank you very much for this wonderful tutorial on templating. I am trying to use mysql_fetch_assoc and then parse the data to the template, i am editing the set function to work with array as nunof said,but without succes, so if there is anybody who have succeeded to load data from the database and parsed it into the template, please help me out here.thanks
14:15 12-02-2010qwer
EDIT: I am editing the output function and not the set function.14:44 20-02-2010Hendrik
I was after for understanding of the logic of such template engines and your article explained it very good :)23:14 17-04-2010Daniella
Hey Nunof,First of all, thank you, this tutorial taught me a lot about how a templating system works in the first place.
I was wondering one thing though: is it possible to somehow use PHP inside a .tpl file? I tried simply changing the .tpl file to a .php file, but the PHP code inside it is simply printed out to the browser.
I'm not sure why this isn't working. I thought it would. Could you tell me why it doesn't work?
I really hope to hear back from you!
Thanks,
Daniella
23:48 17-04-2010nunof
Hi Daniella,Thanks for your comment.
In this case putting PHP in your template files won't work, because we're echoing the contents of the file, after they're parsed.
Instead of using a template engine, you might also consider building your templates using PHP alone.
13:58 18-04-2010Daniella
Aaah of course, DOH, it all makes sense now.It's all pretty much one big string.
Thank your for the explanation, I'll think of a different structure for my files.
Thanks!
Daniella
17:58 19-04-2010Desmond
Excelent, I shall use this and add extra bits to it. Thanks for allowing people to use it :)22:57 03-08-2010safipeti
The best tutorial on php template engine I've read so far! Simple, quick and works!Thanks!
safipeti



03:11 09-04-2008kiran
very helpful indeed.