What is Smarty?
Smarty is a PHP framework (or Template Engine, depending on your point of view). It is licensed under the LGPL and works under either PHP4 or PHP5.
Personally, I first heard of Smarty quite a few years ago, while in University installing a PHP based bug and issue tracking system (phpBugTracker). Back then, Smarty hadn't reached a stable release, and I wasn't particularly keen on having to learn yet another mark up in order to use it, and I couldn't see why I should bother using it. Over the next few years, I developed numerous PHP applications, and Smarty remained near the bottom of my to do list. I knew that Smarty was probably something I really should look into using, but kept avoiding it for one reason or another.
Some time later, while involved in developing a PHP application as part of a much larger team I found it increasingly difficult to understand code written by others (or even myself, after a few months had elapsed). My problems weren't caused by a lack of knowledge, but rather by the fact that the code was too convoluted due to the mixture of presentation and logic within a single PHP file - multi-page nested if statements with embedded HTML were making life difficult. At this point I decided to spend a few hours investigating Smarty, and was soon converted.
Smarty has enabled me to cleanly separate logic from the actual content of the rendered page itself. I believe this has resulted in it being easier and faster for me to develop new pages. After 'Smarty-ising' existing code, there appears to be no noticeable increase or decrease in the number of lines of code involved, however everyone in the team agreed that it became far easier to read, maintain and understand.
Basic Templating
At the most basic level, templating can be achieved by removing the common HTML code into separate files and including them into any php file:
<?php include("header.php"); ?>
some content and html goes here
<?php include("footer.php"); ?>
This approach is fine for most purposes and does help reusing common HTML (and/or php) code, for menu generation and setting of page footers and headers. Unfortunately, it does not adapt particularly well to all situations. In the event of the page dealing with form handling or session usage, life becomes slightly more complex.
The relatively simple form below shows how the code can become relatively messy and difficult to follow.
<?php
session_start();
require_once("header.php");
if(isset($_SESSION['username'])) {
echo "Logged in as : " . $_SESSION['username'];
}
if(isset($_POST['id'])) {
$results = database_query("SELECT * FROM blah WHERE id = ?", Array($_POST['id']));
if(sizeof($results>0)) {
echo "<p>The following matches were found:</p>";
echo "<ul>\n";
foreach($results as $item) {
echo "<li>" . $item . "</li>\n";
}
echo "</ul>\n";
}
else {
echo "<p>No matches found.</p>";
}
}
?>
<p>Please enter an ID to search for</p>
<form method='post'>
<input type='text' name='id' value=''>
<input type='submit'>
</form>
<?php
require_once("footer.php");
?>
Fom looking at the above code, we have the following problems :
- We have to do some things before we can include the header page (e.g. session_start() or header() calls).
- Ideally we should only be outputting HTML at the last possible moment
- We have a mixture of PHP and HTML code, which makes the file bulky and difficult to edit
- It wouldn't be very easy to use the same underlying code to generate a page as a .pdf or other format
- Upon adding more logic, it's likely that it will become harder to follow the flow of execution
- The over complex nature of the page means it's highly likely that bugs will be introduced
We can help to solve the above problems by splitting the page into two distinct parts :
- Logic (dealing with form parameters and data retrieval)
- Display (HTML or other output).
Smarty has the answer...
There are numerous templating engines available for PHP, and some projects have the discipline to use native PHP embedded within a file for display of logic. I found Smarty's existing documentation and framework to make the migration far easier, as well as forcing me to resist the temptation to embed PHP based logic within the template.
Smarty is written in PHP, and installation is a matter of downloading a compressed file, extracting it somewhere appropriate (E.g. /usr/local/lib/php) and creating the necessary directory structure within your project. In order to use Smarty from within a PHP page, the following is required :
<?php
require_once("path/to/Smarty.class.php");
$smarty = new Smarty();
....
?>
Jumping straight to the refactored example, the way Smarty can be used, and the benefits of it should become clear.
First, the template file which is used by Smarty (this will normally live in a directory called 'templates'). This file contains HTML markup and some simple Smarty specific syntax. This file has a suffix of .tpl. An example one follows :
{include file='header.php'}
<p>Welcome to the application</p>
{if isset $logged_in_as}
<p>{$logged_in_as}</p>
{/if}
{if sizeof($results > 0)}
<ul>
{foreach from=$results item=i}
<li>{$i}</li>
{/foreach}
</ul>
{/if}
<p>Please enter an ID to search for</p>
<form method='post'>
<input type='text' name='id' value=''>
<input type='submit'>
</form>
{include file='footer.php'}
As you can see, Smarty provides constructs for looping and checking for the existence of variables. Smarty's template language is very similar to PHP in many respects.
In order to populate this template, the following php code could be used :
<?php
require_once("path/to/Smarty.class.php");
$smarty = new Smarty();
if(isset($_SESSION['username'])) {
$smarty->assign("logged_in_as", "You are logged in as " . $_SESSION['username']);
}
if(isset($_POST['id'])) {
$results = database_query("SELECT * FROM blah WHERE id = ?", Array($_POST['id']));
$smarty->assign("results", $results);
}
$smarty->display("our_template.tpl");
?>
As should be apparent, the assign function is used to pass data to the template for display, and the 'display' function is used to select which template we wish to use. Because there is no output to the browser until 'display' is called, we can use any of the functions that output HTTP headers to the browser throughout nearly all of the file without getting the 'headers already sent' error.
Cool Smarty features
When defining a variable within a template, using the {$variable} notation, it's possible to modify them on the fly, using the variable name followed by a pipe (|) and a modifier. For example :
- {$title|upper} which converts $title to uppercase before printing it out
- {$title|truncate:20:"..."} which truncates $title after 20 characters and replaces the truncated part with '...'
- {$comment|nl2br} which converts new lines to <br> characters
A full list of modifiers is available from Smarty's documentation
It's also possible to create your own custom functions, should you so wish. This can be used to, for example, create reusable snippets of HTML code, thereby allowing you to display a particular object in a common manner across all pages. To do this, you will need to do something similar to the following :
- Create a custom Smarty plugin in a file with format function.draw_widget.php
- The file should contain a function defined with 'function smarty_function_draw_widget'
- The smarty_function_draw_widget should contain return appropriate HTML.
For example :
<?php
// File: function.draw_widget.php
function smarty_function_draw_widget($params, &$smarty) {
$html = "<div class='widget_name'>" . $params[0]['name'] . "</div>";
$html .= "<a href='view.php?id=" . $params[0]['id'] . "'>view full details</a>\n";
return $html;
}
?>
This can then be used within a template as follows : {draw_widget $the_widget}
You will probably need to tell Smarty where to find plugin files - something like :
$smarty->plugins_dir[] = "/some/where";
Should do the trick
Smarty can improve security
Hopefully, you're thinking Smarty is the best thing since sliced bread - but if not, it can also be used to protect your web application from XSS (Cross Site Scripting). There are two ways this can be done :
- Firstly, Smarty can automatically convert all variables to HTML safe versions, through use of the $smarty->default_modifiers = array('escape:"htmlall"').
- Secondly, if using PHP5 you could extend the Smarty object and override the behavior of $smarty->assign().
Through either approach, you will gain automatic escaping of HTML characters within your application, with the programmer by default opting into the secure practice, and having to purposely opt-out if necessary.
Debugging
If you'd like to see a little bit more information from Smarty, then it's possible to turn on the debug mode, which results in a pop-up window being displayed when the page is rendered in your browser, as follows :
$smarty = new Smarty();
$smarty->debugging = true;
....
$smarty->display("mytemplate.tpl");
Comments
multiple plugins locations
I do not like to garbage my plugins with three different types:
a. The smarty core plugins (which may be likely to change all the times)
b. My generic plugins that are reusable.
c. My application (or, I call directory) related plugins that are not reusable.
Since Smarty supports plugins_dir as array of physical relative/absolute locations, I clearly write those plugings differently! See the below example.
/**
* Custom plugins developed, are put here.
*/
$smarty->plugins_dir[] = __PARENT_ROOT__.'/smarty_plugins'; # Common plugins
$smarty->plugins_dir[] = 'smarty_plugins'; # Application related plugins
Here, even if you do not write your own spcific plugins (at level c,) it does not harm any!
Indeed
We do the same (at work) - where we have a set of core smarty plugins which are used across all projects. We can either create new ones, or over-ride existing ones within a specific project on a as-needed-basis
Post new comment