#native_company# #native_desc#
#native_cta#

Caching Dynamic Pages Using PHPLIB Templates

By Tom Anderson
on November 2, 2000

This may be better as a column

Extending PHPLIB Template class to cache pages

I’ve found the PHPLIB template class to be very useful for seperating code from logic.
After putting a lot of thought into how I can allow my current site to cache dynamic pages, I decided to extend the Template class to do it for me.

For use of the template class, please see the article https://phpbuilder.com/columns/david20000512.php3

On my pages, I call the pparse(“parseIntoVar”, “parseVar”) function at the very end of my scripts. This function parses the template variable parseVar into parseIntoVar and print()’s the result. By caching my dynamic page at this point, I will only need to change my initialization of my template variable and add one call to a new function set_cache() on any page I want cached.

Four new instance variables are needed. They should all be concidered private variables.
They are:


/* for caching pages */
var $refreshCache = false;
var $cacheName = "";
var $cachePath = "/tmp/";

/* default = 15 min */
var $cacheTimeout = 900;

The $cachePath and $cacheTimeout variables are used for setting defaults. The $cacheTimeout specifies, in seconds, how often a cached page should be refreshed. The $cachePath is the default path for saving cached pages and should be terminated with a / to avoid a sanity check each time set_cache() is called.

$cacheName is used internally and should not be set by hand. $refreshCache is a flag set in set_cache() and checked in pparse() to see if the page should be saved as a cache object.

The new pparse() function stores the returned value from parse() long enough to write it to a file (cache object) the prints it.


  /**
   * Override the ancestor function so we can save the parse result
   */
  function pparse($target, $handle, $append = false) {
    $str = $this->parse($target, $handle, $append);

    // For caching
    if ($this->refreshCache) error_log($str, 3, $this->cachePath . $this->cacheName);

    print $str;
    return false;
  }

Finally the new function, set_cache():


  /**
   * set_cache ( array or string )
   *
   * Parameter:  array or cache file name
   *
   * set_cache(array("name" => "cache file name", 
   *       "path" => "cache file path", 
   *       "timeout" => seconds before refresh));
   *
   * or set_cache("cache file name");
   */

  function set_cache($cacheParms) {
    if (is_array($cacheParms)) {
      // Set name and the path to the cached file
      $this->cacheName = $cacheParms["name"];
      if ($cacheParms["path"]) $this->cachePath = $cacheParms["path"];
      // Set timeout to 15 min. if not specified
      if ($cacheParms["timeout"]) $this->cacheTimeout = $cacheParms["timeout"]; 
    } else {
      $this->cacheName = $cacheParms;
    }
	
    if (!$this->cacheName) return false;
    
	// Get the last modified time for the file
	## lstat[9] = FILE_LASTUPDATE
    $fileProps = lstat($this->cachePath . $this->cacheName);

	// Set the refresh cache to true if our delta is too big
    if ($this -> refreshCache = ((time() - $fileProps[9]) > $this->cacheTimeout)) {
      // Delete the cached file
      @unlink($this->cachePath . $this->cacheName);
    } else {
      // Use the cached file
      include ($this->cachePath . $this->cacheName);
      die();
    }
  }

The set_cache() function, in the tradition of the Template object,can accept an array or a string. If you call set_cache($string), the $string will be the file name for the cached page. If you call set_cache with an array, the array must contain at least

"name" => $string

and can contain either of:


"path" => "cache file path"
"timeout" => seconds before refresh

“path” is for overriding $cachePath and “timeout” is for overriding $cacheTimeout.

Finally, a simple script with before and after cache implementation:
Before:


<?
include ("template.inc");

$t = new template("/templates");
$t -> set_file("main", "index.ihtml");
$t -> pparse("output", "main");
?>

And After:


<?
include ("template.inc");
include ("c_template.inc");

$t = new c_template("/templates");
$t -> set_cache(array("name" => "cachetest", "timeout" => 10, "path" => "/tmp/"));
$t -> set_file("main", "index.ihtml");
$t -> pparse("output", "main");
?>

Finally, here is the assembeled class:


class c_Template extends Template {

  /* for caching pages */
  var $refreshCache = false;
  var $cacheName = "";
  var $cachePath = "/tmp/";

  /* default = 15 min */
  var $cacheTimeout = 900;

  /**
   * Override the ancestor function so we can save the parse result
   */
  function pparse($target, $handle, $append = false) {
    $str = $this->parse($target, $handle, $append);

    // For caching
    if ($this->refreshCache) error_log($str, 3, $this->cachePath . $this->cacheName);

    print $str;
    return false;
  }

  /**
   * set_cache ( array or string )
   *
   * Parameter:  array or cache file name
   *
   * set_cache(array("name" => "cache file name", 
   *       "path" => "cache file path", 
   *       "timeout" => seconds before refresh));
   *
   * or set_cache("cache file name");
   */

  function set_cache($cacheParms) {
    if (is_array($cacheParms)) {
      // Set name and the path to the cached file
      $this->cacheName = $cacheParms["name"];
      if ($cacheParms["path"]) $this->cachePath = $cacheParms["path"];
      // Set timeout to 15 min. if not specified
      if ($cacheParms["timeout"]) $this->cacheTimeout = $cacheParms["timeout"]; 
    } else {
      $this->cacheName = $cacheParms;
    }
	
    if (!$this->cacheName) return false;
    
	// Get the last modified time for the file
	## lstat[9] = FILE_LASTUPDATE
    $fileProps = lstat($this->cachePath . $this->cacheName);

	// Set the refresh cache to true if our delta is too big
    if ($this -> refreshCache = ((time() - $fileProps[9]) > $this->cacheTimeout)) {
      // Delete the cached file
      @unlink($this->cachePath . $this->cacheName);
    } else {
      // Use the cache
      include ($this->cachePath . $this->cacheName);
      die();
    }
  }
}

This has worked wonderfully for me, and I hope it others can find it just as useful. – Tom