Home arrow PHP arrow Page 4 - Using Recursive Methods in Object-based PHP Applications

A final example of recursion: creating a template processor class - PHP

Welcome to the second tutorial of the series “Recursion in PHP.” Comprised of three parts, this series introduces the fundamentals of recursion in PHP, including the definition and use of recursive functions in procedural PHP scripts, as well as the creation of recursive methods in object-oriented Web applications.

TABLE OF CONTENTS:
  1. Using Recursive Methods in Object-based PHP Applications
  2. Applying recursion in object-oriented programming: creating object-based web page elements
  3. Defining a recursive method: creating a web page generator class
  4. A final example of recursion: creating a template processor class
By: Alejandro Gervasio
Rating: starstarstarstarstar / 7
May 08, 2006

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

The final hands-on example of this article consists of developing a template processor class in PHP 5, which among other things has the capacity to recursively parse template files by replacing nested placeholders with data coming from different sources.

To begin with, this is how a typical template file looks, before being parsed by the respective class:

<!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="mystyles.css" />
</head>
<body>
<div id="header">{header}</div>
<div id="navbar">{navbar {subnavigationbar1}{ subnavigationbar2}}</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 file that I just created has some nested placeholders that require the use of a recursive method to replace them with actual data. That’s precisely the function of the “TemplateProcessor” class that I’ve listed below:

class TemplateProcessor {
    private $output='';// set default value for general class
output
    private $rowTag='p';// set default value for database row tag
    private $tags=array();// set default value for tags
    private $templateFile='default_template.htm';// set default
value for template file
    private $cacheFile='default_cache.txt';// set default value
for cache file
    private $expiry=3600;// set default value for cache
expiration
    public function __construct($tags=array()){
        if(count($tags)<1){
            throw new Exception('Invalid number of tags');
        }
        if($this->isCacheValid()){
            // read data from cache file
            $this->output=$this->readCache();
        }
        else{
            $this->tags=$tags;
            // read template file
            $this->output=file_get_contents($this->templateFile);
            // process template file
            $this->processTemplate($this->tags);
            // clean up empty tags
            $this->output=preg_replace("/{w}|}/",'',$this-
>output);
            // write compressed data to cache file
            $this->writeCache();
        }
        // send gzip encoding http header
        $this->sendEncodingHeader();
    }
    // check cache validity
    private function isCacheValid(){
        // determine if cache file is valid or not
        if(file_exists($this->cacheFile)&&filemtime($this-
>cacheFile)>(time()-$this->expiry)){
            return true;
        }
        return false;
    }
    // process template file
    private function processTemplate($tags){
        foreach($tags as $tag=>$data){
            // if data is array, traverse recursive array of tags
            if(is_array($data)){
                $this->output=preg_replace("/{$tag/",'',$this-
>output);
                $this->processTemplate($data);
            }
            // if data is a file, fetch processed file
            elseif(file_exists($data)){
                $data=$this->processFile($data);
            }
            // if data is a MySQL result set, obtain a formatted
list of database rows
            elseif(@get_resource_type($data)=='mysql result'){
                $rows='';
                while($row=mysql_fetch_row($data)){
                    $cols='';
                    foreach($row as $col){
                        $cols.='&nbsp;'.$col.'&nbsp;';
                    }
                    $rows.='<'.$this-
>rowTag.'>'.$cols.'</'.$this->rowTag.'>';
                }
                $data=$rows;
            }
            // if data contains the '[code]' elimiter, parse data
as PHP code
            elseif(substr($data,0,6)=='[code]'){
                $data=eval(substr($data,6));
            }
            $this->output=str_replace('{'.$tag.'}',$data,$this-
>output);
        }
    }
    // process input file
    private function processFile($file){
          ob_start();
          include($file);
          $contents=ob_get_contents();
          ob_end_clean();
          return $contents;
    }
    // write compressed data to cache file
    private function writeCache(){
        if(!$fp=fopen($this->cacheFile,'w')){
            throw new Exception('Error writing data to cache
file');
        }
        fwrite($fp,$this->getCompressedHTML());
        fclose($fp);
    }
    // read compressed data from cache file
    private function readCache(){
        if(!$cacheContents=file_get_contents($this->cacheFile)){
            throw new Exception('Error reading data from cache
file');
        }
        return $cacheContents;
    }
    // return overall output
    public function getHTML(){
          return $this->output;
    }
    // return compressed output
    private function getCompressedHTML(){
        // check if browser supports gzip encoding
        if(strstr($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip')){
            // start output buffer
            ob_start();
            // echo page contents to output buffer
            echo $this->output;
            // crunch (X)HTML content & compress it with gzip
            $this->output=gzencode(preg_replace("/
(rn|n)/","",ob_get_contents()),9);
            // clean up output buffer
            ob_end_clean();
            // return compressed (X)HTML content
            return $this->output;
        }
        return false;
    }
    // send gzip encoding http header
    public function sendEncodingHeader(){
        header('Content-Encoding: gzip');
    }
}

As you can see, aside from using recursion for parsing template files, the above class is also capable of caching and compressing the (X)HTML output of parsed web pages. This makes it a quite useful piece of code, particularly if you work very often with template systems. But now, turn your attention to the following class method:

private function processTemplate($tags){
    foreach($tags as $tag=>$data){
        // if data is array, traverse recursive array of tags
        if(is_array($data)){
            $this->output=preg_replace("/{$tag/",'',$this-
>output);
            $this->processTemplate($data);
        }
        // if data is a file, fetch processed file
        elseif(file_exists($data)){
            $data=$this->processFile($data);
        }
        // if data is a MySQL result set, obtain a formatted list
of database rows
        elseif(@get_resource_type($data)=='mysql result'){
            $rows='';
            while($row=mysql_fetch_row($data)){
                $cols='';
                foreach($row as $col){
                    $cols.='&nbsp;'.$col.'&nbsp;';
                }
                $rows.='<'.$this->rowTag.'>'.$cols.'</'.$this-
>rowTag.'>';
            }
            $data=$rows;
        }
        // if data contains the '[code]' elimiter, parse data as
PHP code
        elseif(substr($data,0,6)=='[code]'){
            $data=eval(substr($data,6));
        }
        $this->output=str_replace('{'.$tag.'}',$data,$this-
>output);
    }
}

Regarding the above method, you can see that it calls itself (in other words, uses recursion), in order to replace all the nested placeholders that eventually might be included inside the corresponding template file. Additionally, the method is capable of processing MySQL result sets, parsing dynamic PHP files and evaluating input strings as PHP code.

Now, pay attention to the example below, which illustrates how to use the recursive capabilities of the “TemplateProcessor” class:

try{
    // define input tags for template processor class
    $tags=array('title'=>'PHP 5 Template Processor','header'=>'This is the header section','navbar'=>array
('subnavigationbar1'=>'subnavbar1.php','navigationbar2'=>
'subnavbar2.php'),'
leftcontent'=>'leftcontent','maincontent'=>'This is the main
section','rightcontent'=>'right content','footer'=>'This is the
footer section');
    // instantiate a new template processor object
    $tpl=new TemplateProcessor($tags);
    // display compressed page
    echo $tpl->getHTML();
}
catch(Exception $e){
    echo $e->getMessage();
    exit();
}

In this case, I defined a recursive array of input tags, to be passed directly to the constructor of the “TemplateProcessor” class. Since the template file you saw previously contains some nested placeholders, I specified the elements corresponding to the "navbar" key to also be arrays (hence the concept of recursive array), thus the “subnavigationbar1” and “subnavigationbar2” placeholders will be replaced with the parsed output of the “subnavbar1.php” and “subnavbar2.php” PHP files respectively.

Considering that these dynamic files might be created as follows:

<?php
// definition for 'subnavbar1.php' file
echo 'This navigation section was generated at '.date('H:i:s');
?>

<?php
// definition for 'subnavbar2.php' file
echo 'This navigation section was generated at '.date('H:i:s');
?>

The output of the previous script would look like this:

This is the header section

This navigation section was generated at 12:45:03
This navigation section was generated at 12:45:03
left content
This is the main section

right content

This is the footer section

As you can see, the recursive “processTemplate” method has iterated over the array of input tags and replaced all the placeholders with the respective data, which demonstrates the benefits of having at your disposal a method that uses recursion for parsing template files.

Final thoughts

In this tutorial you learned how to define recursive methods in PHP when developing object-oriented applications. I showed you two specific examples: a web page generator class and a template processor. In both cases you saw how recursion can be quite useful for performing a deep scan on recursive arrays.

However, are you thinking this is it about recursion? You’re wrong. Over the last part of the series I’ll explain how to develop a MySQL-driven discussion forum, which uses recursive methods for fetching user messages from a database table. See you in the next part!



 
 
>>> More PHP Articles          >>> More By Alejandro Gervasio
 

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort
   

PHP ARTICLES

- Hackers Compromise PHP Sites to Launch Attac...
- Red Hat, Zend Form OpenShift PaaS Alliance
- PHP IDE News
- BCD, Zend Extend PHP Partnership
- PHP FAQ Highlight
- PHP Creator Didn't Set Out to Create a Langu...
- PHP Trends Revealed in Zend Study
- PHP: Best Methods for Running Scheduled Jobs
- PHP Array Functions: array_change_key_case
- PHP array_combine Function
- PHP array_chunk Function
- PHP Closures as View Helpers: Lazy-Loading F...
- Using PHP Closures as View Helpers
- PHP File and Operating System Program Execut...
- PHP: Effects of Wrapping Code in Class Const...

Developer Shed Affiliates

 


Dev Shed Tutorial Topics: