Building a Template Parser Class with PHP, Part I

It is easy to create a templating system in PHP; in fact, there are a number of templating system packages. But what if you’re putting together a relatively small website, and don’t really need one of those full-fledged systems? In this first part of a two-part article, you will learn how to create a simple but extensible PHP class for parsing templates.

Introduction

 

Without a doubt, PHP is one of the most popular languages currently available for fast Web development. Its extreme ease and flexibility has made it a great source for creating a wide gamut of applications, well suited for satisfying the needs of different projects. Certainly, one of the most common applications that have grown noticeably in popularity is what is widely know as a templating system.

 

The question is: what is a templating system? Well, in short terms, it’s an easy way to create large sites with some effort to maintain a consistent appearance through the different documents. Just by having only one template file, which stores the HTML markup and the data placeholders, we are able to manipulate it with a PHP script that takes the template file and replaces those placeholders with the corresponding content, in order to generate the final version of the page. The concept behind templating is very handy, since it allows us to separate the markup and visual presentation from the logic of the application in different layers.

 

As a PHP programmer, you probably have used some of the most popular template system packages, including the powerful Smarty, or other solutions such as Fast Template or Pat Template among others (add your own to the list). Most of these packages are full-featured and mature solutions designed to be generally implemented on medium and large size projects. They present numerous options that sometimes are pretty cumbersome, particularly when dealing with small projects, where templating needs are rather basic.

 

Okay, right now I can hear some well-intended programmers complaining about this, arguing that those packages can be perfectly implemented for the tiny lovely pet website of Uncle Johnny, including a huge Microsoft SQL Server database system, and so on. You get the idea. But, let’s be honest. For small applications we need a simple templating system that nicely fits our needs.

 

Because of the reasons I mentioned above, I decided to write on my own a simple but extensible PHP class for parsing templates. I designed it to satisfy the minor requirements of small websites. Fine, are you ready to put our hands on the code? Just follow me. We’re involved in this situation together!

 

{mospagebreak title=PHP: The first templating system available}

 

In just a minute, you may think that I listened to too much strange music at my sister’s wedding, but I really didn’t. Please consider the following code:

 

<?php

// lets’s define some variables

$title=’This page is pretty musical!';

$content=’There are unsmiling faces and bright plastic chains, and a wheel in perpetual motion.';

$author=’The Alan Parsons Project.';

// now let’s replace variable values in the document

?>

 

<html>

<head>

<title><?php echo $title?></title>

<meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1″ />

</head>

<body>

<div>

<p><?php echo $content?></p>

<p>The author of this song is: <?php echo $author?></p>

</div>

</body>

</html>

 

The above example shows us that PHP is itself a templating system. Of course, this approach is not recommended for many reasons. Here we’re mixing up PHP code with HTML markup, which you do not want to do if your goal is to keep applications in different layers.

 

To improve the situation and maintain applications in separated layers, we might define our template in the following way:

 

<html>

<head>

<title>$title</title>

<meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1″ />

</head>

<body>

<div>

<p>$content</p>

<p>The author of this song is: $author</p>

</div>

</body>

</html>

 

And next, the PHP code to replace the variables with the corresponding values:

 

<?php

 

// lets’s define some variables

 

$title=’This page is pretty musical';

$content=’There are unsmiling faces and bright plastic chains, and a wheel in perpetual motion.';

$author=’The Alan Parsons Project.';

 

// now let’s replace their values in the document

ob_start();

include(‘templates/template.htm’);

$pageHTML=addslashes(ob_get_contents());

ob_end_clean();

 

// parse the template to replace variables with their values

eval(“$pageHTML=”$pageHTML”;”);

 

// send parsed file to the browser

echo $pageHTML;

 

?>

 

As you can see, the situation is much more acceptable. We have a single HTML template file, where we’ve defined some variables as placeholders. Then, starting an output buffer, we grab the content of the included template file, escaping any characters that would break a PHP string, and finally dumping it to a variable. Using “eval()” we evaluate a string as if it were PHP code. This means that the variables in our template are replaced by the values previously assigned. We’re getting closer to creating a simple template parser, but still the above approach is not good enough. Still, it is a basic technique for parsing template files.

 

However, we need to have more flexibility at the moment of defining the placeholders, looking for an object-oriented solution. This way our code is more reusable and encapsulated. Taking this into account, we’re going to define a simple PHP template parser class for processing template files and send the finished page to the browser, or wherever you want to submit it. Let’s take a look at the basic structure of the PHP class.

 

{mospagebreak title=Defining the structure of the PHP class}

 

Despite the fact that PHP4 doesn’t offer a full-featured object-oriented programming model, with a bit of good will it’s quite possible to create classes and manipulate them in a decent way for the purposes of any project. Fortunately, with the release of PHP5, Object Oriented Programming has been greatly enhanced, introducing object-oriented features such as destructors, exception handling, true public and private methods and properties, and so on.

 

Regarding our class, we’re going to create it in PHP4, but it may be easily adapted to work seamlessly in PHP5. Let’s not waste more time in preliminaries and set up the class’ basics. Here’s the initial definition:

 

<?php

class templateParser {

    

    // member definition

 

    var $output;

 

    function templateParser(){

 

    // constructor setting up class initialization

    }

 

    function parseTemplate(){

 

    // code for parsing template files

    }

 

    function display(){

 

    // code for displaying the finished parsed page

    }

 

}

?>

 

As can be deduced from the listed code, the class will expose three main methods for parsing template files (including the constructor). Please notice that I’ve defined the class property “$output”, which will store the code for the parsed page as it’s being generated. That’s the only property that we need. For now, the class is very simple.

 

The constructor will accept one parameter, the template file to be parsed. Therefore, before we go deeper into the template parser class code, it’s necessary to have a template file for processing.

 

I’ve chosen a typical three-column design, with a “header”, “navbar”, left and right columns, and “footer” sections. Also I decided to delimit the placeholders using braces, since they’re seemingly the most common characters used in template files; hopefully, they allow more compatibility with other possible template systems. Having defined the general guidelines, the template file would look similar to this:

 

<!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>

<meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1″ />

<link rel=”stylesheet” type=”text/css” href=”style.css” />

</head>

<body>

<div id=”header”>{header}</div>

<div id=”navbar”>{navbar}</div>

<div id=”leftcol”>{leftcontent}</div>

<div id=”content”>{maincontent}</div>

<div id=”rightcol”>{rightcontent}</div>

<div id=”footer”>{footer}</div>

</body>

</html>

 

As you can see, the template is extremely simplistic. However, keep in mind that each placeholder might be replaced with more complex structures, either from static or dynamic files. Now we’re moving forward. Since we’ve already defined the template file, let’s add some functionality to the class constructor, which takes the template file as the unique parameter:

 

function templateParser($templateFile=’default_template.htm’){

 

    (file_exists($templateFile))?$this->output=
file_get_contents($templateFile):die(‘Error:Template file ‘_
.$templateFile.’ not found’);

 

}

 

Let’s explain the tasks performed by the constructor. First, the method checks for the existence of the template file passed as parameter. If the file is found, it grabs the file contents using the “file_get_contents()” PHP built-in function, assigning them to the private variable (in fact it’s a property) $output. Since sometimes is useful to specify default values for incoming parameters, I’ve assigned a default file for the template file. In this case, the default template file is “default_template.htm”, but it might be overridden, only passing the name of the other file to the constructor. If no template file is found, I stop the script from executing, displaying an error message to the user.

 

Now, the best part is coming up. It’s time to have a look at the “parseTemplate()” method, which is the main engine of the class. Let’s define it in the following manner:

 

function parseTemplate($tags=array()){

  if(count($tags)>0){

    foreach($tags as $tag=>$data){

      $data=(file_exists($data))?$this->parseFile($data):$data;

      $this->output=str_replace(‘{‘.$tag.’}’,$data,$this->output);

      }

  }

  else {

       die(‘Error: No tags were provided for replacement’);

  }

}

 

The method accepts an incoming array as a parameter, which stores the data to be inserted into the template placeholders. In this case, I’ve specified again a default “$tag” array, for passing data. Then, the method checks to verify whether the incoming “$tags” array is not empty. If it is not, it iterates over each array element, checking whether the value is a file. If the value corresponds to a file, the method calls the “parseFile()” private method, that, as the name suggest, will parse any file passed as an argument, including files with dynamic content, returning the file contents and assigning them to the local variable $data.

 

Don’t worry; we’ll see this method in detail in a moment. Let’s go back to the current method. If the value is not a file, it’s directly assigned to the $data variable. Next, the placeholder replacement operation is performed over that $data variable, substituting the placeholders with the proper values, and finally assigning the result to $output. Don’t forget that inside the class we’re referencing any property or method belonging to that class with the prefix “$this”.

 

As you can appreciate, this last section is the workhorse of the method, since it’s where the placeholders’ replacement really occurs. If no valid array parameters are supplied, then I kill the script with the usual die statement, displaying a message that indicates the error. I’m sure the code is pretty easy to follow.

 

Well, I feel a little more comfortable now, having explained the “parseTemplate()” method, because it’s really the main performer of the class. There are still a couple of methods to explain. So, let’s move on and take a look at them.

 

{mospagebreak title=Completing the class: the “parseFile()” and “display()” methods}

 

We have the class still in an incomplete state. As I stated before, the “parseTemplate()” method calls internally to “parseFile()”. Why did I decide to implement this method? The reason for its existence is simple. I want the class to parse any kind of files either with static or dynamic content.

 

Let us suppose that we’re generating the content section of the page, including some dynamic data such as date information or PHP variables populated with database records. The need to process files with dynamic information becomes evident, expanding the class’ capabilities. Let’s look at the corresponding code for this method. Its definition is the following:

 

function parseFile($file){

    ob_start();

    include($file);

    $content=ob_get_contents();

    ob_end_clean();

    return $content;

}

 

In order to parse files with dynamic data, the method starts an output buffer, includes the file and retrieves the file contents from the buffer. Next, it clears up the buffer and returns the data. That’s simple and straightforward, right?

 

At this point, the class is capable of parsing template files, replacing the placeholders with static or dynamic information and generating the page. What more can we ask for? Of course, we still need to display the corresponding content for the page generated. So, let’s define the last method of the class, the “display()” method:

 

function display(){

           return $this->output;

}

 

Whew, I’m sure you’re completely overwhelmed by the code listed above. Just kidding. In fact, it’s all we need to define the method. It simply returns the contents of the page generated after the replacement operation.

 

You may be wondering why I am not displaying directly the page, instead of returning its contents? Generally it’s a bad idea to display contents inside the class, because we might need to do something different with the data (i.e. send it via email). That’s why the method returns the page instead of displaying it directly in the browser.

 

The class is now finished and ready to be implemented in any project. It’s time to put the class into action.

 

{mospagebreak title=Implementing the class}

 

Before we see how the class is practically implemented, let’s see the complete source code. Here’s the listing:

 

class templateParser {

    var $output;

    function templateParser($templateFile=’default_template.htm’){

          (file_exists($templateFile))?$this-
>output=file_get_contents($templateFile):die
(‘Error:Template file ‘.$templateFile.’ not found’);

    }

    function parseTemplate($tags=array()){

          if(count($tags)>0){

               foreach($tags as $tag=>$data){

                    $data=(file_exists($data))?$this-
>parseFile($data):$data;

                    $this->output=str_replace
(‘{‘.$tag.’}’,$data,$this->output);

               }

          }

          else {

               die(‘Error: No tags were provided for replacement’);

          }

    }

    function parseFile($file){

          ob_start();

          include($file);

          $content=ob_get_contents();

          ob_end_clean();

          return $content;

    }

    function display(){

          return $this->output;

    }

}

 

Finally, here’s a possible real implementation for the class:

 

<?php

// include the class

require_once(‘template.php’);

// instantiate a new template Parser object

$tp=&new templateParser(‘template.htm’);

// define parameters for the class

$tags=array(‘title’=>’You are seeing the template parser class in action!’,’header’=>’header.php’,’navbar’=>
‘navigation bar.php’,’leftcontent’=>’leftcontent.php’,
‘maincontent’=>’maincontent.php’,
‘rightcontent’=>’rightcontent.php’,
‘footer’=>’footer.php’);

// parse template file

$tp->parseTemplate($tags);

// display generated page

echo $tp->display();

?>

 

The above code is really easy to follow. First, I include the proper class file. Then a new template parser object is instantiated, specifying a “template.htm” file as the template to be parsed.

 

Please note that I’m instantiating the object with the (&) ampersand operator, which means that I’m working with a reference of the object, not a copy. This is necessary in PHP4 for specifying the behavior when we’re instantiating objects. Fortunately, in PHP 5, the default behavior is working with object references.

 

Next, I define an array structure containing the parameters to be passed to the class. In the example, I’ve chosen a string for page title, and several PHP files for generating the different sections of the page. As you can see, the class offers the possibility to include dynamic or regular content. 

 

Finally, the object invokes the “parseTemplate()” method, which will replace the placeholders with the real values contained in the “$tags” array. Once the template is processed, the “display()” method is called, displaying the generated page. Here, it becomes clear that instead of directly echoing page contents, we might, for instance, send the page via email. Doing so makes the class much more flexible.

 

Summary

 

Are you still with me? Okay, we have finally created a simple but extensible class for parsing template files with no major headaches. What’s more, the code is easily portable to PHP 5 with minor modifications. However, the job is not completely done. In the second part of this article, I’ll add some caching capabilities to the original class, increasing its current functionality. In the meantime, feel free to play around with the code and think about possible improvements. I’ll meet you in the second part!  

[gp-comments width="770" linklove="off" ]

chat