PHPRO.ORG

This class provides an excellent method of templating and caching of you files. As it uses PHP within the template, it does away with the need for another, (pointless) layer. After all, PHP is an embedded language. Before we look at the code to display a template, a template file is needed. Here is a simple one to get started.


<?php
try    {
        
/*** include the class definition ***/
        
include "template.class.php";

        
/*** create a new instance ***/
        
$template = new template;

        
/*** dynamically add a variable ***/
        
$template->heading "PHPRO Templates";

        
/*** turn cache on or off ***/
        
$template->setCaching(true);

        
/*** set the template directory ***/
        
$template->setTemplateDir("templates");

        
/*** set the cache directory ***/
        
$template->setCacheDir("my_cache");

        
/*** set the cache lifetime in seconds ***/
        
$template->setCacheLifetime(3600);

        
/*** show the template ***/
        
$template->display("my_template.tpl.html"$id);

        
/*** clear the cache ***/
        
$template->clearCache();
        }
catch(
Exception $e)
        {
        echo 
$e->getMessage();
        }

?>

Of course, you sill need a template file to go along with this script, here is one we prepared earlier.

<html>
<body>
<h1><?php echo $heading; ?></h1>
<p>PHPRO Templating at work.</p>
</body>
</html>

Using this template the output source will look like this

<html>
<body>
<h1>PHPRO Templates</h1>
<p>PHPRO Templating at work.</p>
</body>
</html>

To further the template flexibilty, the use of the $id variable can be used. This could be a value from GET or POST or even the name of a page being called from somewhere. When the $id is passed to the template, a new cache file is created to reflect the changed state of the document. You could use file.php?id=6 or file.php?id=my_page or any id you like.

The Template Class

Copy this code to a file named template.class.php and include it in your script


<?php
/** 

vim: set expandtab tabstop=4 shiftwidth=4:

 * Template Class
 *
 * A simple template class for separating layout and php logic.
 * It still uses PHP inside of the templates, but the actual
 * program logic remains outside of the template.
 *
 * @author     Kevin Waterson <kevin@phpro.org>
 * @copyright  2007 PHPRO 
 * @version    CVS: $Id:$
 * @since      Class available since Release 0.0.1
 */

class Template{

/**
 * The variable property contains the variables
 * that can be used inside of the templates.
 *
 * @access private
 * @var array
 */
private $variables = array();

/**
 * The directory where the templates are stored
 *
 * @access private
 * @var string
 */
private $template_dir null;

/**
 * Turns caching on or off
 *
 * @access private
 * @var bool
 */
private $caching false;

/**
 * The directory where the cache files will be saved.
 *
 * @access private
 * @var string
 */
private $cache_dir 'cache';

/**
 * Lifetime of a cache file in seconds.
 * @access private
 * @var int
 */
private $cache_lifetime 300;


/**
 * Adds a variable that can be used by the templates.
 *
 * Adds a new array index to the variable property. This
 * new array index will be treated as a variable by the templates.
 *
 * @param string $name The variable name to use in the template
 *
 * @param string $value The content you assign to $name
 *
 * @access public
 *
 * @return void
 *
 * @see getVars, $variables
 *
 */
public function __set($name$value){
  
$this->variables[$name] = $value;
}

/**
 * @Returns names of all the added variables
 *
 * Returns a numeral array containing the names of all
 * added variables.
 *
 * @access public
 *
 * @return array
 *
 * @see addVar, $variables
 *
 */
public function getVars(){
 
$variables array_keys($this->variables);
 return !empty(
$variables) ? $variables false;
}

/**
 *
 * Outputs the final template output
 *
 * Fetches the final template output, and echoes it to the browser.
 *
 * @param string $file Filename (with path) to the template you want to output
 *
 * @param string $id The cache identification number/string of the template you want to fetch
 *
 * @access public
 *
 * @return void
 *
 * @see fetch
 *
 */
public function display($file$id null){
   echo 
$this->fetch($file$id);
}

/**
 * Fetch the final template output and returns it
 *
 * @param string $template_file Filename (with path) to the template you want to fetch
 *
 * @param string $id The cache identification number/string of the template you want to fetch
 *
 * @access private
 *
 * @return string Returns a string on success, FALSE on failure
 *
 * @see display
 *
 */
private function fetch($template_file$id null){

/*** if the template_dir property is set, add it to the filename ***/
 
if (!empty($this->template_dir))
    {
    
$template_file realpath($this->template_dir) . '/' $template_file;
    }
/*** get the cached file contents ***/
if ($this->caching == true && $this->isCached($template_file$id))
    {
    
$output $this->getCache($template_file$id);
    }
 else
    {
    
$output $this->getOutput($template_file);
    
/*** create the cache file ***/
    
if ($this->caching == true)
        {
        
$this->addCache($output$template_file$id);
        }
    }
 return isset(
$output) ? $output false;
}

/**
 *
 * Fetch the template output, and return it
 *
 * @param string $template_file Filename (with path) to the template to be processed
 *
 * @return string Returns a string on success, and FALSE on failure
 *
 * @access private
 *
 * @see fetch, display
 *
 */
private function getOutput($template_file){
 
/*** extract all the variables ***/
 
extract($this->variables);

 if (
file_exists($template_file))
    {
    
ob_start();
    include(
$template_file);
    
$output ob_get_contents();
    
ob_end_clean();
    }
 else
    {
    throw new 
Exception("The template file '$template_file' does not exist");
    }
return !empty(
$output) ? $output false;
}

/**
 *
 * Sets the template directory
 *
 * @param string $dir Path to the template dir you want to use
 *
 * @access public
 *
 * @return void
 *
 */
public function setTemplateDir($dir){
$template_dir realpath($dir);
if (
is_dir($template_dir))
    {
    
$this->template_dir $template_dir;
    }
else
    {
    throw new 
Exception("The template directory '$dir' does not exist");
    }
}
    
/**
 *
 * Sets the cache directory
 *
 * @param string $dir Path to the cache dir you want to use
 *
 * @access public
 *
 * @return void
 *
 * @see setCacheLifetime
 *
 */
function setCacheDir($dir){
 
$cacheDir realpath($dir);
        
 if (
is_dir($cacheDir) && is_writable($cacheDir))
    {
    
$this->cache_dir $cacheDir;
    }
else
    {
    throw new 
Exception("The cache directory '$cacheDir' either does not exist, or is not writable");
    }
}

/**
 * Sets how long the cache files should survive
 *
 * @param INT $seconds Number of seconds the cache should survive
 *
 * @access public
 *
 * @return void
 *
 * @see setCacheDir, isCached, setCaching
 *
 */
public function setCacheLifetime($seconds=0){
 
$this->cache_lifetime is_numeric($seconds) ? $seconds 0;
}

/**
 * Turn caching on or off
 *
 * @param bool $state Set TRUE turns caching on, FALSE turns caching off
 *
 * @access public
 *
 * @return void
 *
 * @see setCacheLifetime, isCached, setCacheDir
 *
 */
public function setCaching($state){
    if (
is_bool($state))
    {
    
$this->caching $state;
    }
}

/**
 * Checks if the template in $template is cached
 *
 * @param string $file Filename of the template
 *
 * @param string $id The cache identification number/string of the template you want to fetch
 *
 * @access public
 *
 * @return bool
 *
 * @see setCacheLifetime, setCacheDir, setCaching
 *
 */
public function isCached($file$id null){
$cacheId  $id md5($id basename($file)) : md5(basename($file));
$filename $this->cache_dir '/' $cacheId '/' basename($file);
if (
is_file($filename))
    {
    
clearstatcache();
    if (
filemtime($filename) > (time() - $this->cache_lifetime))
        {
        
$isCached true;
        }
    }
 return isset(
$isCached) ? true false;
}

/**
 * Makes a cache file. Internal method
 *
 * @param string $content The template output that will be saved in cache
 *
 * @param string $file The filename of the template that is being cached
 *
 * @param string $id The cache identification number/string of the template you want to fetch
 *
 * @access private
 *
 * @return void
 *
 * @see getCache, clearCache
 */
private function addCache($content$file$id null){
/*** create the cache id ***/
$cacheId   $id md5($id basename($file)) : md5(basename($file));
/*** create the cache filename ***/
$filename  $this->cache_dir '/' $cacheId '/' basename($file);
/*** create the directory name for the cache file ***/
$directory $this->cache_dir '/' $cacheId;
/*** create the cache directory ***/
@mkdir($directory0775);
/*** write to the cache ***/
if(file_put_contents($filename$content) == FALSE)
    {
    throw new 
Exception("Unable to write to cache");
    }
}


/**
 * Returns the content of a cached file
 *
 * @param string $file The filename of the template you want to fetch
 *
 * @param string $id The cache identification number/string of the template you want to fetch
 *
 * @access private
 *
 * @return string Cached content on success, FALSE on failure
 *
 * @see addCache, clearCache
 *
 */
private function getCache($file$id null){
$cacheId  $id md5($id basename($file)) : md5(basename($file));
$filename $this->cache_dir '/' $cacheId '/' basename($file);
/*** read the cache file into a variable ***/
$content=file_get_contents($filename);
return isset(
$content) ? $content false;
}

/**

 * Deletes all of the stored cache files
 *
 * @access public
 *
 * @return void
 *
 * @see addCache, getCache
 *
 */
public function clearCache(){
  
$cacheDir realpath($this->cache_dir);
  
$this->delDir($cacheDir);
}

/**
 * Remove files and folders recursively.
 * WARNING: It does not care what directory $dir is.
 *
 * @param string $dir directory to remove files and folders from
 *
 * @access private
 *
 * @return void
 *
 * @see clearCache
 *
 */

private function delDir($dir) {

/*** perhaps a recursiveDirectoryIteratory here ***/
$deleteDir realpath($dir);

if (
$handle opendir($deleteDir))
    {
    while (
false !== ($file readdir($handle)))
        {
        if (
$file != '.' && $file != '..')
            {
            if (
is_dir($deleteDir '/' $file))
                {
                
$this->delDir($deleteDir '/' $file);
                if(
is_writable($deleteDir '/' $file))
                    {
                    
rmdir($deleteDir '/' $file);
                    }
                else
                    {
                    throw new 
Exception("Unable to remove Directory");
                    }
                }
            elseif(
is_file($deleteDir '/' $file))
                {
                if(
is_writable($deleteDir '/' $file))
                    {
                    
unlink($deleteDir '/' $file);
                    }
                else
                    {
                    throw new 
Exception("Unable to unlink $deleteDir".'/'."$file");
                    }
                }
            }
        }
    
closedir($handle);
    }
}

/*** end of class ***/

?>