Getting Started With PHP Templating
In the early days of PHP applications, “spaghetti code” was a familiar sight. In this article, we’ll cover how to separate the view of your PHP application from its other components. We’ll look at why using such an architecture is useful and what tools we can use to accomplish this. Here’s what we’ll cover:
- Learn some basic MVC concepts,
- Review some popular templating libraries,
- Play around with a small custom-made view class.
- Explore the basics of using the Twig library.
To fully benefit from this article, you should already know how to write and run your own PHP scripts on a Web server (i.e. using Apache).
A Quick Introduction To The MVC Pattern
In the early days of PHP applications, “spaghetti code” was a familiar sight. Fragments of PHP code were mixed in with HTML mark-up. There were no frameworks, so Web applications were just a bunch of source files. As the PHP language matured, developers started to think about the cleanliness and maintainability of their code. The model-view-controller (MVC) pattern was introduced.
MVC is a software architecture that allows for the separation of business logic from the user interface. In this architecture, the user sees and interacts with the view that, in the case of Web applications, is generated HTML code (along with JavaScript, CSS, images, etc.)
User actions are passed (as HTTP requests, GET
or POST
methods) to the controller. The controller is a piece of code that handles and processes user input and then reads and makes necessary changes to the model, which is responsible for the storage and modification of data. (In simple terms, the model consists of the database structure and contents, and the code used to access it.) Then, the controller generates the proper view that will be sent and displayed to user.
Such separation of layers has many advantages…
Code Is Easier To Maintain
Because the model is separated, changing internal data relations without changing the rest of the application is easier. For example, the model for the user could provide the is_active()
method, returning a boolean variable. In the business logic, it would be enough to check what value is returned by this method, without any knowledge of its internals.
You can also check for various conditions (for example, whether the user has confirmed their registration, paid their monthly fee, etc.), and you can change these rules in one place, without having to change anything in the other parts of the code. It also becomes easy to make a platform-independent application that allows for switching between database engines; this is often performed by ORM (object-relational mapper) libraries, which are a sub-layer of the model layer. ORMs add a level of abstraction to the library that accesses the specific database directly.
The Same Content In Multiple Views
Separating the view allows a single result to be presented in different forms. For example, based on some logic, a news page could be displayed in normal, mobile and RSS versions, all using the same content returned from the controller.
More Secure
Variables are escaped by default before being sent to the view, which is useful in applications that display user-generated content. Consider a variable with some unknown HTML or JavaScript code being passed to the view (which should never happen in your application). Escaping the variables prevents malicious code from being injected in such cases.
Better Code
Separating the layers forces the programmer to design better applications that are easier and cheaper to maintain. Also, designers and front-end developers don’t have to work with the business-logic code, because their task is only to display the contents of the variables that are provided to the view. This reduces the risk of the breaking some PHP code.
Today, every modern framework implements the MVC architecture. There are also some libraries that enable you to use selected features of the architecture. In this article, we’ll go over the recipes for the view, and in future articles we’ll cover the model and some ORM libraries.
Overview Of PHP Template Engines
As you might expect, many templating libraries allow you to separate the view layer from the rest of the application. In the most basic scenario, you can implement such a library by yourself (which we’ll do in the next section). You don’t even need a special library; sometimes it is enough just to separate your view (i.e. the template’s HTML files) into a different directory, prepare some variables in the simple controller (usually the main PHP file) and include the templates.
Every modern PHP Web application framework employs some kind of templating engine. Most of them use plain PHP by default (including Symfony 1.x, Zend Framework and CakePHP), but many standalone libraries can be plugged into your favorite framework or custom application.
Smarty
Smarty was one of the first and most advanced templating engines. It was developed as a sub-project of the main PHP project, but it has lost its popularity in recent years due to poor performance and forced backward compatibility with PHP 4. It has also lacked many modern features, such as template inheritance. Now, it is rising back to power with version 3. Many other frameworks (such as Twig and Django) have inherited some of the basic concepts laid down by Smarty.
<html>
<head>
<title>Info</title>
</head>
<body>
<pre>
User Information:
Name: {$name|capitalize}
Addr: {$address|escape}
Date: {$smarty.now|date_format:"%b %e, %Y"}
</pre>
</body>
</html>
PHPTAL
PHPTAL is a template language based on a very different syntax and concept; it implements the Zope Page Templates syntax (Zope is an application server written in Python). It is based on well-formed XML/XHTML, as in this example that comes from the project page:
<div tal:repeat="value values">
<div>
<span tal:condition="value/hasDate"
tal:replace="value/getDate">
2008-10-06
</span>
<a href="sample.html"
tal:attributes="href value/getUrl"
tal:content="value/getTitle">
My item title
</a>
</div>
<div tal:content="value/getContent">
This is sample content that will be replaced by
real content when the template is run with real
data.
</div>
</div>
If you have prior experience with ZOPE or are a fan of XML, then this template engine is for you.
Twig
The last PHP template engine we’ll look at, and the one we’ll focus on in this article, is Twig, from the authors of Symfony, which is the default view library of this framework’s 2.0 version. Its advantages are rich features, extensibility, good documentation, security and speed (it compiles your templates to the native PHP language).
<html>
<head><title>My first Twig template!</title></head>
<body>
My name is {{ name }}.
My friends are:
<ul>
{% for person in friends %}
<li>{{ person.firstname}} {{ person.lastname }}</li>
{% endfor %}
</ul>
</body>
</html>
The Basic Concept
Knowing how to use a particular library or framework is not enough. A good programmer should also know what is under the hood: how the stuff works, the relationship between the components, and possible caveats. For this reason, we’ll go over the concept with a simple templating library, custom-made for this tutorial. This should give you an idea of how more advanced libraries work. To run this code on your own, you will need to properly configure your HTTP server (with Apache), using PHP 5. No other libraries are needed.
Note: you can download this example code from the Mercurial repository on Bitbucket either by downloading the ZIP file or, if you have a command-line version of Mercurial installed, by entering the following command:
hg clone https://bitbucket.org/krzysztofr/sm-view
.
First, let’s look at the code.
MyView.php: Templating Library
<?php
class MyView {
protected $template_dir = 'templates/';
protected $vars = array();
public function __construct($template_dir = null) {
if ($template_dir !== null) {
// Check here whether this directory really exists
$this->template_dir = $template_dir;
}
}
public function render($template_file) {
if (file_exists($this->template_dir.$template_file)) {
include $this->template_dir.$template_file;
} else {
throw new Exception('no template file ' . $template_file . ' present in directory ' . $this->template_dir);
}
}
public function __set($name, $value) {
$this->vars[$name] = $value;
}
public function __get($name) {
return $this->vars[$name];
}
}
?>
The main (and only) class of our templating engine is very simple. We use the “magic methods” (yes, that’s the official name) __set()
and __get()
to pass variables to the internal repository and then, in the template script, read from it. (A little explanation on magic methods: they are called when you try to read or write to nonexistent properties of the class. You can read more on this topic in the chapter on overloading in the official PHP manual.) As a bonus, I’ve added a configurable template directory.
Invoking our library is straightforward. In this example, I’ve invoked two views, although different templates should be used in the actual application, depending on the application’s logic.
index.php: Controller
<?php
include_once('MyView.php');
$t = new MyView();
$t->friends = array(
'Rachel', 'Monica', 'Phoebe', 'Chandler', 'Joey', 'Ross'
);
$t->render('index.phtml');
$t->render('index.xml');
?>
The templates look like this…
index.phtml: HTML Template
<html>
<body>
Names of my friends:
<ul>
<?php foreach ($this->friends as $friend): ?>
<li><?=$friend?></li>
<?php endforeach; ?>
</ul>
</body>
</html>
index.xml: XML Template
<?='<?xml version="1.0" encoding="utf-8"?>'?>
<myfriends>
<?php foreach ($this->friends as $friend): ?>
<friend><?=$friend?></friend>
<?php endforeach; ?>
</myfriends>
If you have basic knowledge of PHP, you can imagine how this works. Some logic is being used to set the variables, to read them from the database and so on, and then these variables are passed to the object of our view class. Next, we can access these variables in the template file, which is in HTML format, and the second one is XML (but could be in any other: JSON, plain text, CSS, JavaScript, etc.).
That’s the main idea behing the MVC model (without the “M,” in this example): the business logic happens in one place, while the content generation is sent to the user (or view) in another place. From one set of variables, you can generate many different views. In the template, you’re using plain PHP to display the variables.
Twig Library (With Tutorial)
The Twig library basically does the same job as the simple example above, but in a more sophisticated way. It has many useful features that enable you to develop and maintain your projects more easily and quickly.
Installation
Let’s start this short tutorial with the installation. Twig requires PHP 5.2.4 or above, but I assume that a PHP-enabled HTTP server (such as Apache) is already at your disposal. The easiest way to get it is to clone the repository on GitHub:
git clone git://github.com/fabpot/Twig.git
Or you could get it from the SVN repository:
svn co https://svn.twig-project.org/trunk/ twig
Alternatively, you could install it using PEAR:
pear channel-discover pear.twig-project.org
pear install twig/Twig
Lastly, you could get the file from the project’s download page.
Configuring The Environment
Once the library is installed, you can start playing with it. I assume you will prepare your own application logic, which is beyond the scope of this article, so we’ll skip to the part where we already have something to display.
First, we’ll need to include the Twig library and register its autoloader:
require_once './twig/lib/Twig/Autoloader.php';
Twig_Autoloader::register();
Notice that we have to provide the proper path to the Autoloader.php file, which is in the lib/Twig
directory of the downloaded source package. This piece of code enables us to use all of the Twig libraries without having to explicitly include them. You should be familiar with this technique if you use other popular libraries or frameworks (such as Zend Framework and PEAR).
The next essential part of the Twig library is the loader. This is a class that tells the Twig engine how it should load the templates. With the object of this class, we can initialize the main part of the engine, the Twig environment. Here’s an example:
$loader = new Twig_Loader_Filesystem('./templates');
$twig = new Twig_Environment($loader, array(
'cache' => './tmp/cache',
));
In this example, Twig_Loader_Filesystem
is initialized with the directory where templates are stored as a parameter. An instance of the loader class is used to initialize the Twig environment as its first parameter. The second parameter is an array of options. The most important of them are these:
cache
The directory where cached templates will be stored (more on the cache later). If you don’t provide a valid path here, the templates will not be cached (which is the default setting). Remember that you have to set the proper permissions for the cache directory so that your script can write there.auto_reload
This tells Twig to reload the templates every time they change. If it’s set tofalse
, the templates will be reloaded only if you delete the contents of the cache directory. This is useful if you need to improve the performance of your website (and you rarely modify your templates).autoescape
This determines whether variables are escaped in the view automatically (the default istrue
).
Twig_Loader_Filesystem
is the most common way to load the templates. For a parameter, you can provide a single string variable or (if you have two or more directories with template files) an array of strings. In such situations, Twig looks for the requested template files in the same order as the paths appear in the array.
Another loader that you might want to use in certain situations is Twig_Loader_String
, which enables you to provide the template directly in the form of a string. This can be useful if you store your templates in some form other than files in a file system (for example, if you generate them dynamically, store them in the database, store them in some cache, etc.).
Twig is able to speed things up by caching your templates. That is, Twig processes the templates not every time you need them, but only once, when it detects that the template’s contents have changed (if you have enabled the auto_reload
option). Pure PHP code is generated and stored in the cache directory. (Look into the cache files to learn about Twig’s internals.)
Templates
So, how do we display our first template? Let’s make it a simple “My name is [your name here]” page, with a list of some other items. First, prepare your template file. It will be very simple, because it contains only the basic output:
<html>
<head><title>My first Twig template!</title></head>
<body>
My name is {{ name }}.
My friends are:
<ul>
{% for person in friends %}
<li>{{ person.firstname}} {{ person.lastname }}</li>
{% endfor %}
</ul>
</body>
</html>
Save this file in the templates directory. The convention is to save a template with the .phtml extension, so let’s save ours as hello.phtml. Now we’ll prepare the script that will display the template. Its content will be as follows:
<?php
require_once './twig/lib/Twig/Autoloader.php';
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem('./templates');
$twig = new Twig_Environment($loader, array(
'cache' => './tmp/cache',
));
$template = $twig->loadTemplate('hello.phtml');
$params = array(
'name' => 'Krzysztof',
'friends' => array(
array(
'firstname' => 'John',
'lastname' => 'Smith'
),
array(
'firstname' => 'Britney',
'lastname' => 'Spears'
),
array(
'firstname' => 'Brad',
'lastname' => 'Pitt'
)
)
);
$template->display($params);
?>
In this code, the Twig autoloader is included and registered. Then, we created the loader from the file system, which looks for the template files in the templates directory in our project’s root. In the next step, the environment is initialized with the previously created loader. We’ve enabled caching as a parameter by providing a path to the cache directory. As a result, the following code is sent to the browser:
<html>
<head><title>My first Twig template!</title></head>
<body>
My name is Krzysztof.
My friends are:
<ul>
<li>John Smith</li>
<li>Britney Spears</li>
<li>Brad Pitt</li>
</ul>
</body>
</html>
If you look again at the template, you will notice some basic rules:
- The
{{ … }}
tag is used to print the variable or expression; - The
{% … %}
tag is used for flow-control statements, such asif
andforeach
; - You can access the array elements by using the
myarray.item
syntax. (It’s actually more sophisticated at the implementation level: it checks for the elements of the array, the object properties and their methods, and it even looks for the methodsgetItem
andisItem
— read more about it in the documentation).
But what if you want to send the browser an XML response rather than an HTML file? Nothing could be simpler. Just prepare the XML template, like so:
<?xml version="1.0" encoding="utf-8"?>
<helloworld myname="{{ name }}">
<friends>
{% for person in friends %}
<friend firstname="{{ person.firstname}}" lastname="{{ person.lastname }}"/>
{% endfor %}
</friends>
</helloworld>
Save it as, say, hello.xml in the templates directory. To generate the result, just load this file instead of index.phtml in the PHP script, and you’re done. (Of course, in a real environment you might want to use more sophisticated logic to differentiate between the XML and HTML output).
This example shows how easy it is to use Twig. In the following examples, I will leave out the whole initialization and rendering phase, just to show the relevant pieces of the template’s code.
Filters
Filters are a useful concept and are common to many templating engines. Filters are applied to the variable to change its contents and print it to the template. Take the following code:
{{ myvar|upper }}
This will affect the variable being printed, and all lowercase letters will be switched to uppercase. Filters can be chained, too. So, you could strip out any HTML tags and convert the variable to uppercase with following code:
{{ myvar|striptags|upper }}
Filters can also take parameters. An example is the replace filter, whose purpose is obvious:
{{ "I like Twig."|replace({'like':'love', 'Twig':'you'}) }}
The following filters are built in:
date
format
replace
url_encode
json_encode
title
capitalize
upper
lower
striptags
join
reverse
length
sort
default
keys
escape
e
raw
merge
Most of these are self-explanatory. You can check the details in the documentation.
Control Structures
In templates, most of the operations you will be doing will be looping over a set of data (listing some items), sometimes accompanied by conditional statements (if … then
). This is why Twig has implemented only for
and if
control structures, but they are quite powerful.
The for
loop was presented in its simplest form in the previous section. Below are some more sophisticated examples.
Here is the code to iterate over a sequence of numbers:
{% for i in 0..10 %}
item number {{ i }}
{% endfor %}
You can do the same with letters:
{% for l in 'a'..'z' %}
{{ l }}
{% endfor %}
You can use variables and filters on both sides of the ..
operator.
To access the keys of an array, you can use the following:
{% for k in myarray|keys %}
key: {{ k }}
{% endfor %}
To access keys and values at the same time, you can use this:
{% for key, val in myarray %}
key: {{ key }}, val: {{ val }}
{% else %}
no items in the array
{% endfor %}
Notice the else
block. It is rendered when the array is empty and no iteration takes place.
You may have noticed that the author of Twig was highly inspired by the behavior of the for
structure in the Python language.
Important: you cannot use break
or continue
statements in the Twig version of the for
loop. On the other hand, you do have access to a few useful variables inside the loop:
loop.index
loop.index0
The iteration number. It is counted from 1 by default, so useindex0
to make the index number start from 0.loop.revindex
loop.revindex0
This is the same asindex0
but counted from the end of the loop.loop.first
loop.last
This istrue
if it is the first or last iteration in the loop.loop.length
The if
statement is almost the same as it is in pure PHP. You can use if
, else
and elseif
tags. For example:
{% if user.role == 'admin' %}
Hello, administrator!
{% elseif user.role == 'user' %}
Hello, user!
{% else %}
You don't have a role defined.
{% endif %}
As with the for
loop, the operators in Twig are highly inspired by the ones in Python. For example, here we’ll check whether John
is in the list of friends:
{% if 'John' in friends %}
Or you could use a predefined list:
{% if 'John' in ['Bob', 'Dave', 'John', 'Mike'] %}
To negate the value, use the word not
, like so:
{% if 'John' not in friends %}
You may want to use tests in conditional statements, in addition to operators. For example:
{% if var is divisibleby(2) %}
Here are the tests that are built in:
divisibleby
none
even
odd
sameas
constant
defined
empty
This covers most of the basic syntax of Twig. To go deeper, you should read the extensive documentation.
Template Inheritance
As the creator of Twig states, template inheritance is the most powerful and important feature of Twig. It lets you define a main template as the base of other templates depending on the business logic. For example, you could define the base template below and save it to the file layout.phtml, in which you might want to define the basic layout of your website.
<html>
<head>
<title>{% block title %}My page{% endblock %}</title>
</head>
<body>
<div id="left-box">{% block leftbox %}This is the left box.{% endblock %}</div>
<div id="content">{% block content %}{% endblock %}</div>
</body>
</html>
Then, by calling the template that extends layout.phtml, you will be able to add blocks containing only content in addition to the base template.
{% extends "layout.phtml" %}
{% block title%}{{ parent() }} - About me{% endblock %}
{% block content %}Lorem ipsum dolor sit amet.{% endblock %}
You can call the parent block using the {{ parent() }}
function and extend it. If you don’t call a block (leftbox
, in this example), its content will be left intact, as is the case with the parent template.
Template inheritance could help you create a flexible template architecture. Read the documentation to learn the details.
Incorporating Twig Into An Existing Project
Because Twig is so flexible an engine, there is no one specific way to incorporate it into your project. You just have to place the Twig library somewhere in your project and create directories for the cache and templates. The locations of all of these are up to you. Furthermore, you can define a PHP script as an entry point to your application, or include and instantiate the Twig object in every script in your project.
Conclusion
I hope you enjoy working with template engines such as Twig and that they increase your productivity and the cleanliness of your code. Good luck!
Further Reading
Here are some websites you might find useful:
- Sample code from the Mercurial repository on Bitbucket
- “Twig Documentation”
- “Twig Documentation - Sandbox extension”
- “Templating Engines in PHP” (a comparison), Fabien Potencier
- Documentation for Twig in Symfony 2.0