Building a Template Parser Class with PHP, Part II

In part one of this two part article series, you learned how to build a simple template parser class in PHP. In this second article, you will learn how to add caching capabilities to the class.

 

Introduction

 

Welcome to the second part of the article “Building a Template Parser class with PHP.” In the first part of the article, I’ve hopefully demonstrated that building a simple template parser class is not as intimidating as it seems. Given the flexibility and power that PHP offers, it’s possible to implement efficient applications without scratching our heads.

 

Since the original version of the class was quite simple, I felt strongly tempted to expand it, adding a little more functionality. So, I thought that incorporating caching capabilities to the class, without incurring time-consuming processes, was worth considering. In this second part, I’m going to show how to improve the original class, by adding a simple caching mechanism, with little modifications. So, let’s get started. 

 

Refreshing memories: a quick look at the original class

 

Before we get into the process of tweaking the class code a bit, it would be good to remind ourselves how the original version looked, in order to establish a starting point for implementing the new caching capabilities. Here’s the full code for the template parser class:

 

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;

    }

}

 

Since we’re talking about a template class, there must be a template file for proper processing. Therefore, let’s show the code for the template file used, as it was originally conceived:

 

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.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>

 

Now, things are beginning to make sense, and I can briefly explain the underlying logic of the class. To begin with, the class constructor accepts the template file as the only parameter for being processed. After checking whether the file really exists, it simply assigns the file contents to the $output variable, which is storing the page, as it’s being generated. Then, the workhorse method of the class, that is, “parseTemplate()”, takes the incoming $tags array for replacing all of the placeholders present in the template file, along with the corresponding data, stored in the mentioned $tags array. Doing so, the placeholders {header}, {navbar}, {leftcontent}, and so forth, will be substituted will real data, coming from regular variables or files.

 

As you can see, the method is calling internally to “parseFile(),” which takes care of retrieving the contents from any file that might be included as part of the $tags array, parsing static or dynamic content. This is extremely handy for processing data from PHP variables, including any kind of dynamic information. Stick with me, because the explanation is almost done.

 

Once the “parseTemplate()” method has performed the replacement process, the remaining task is a breeze. The class exposes the “display()” method for getting the finished version of the page, which is stored in the $output variable, to be displayed in the browser, or processed in another way, according to the application’s needs.

 

Well, maybe this is not shocking news, but if we feed the class with a correct template file, we obtain a finished page populated with the corresponding data. This is a simple and effective way to give websites a consistent look with no big hassles. The final class implementation might be something similar to this:

 

<?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();

?>

 

Okay, if you read the first part of this article, the above code is pretty familiar, but for those just coming in for this sequel, the previous explanation was well spent. Now, having briefly explained the core logic behind the class, let’s get ready to add a pinch of caching capabilities. Just keep reading to find out more.

 

{mospagebreak title=One step forward: defining caching methods}

 

The first question that came to my mind when I decided to implement a caching mechanism was: is it really worth having those capabilities? The answer was a resounding yes! Let’s think about it. If we’re working with websites that don’t change their content very often, say on a daily basis, it’s highly desirable to have a caching system that simply reads the contents to be displayed from a flat file, and delivers them directly to the browser, without the need to process a template file each time the requested page has to be rendered. That sounds like common sense..

 

With this conclusion firmly in mind, I quickly started implementing those desirable caching capabilities inside the class. To keep things ordered, I defined two class methods for reading and retrieving data from a specific cache file, specifying a time-based cache validity policy. The first caching method, “readCache()” simply reads data from a cache file, and is defined in the following way:

 

function readCache($cacheFile,$expireTime){

    if(file_exists($cacheFile)&&filemtime($cacheFile)>
(time()-$expireTime)){

          return file_get_contents($cacheFile);

    }

    return false;

}

 

As you can appreciate, the above method takes two parameters: the cache file and time expiry, respectively, allowing us to specify a value expressed in seconds for setting the proper cache validness.

 

The method checks simultaneously if there is a cache file in the system and that the time expiry has not been reached. Accordingly, if the cache file exists and is valid, the contents from the file are returned. Otherwise, the method returns false. Certainly, you’ll agree with me that this is not rocket science.

 

Now, let’s take a look at the next method, “writeCache()”, which, not surprisingly writes contents to a specified cache file:

 

function writeCache($cacheFile,$content){

    $fp=fopen($cacheFile,’w’);

    fwrite($fp,$content);

    fclose($fp);

}

 

On the opposite side, this method accepts a cache file name, and the contents to be written on it as parameters. It naturally opens the file, writes the contents (overriding any previous values) and closes the file. The method is straightforward and easy to understand.

 

At this point, we’re armed with two methods to read and write cache files in an efficient manner. It’s time place them into the class puzzle.

 

{mospagebreak title=Putting the pieces together}

 

Now, things are getting more exciting. Once we’ve defined the corresponding caching methods, the class’ logic is really easy to understand. Take my word for it. Before doing any template file process, the handy class just checks whether there is a valid cache file for retrieving contents. If there is, they are grabbed from the cache file and assigned to the $output variable, avoiding any additional template processing. Otherwise, the class performs the regular template-parsing task, as defined in the original version. Isn’t it sweet and simple?

 

Indeed, the explanation I just gave would be rather useless without showing the code of the new, caching-improved class. So here’s the complete listing:

 

class templateParser {

    var $output;

    function templateParser($tags=array
(),$cacheFile=’default_cache.txt’,$expireTime=3600,
$templateFile=’default_template.htm’){

          if(!$this->output=$this->readCache
($cacheFile,$expireTime)){

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

          }

    }

    function parseTemplate($tags,$cacheFile){

          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);

               }

               $this->writeCache($cacheFile,$this-
>output);

          }

          else {

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

          }

    }

    function parseFile($file){

          ob_start();

          include($file);

          $content=ob_get_contents();

          ob_end_clean();

          return $content;

    }

    function writeCache($cacheFile,$content){

          $fp=fopen($cacheFile,’w’);

          fwrite($fp,$content);

          fclose($fp);

    }

    function readCache($cacheFile,$expireTime){

          if(file_exists($cacheFile)&&filemtime
($cacheFile)>(time()-$expireTime)){
               return file_get_contents($cacheFile);

          }

          return false;

    }

    function display(){

          return $this->output;

    }

}

 

From the above code, we can deduct that this new version is similar to the “cache-less” one. But let’s break down the code to understand what it does. First, we notice that the constructor takes four parameters respectively: the $tags array, the cache file, the time expiry and finally, the template file. For all of these arguments I’ve specified default values, in the following sequence: an empty $tag array, “default_cache.txt” for default cache file, a value of 3600 seconds (1 hour) for cache time expiry and finally “default_template.htm” for the template file.

 

Then, as stated previously, the constructor checks for the existence of a valid cache file, invoking the private “readCache()” method. If a valid cache is found, the cached contents are stored in the $output variable to be returned later from the class. Otherwise, if no valid cache exists (the file doesn’t exists or the time expiry has been reached), the constructor performs the regular template parsing process, as defined in the original version, calling the “parseTemplate()” method.

 

As you can see, here we’ve moved “parseTemplate()” inside the constructor, turning it into a private method (remember that originally it was defined as public), for being called only if no valid cache file is found. The reason for doing this should become clear, since the class on its own is now capable of determining where to look to retrieve page contents. Regarding the “parseTemplate()” method, the only addition resides in writing the contents of the finished page to the cache file, once the template file has been parsed.

 

Finally, the “display()” method remains identical to the previous version, this time returning either the contents from the generated page or directly from the cache file. Not too difficult, right?

 

At this point, we’ve made some minor changes to the original class, adding a couple of simple methods for implementing a basic cache mechanism, based on a time expiration policy. This might be a possible implementation of the class:

 

<?php

// include template parser class

require_once(‘template.php’);

// define class parameters

$tags=array(‘title’=>’Template System’,’header’=>’header.php’,’navbar’=>
‘navbar.php’,’leftcontent’=>
‘leftcontent.php’,’maincontent’=>’maincontent.php’,
‘rightcontent’=>
‘rightcontent.php’,’footer’=>’footer.php’);

// instantiate a new template parser object specifying a cache file valid for 2 hours

$tp=&new templateParser($tags,’cache.txt’,2*3600,’template.htm’);

// display finished page

echo $tp->display();

?>

 

Truly, the example is self-explanatory. First, as usual, we include the required class file. Second, we define the values of the $tags array for  processing, specifying page title, and the required files for page generation. I’ve chosen to pass file names, but I could have hard-coded HTML instead. Finally, we instantiate a new object from the class, assigning a cache file valid for two hours (2*3600), and the corresponding template file, in this case, “template.htm.” The last statement simply displays the page created, either from the parsed template file or directly from the cache file.

 

Good! We have a template parsing class that offers basic but effective caching capabilities. Let’s congratulate ourselves. If you ever thought that building such a class was an obscure and painful experience, you were mistaken!

 

Wrapping up

 

In this article series, I hope I’ve demonstrated that building a PHP class for parsing template files and caching generated output is not difficult at all. Also, I hope this encourages PHP programmers wishing to taste the power of Object Oriented Programming with real-world applications. Of course, we must keep in mind that PHP4 doesn’t offer the full features for doing so, but with a little bit of will power, the job can be done decently. If you really want to dig deeper in OOP, while keeping your background in PHP, give PHP5 a try. The new and powerful Object Model will amaze you. The challenge is out there. Just try it out.  

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

chat