Location: PHPKode > scripts > Generalized CachedTemplate class > CachedTemplate.txt
CachedTemplate: A generalized template caching class

Document: http://www.scripps.edu/~jesusmc/cachedtpl/CachedTemplate.html

Version: 1.4, Copyright © 2000-2001 Jesus M. Castagnetto
(hide@address.com), License: GPL


New: (1.4) Added support for reading into a variable the contents of a
cached file, which can be used for partial caching of page contents. An
example (partial_caching.php) is included to illustrate the use of the

New: (1.3, unreleased) Added a locking mechanism to avoid race conditions
while updating a cached file

(1.2) Added a general way of using user's variables for
writing/reading/validating cached information. Added support for external
data validation.

NOTE: For questions about these classes contact me at:
hide@address.com, any e-mail related to these classes going to any of
my other e-mail addresses will go unanswered.


Table of contents

   * Introduction
   * Class TemplateAdaptor
   * Class CachedTemplate
   * History
   * Known Bugs
   * License
   * Download
        o everything
        o documentation
   * Acknowledgements


Sometime ago I made an extension to CDI's FastTemplate class (the
CachedFastTemplate class), with the idea of creating a file caching
mechanism that saves parsed documents to avoid processing of templates each
time it is called. This simple file caching would reduce the load on the PHP
interpreter by several orders of magnitude if you have a complex template.
Returning a pre-parsed file is always faster than parsing it anew.

But, as always happens more template classes were created each one with
interesting characteristics of their own. So, I decided to reuse the code
and kludge together a more general way of extending those template classes
by using an intermediate TemplateAdapter class, which extends the
appropriate template class, and is itself extended by the CachedTemplate

Below is an (ugly) ASCII diagram, showing the way the template adaptor and
the caching class work with an original template class.

    +----------+      +----------+      +----------+
    | original |      | template |      |  cached  |
    | template +----->+  adaptor +----->+ template +=====> object instance
    |  class   |      |   class  |      |  class   |
    +----------+      +-+--------+      +----------+
                        +-- init()
                        +-- getTemplatesList()
                        +-- getParsedDoc()
                        +-- [accessory methods]
                        +-- [overriding methods]

The Template Adaptor class must implement the methods needed by the Cached
Template class: init, getTemplatesList and getParsedDoc. To do this the
Template Adaptor may need to generate accessory methods or override one or
more of the original methods of the parent template class. (see
documentation below for more information)

This package contains adapter classes and examples for 5 different template

   * CDI's FastTemplate class
   * Barnabás Debrenceni's XTemplate class
   * Stefan Bocskai's QuickTemplate class
   * Herman Veluwenkamp's HTML Template class
   * Richard Heyes's Template class
   * PHPLIB's Template class

Basically I picked all the templates classes from PHPClasses (as of June
2000), and included CDI's and PHPLIB's too.


Class TemplateAdaptor


An class that extends the appropriate template class, and implements the
methods needed by the CachedTemplate class: init(), getTemplatesList() and
getParsedDoc(). It may need to override methods from the parent class, for
examples on how to do this, look at the classes in the files:
class.HTMLTemplateAdaptor.php, class.RHTemplateAdaptor.php and

You can easily construct your own TemplateAdaptor class by using one of the
included ones as a starting point. Basically, you need to know how to call
the constructor (if any) of the template class being extended, how to list
the names of the template files, and how to get the parsed document as a
string (so you can assign that to a variable). For example, after reading
the code and documentation for PHPLIB's Template class, it was simple of
create an adaptor class for it based on the existing one for the
FastTemplate class.

Example of definition:

Below is the adaptor class for FastTemplate (class.FastTemplateAdaptor.php):


 * Class TemplateAdaptor (FastTemplate version)
 * by Jesus M. Castagnetto (hide@address.com)
 * (c) 2000. Version 1.0
 * Description:
 * This class extends CDI's (hide@address.com) FastTemplate class,
 * implementing methods and attributes needed for the CachedTemplate class.
 * The adaptor class needs to implement the getTemplatesList() method that
 * returns an array with the names of the templates loaded, and the init()
 * method used to initialize the constructor of the parent template class
 * Changes:
 * 2000/06/10 - Initial release.

class TemplateAdaptor extends FastTemplate {

        var $TEMPLATES = array();

         * This method is used to initialize the parent class

        function init($pathToTemplates="") {

         * method to return the list of template names

        function getTemplatesList() {
                if (count($this->FILELIST) > 0 ) {
                        while (list($tag, $fn) = each($this->FILELIST))
                                $this->TEMPLATES[] = $this->ROOT.$fn;
                return $this->TEMPLATES;

         * method to return the parsed document

        function getParsedDoc($sect="") {
                return $this->fetch($sect);

} // end of class definition

Public Methods:


     void init ([mixed $par1 [, mixed $par2, ...]])
     This method is a wrapper to initialize variables and call the
     constructor method (if any) of the parent template class. As such is
     very dependent on template class being extended, so the number and type
     of parameters passed is not always the same.


     array getTemplatesList (void)
     This method returns an array with the names of the templates (if any)
     that the main template script is using.


     string getParsedDoc (void)
     This method returns a string containing the parsed template file, in a
     form suitable to output back to the browser or save into a file.


Class CachedTemplate


A generalized template caching class, extends the appropriate
TemplateAdaptor class. In a few words, it allows the caching of the output
from parsing a template into a file, so the next time the script is
requested it can just send back that file and avoid the processing overhead
of re-parsing the template.

This class can be used also when caching blocks of content from a page (or
pages), by allowing the selective reading of cached information into a
variable (see: get_from_cache()).

To avoid problems when several processes attempt to read or update a cached
file (race condition), a locking mechanism has been implemented, which when
active, is used automatically by all the reading/writing methods of the
class (see: read_from_cache(), get_from_cache() and write_to_cache())

Locking explained

The logic used is the following:

  1. when reading a cache file
        o if a lock file is found, wait for $WAITLOCK microseconds and check
        o if after trying $MAXWAITLOCKCYCLES times the lock is still there
          this may be due to an error somewhere, emit an error and return
          false (read_from_cache()) or the empty string (get_from_cache())
        o when the lock goes away (if there was one) read the cached info
  2. when writing a cache file
        o if there is a lock, some other instance of the same script must be
          updating the info, so do nothing a return

There are also methods that allow to turn on/off lock file usage, which
should speed things very little in general use, and which could be useful
when regenerating pages using a cron job.


Modified from the sample file: fasttemplate_example.php

        require "./class.FastTemplate.php3";
        require "./class.FastTemplateAdaptor.php";
        require "./class.CachedTemplate.php";

        // Instantiate a CachedTemplate object with a
        // lifetime of maximum 6 hours
        $ftpl = new CachedTemplate("./mycache",6,"hours");

        // Initialize the parent template class

        // Define which templates will be used
        // this needs to be done here so the validity of the
        // cached file can be correctly ascertained
                        1main    => "main.tpl",
                        table   => "table.tpl",
                        row     => "row.tpl"

        // Check if we can send the cached file
        if ($ftpl->valid_cache_file()) {
                $end = $ftpl->utime();
                $runtime = ($end - $start) * 1000;
                echo "Completed in $runtime milliseconds<BR>\n";

        // Otherwise process the page



This class has the following attributes (member properties):

     Directory to save cached files, default "./cache"
     Length of caching, default: 30 units
     Time unit for caching, default: "day"
     An associative array of valid time units and the corresponding factors
     (divisors) to use when calculating time differences, defined as: array
     ("sec"=>1, "min"=>60, "hour"=>3600, "day"=>86400)
     a flag variable to indicate that the cache validation, reading and
     writing methods will need to also use the contents of the $VARSVAL
     Valid values:
        o "in_name" - appends the output of rawurlencode on the $VARSVAL
          variable to the names of cached and control files
        o "store" - store the contents of $VARSVAL into a *.vars file, and
          use that file for cache validation
     If the flag $USEVARS is set, then this variable will contain the
     variable name and value pairs encoded as an URL query string. If the
     method use_get is used, this variable will contain the global variable
     If the flag $USELOCK is set, then a lock will be used when reading from
     or writing to a cache file. Default true. Use the methods
     set_use_lock() and unset_use_lock() to change the status of this
     How many microseconds (default 100,000 microseconds = 100 milliseconds)
     to wait until trying to read from, or write to, a locked cache file.
     How many times to attempt reading from a locked cache file. Default is
     20 times. This is used to limit the number of retries before returning
     an error condition.

Public Methods:


     void set_cache_dir (string $dir)
     Sets the directory to be used for caching parsed documents

             // Set the caching directory to "../mycachedir"
             $obj = new CachedTemplate();


     void set_cache_length (int $length)
     Sets the maximum length (in time units) to keep cached documents

             // Set the caching length to 12 hours


     void set_time_unit (string $tunit)
     Sets the time unit to be used for calculating the lifetime of a cached
     document. Valid units are: "sec", "min", "hour", "day"; if and invalid
     unit is used, it defaults to "day".


     void use_get ([string $how])
     Calls the method use_vars to use the contents of the $QUERY_STRING
     global variable for cache writing, reading and validation. The default
     value of the the parameter is "in_name", i.e. use the $QUERY_STRING
     when generating a unique name for the cache file. See the examples in
     the files get_example1.php and get_example2.php for more information.
     Note: This method has to be used before the validation methods.
     Assuming that the script mypage.php was called using the URL:
     http://www.mysite.com/path/to/mypage.php?somevar=simple, we can use the
     query string as part of the name of the cached file, so if we pass
     other values, e.g. somevar=notsimple, a cached file will be created for
     each instance.

             $tpl = new CachedTemplate();

             // use the query string for generating the names of the cache files
             // which is the default behavior, and equivalent to using
             // $tpl->use_get("in_name");

     In case you prefer not to append the GET query string to the name of
     the cached file, then you can store it for comparison purposes:



     void use_vars ([mixed $vars, string $how])
     Sets the variable flag $USEVARS and sets the appropriate value for the
     $VARSVAL variable. This method accepts as a first parameter either the
     string "QUERY_STRING" (when using the variables passed in a GET query
     string), or an array of variable names and values to be used in the
     writing, reading and validation of the cached files. The default values
     for the parameters are "QUERY_STRING" and "in_name" respectively. Valid
     values for $how are: "in_name" (default) and "store".
     Note: This method has to be used before the validation methods.
     We will want to use some important values to generate unique cache

             $tpl = new CachedTemplate();

             $important_vars = array(
                             "theme" => "simple",
                             "fgcolor" => "black",
                             "bgcolor" => "white"

             $tpl->use_vars($important_vars, "in_name");

     If you are using PHP4, you can use the compact function to generate the
     array needed. In the example below, we also decide to store the
     variables into a *.vars file, which will be used during validation:

             // assuming $theme, $fgcolor and $bgcolor have been defined
             $important_vars = compact($theme, $fgcolor, $bgcolor);

             $tpl->use_vars($important_vars, "store");


     void write_to_cache (string $data [, string $datacheck, string
     Writes the parsed data to a cache file, and generates an associated
     control file containing a creation timestamp, the length of time to
     cache the information, and the unit of time used. If the second
     parameter is present, it will included in the control file to be used
     for external data validation (see: is_data_valid). If the third
     parameter is present, this will be the name used for the cache and
     control files, otherwise the method will use the filename stored in
     $PHP_SELF, replacing all "/" characters with "_".

             // write parsed document to files "coolfile.parsed.cntrl"
             // and "coolfile.parsed.cache", using today's timestamp
             // as data validation parameter
             $data = $obj->getParsedDoc();
             $obj->write_to_cache($data, time(), "coolfile.parsed");


     boolean read_from_cache ([string $filename])
     Reads the cached file from the cache directory and sends it to the
     standard output, returns true on success, false otherwise. If a
     parameter is passed, it will be used as the name for the cached file.

             // read the current document from cache


     string get_from_cache ([string $filename])
     Reads the cached file from the cache directory and returns it as a
     string if succesful, an empty string otherwise. If a parameter is
     passed, it will be used as the name for the cached file.

             // get a cached block in this document
             $header = $obj->get_from_cache("header_".$PHP_SELF);


     boolean is_cached (string $filename)
     Returns true if the file $filename is cached, false otherwise.

             if ($obj->is_cached("coolfile.parsed")) {
                     // do something


     boolean valid_cache_file ([string $filename])
     Returns true if it finds a valid cache file, false otherwise. If the
     parameter $filename is omitted, then the variable $PHP_SELF with the
     "/"s replaced by "_"s will be used.

     The algorithm to decide whether a cached file is valid is:
       1. If a cached file exists (uses method is_cached) go to the next
          step, otherwise return false and exit method.
       2. Is the variable $USEVARS set?
             + No - then go to the next step
             + Yes - then check how do we have to use it, if the value of
               $USEVARS is:
                  + "in_name" - apply URL encoding to $VARSVAL and append it
                    to the filename for the cache and control files, then go
                    to the next step (this is done by calling the method
                  + "store" - read the stored $VARSVAL from the *.vars file
                    and compare with the current one, return false if they
                    are different, otherwise go to the next step
       3. Get the creation timestamp, time length and time unit from the
          control file. If it is an invalid time unit return false,
          otherwise go to the next step.
       4. Get the list (if any) of templates included in the document. Loop
          through the list and return false if any of the templates does not
          exist, or has been changed since the cached file was created (use
          the the creation timestamp obtained in the previous step),
          otherwise go to the next step.
       5. Finally, compare the creation timestamp with the current one, and
          determine if the cached file content have expired (return false)
          or not (return true).

             if ($obj->valid_cache_file()) {
                     echo $obj->read_from_cache();
             } else {
                     // parse the document and save into cache


     boolean is_data_valid (string $datacheck[, string $type, string
     Used to determine if the data from an external source has changed since
     the cache file was last created. It expects the first parameter to be
     either a unix timestamp or an md5 hash of the data. The second
     parameter should be either "timestamp" or "md5", it defaults to
     "timestamp". The third parameter (if present), should be the name used
     to generate the cached files.

     The idea behind using this method is, to allow for cached data
     regeneration when the data contained there changes in the original
     source, i.e. if you are pulling the body of a page from a database, and
     the information in the database changes, you can use the timestamp of
     the changed row to decide if the page needs to be reparsed. Similar
     approach can be done using an md5 hash of the data, which should also
     detect changes in it, this is for cases in which a timestamp is not
     In the following example, we extract information from a database of
     news articles, so we need to check if the cached information is up to
     date, we use the field ts which contains a MySQL timestamp, and we ask
     for the corresponding unix timestamp:

             $link = mysql_pconnect();
             $query = "select UNIX_TIMESTAMP(ts) from news where topic='science' ";
             $query .= "and subtopic='compchem' order by ts DESC limit 1";
             $res = mysql_query($query, $link);
             list($datats) = mysql_fetch_row($res);

             $tpl = new CachedTemplate();
             if ($tpl->valid_cache_file() && $tpl->is_data_valid($datats, "timestamp")) {
                     // cached info valid, send it to the user
             } else {
                     // parse the document

     A similar approach can be done with data coming from files, etc. See
     also the files datavalid_example1.php and datavalid_example2.php.


     void set_use_lock (void)
     Activates the use of locking while reading/writing cache files

             // use locking while processing cache files


     void unset_use_lock (void)
     Deactivates the use of locking while reading/writing cache files

             // do not use locking while processing cache files


     boolean is_locked (string 4filename)
     Returns true if file lock exists, false otherwise

             if ($obj->is_locked("mycachefilename")) {
                     // then do something;
             } else {
                     // proceed as always


     boolean mk_locked (string 4filename)
     Creates a lock file if one does not already exists, returning true if
     succesful, false otherwise

             if ($obj->mk_lock("mycachefilename"))
                     // then we can manipulate the cache file
             } else {
                     // other process is locking the cache file, we need to wait


     boolean rm_locked (string 4filename)
     Removes a lock file if one already exists, returning true if succesful,
     false otherwise

             // clean old lock files
             if ($obj->is_locked("mycachefilename"))


     int utime (void)
     Returns timestamp including microseconds. Shamelessly *borrowed* from
     CDI's FastTemplate class

             $start = utime();
             // after some code
             $end = utime();
             echo "Processing took: ".($end - $start) * 1000." milliseconds\n";

Private Methods:


     _gen_var_val (array $vars)
     Converts an associative array of $vars[name] = value into a url query
     string of the form: name1=val1&name2=val2&...


     string _gen_filename (void)
     Generates a unique filename based on the name of the file being cached.
     First it replaces all slashes (/) by underlines (_), and then if the
     $USEVARS variable is set to "in_name", appends the rawurlencode'd
     contents of $VARSVAL to the filename.


     boolean _key_in_array (string $key, array $arr)
     Returns true if the key is in the array, false otherwise.


     boolean _is_valid_time_unit (string $timeunit)
     Returns true if a the parameter passed is a valid time unit, false


     int _mktimestamp (void)
     Returns a timestamp in seconds. Wrapper for time().


     int _diff_time (int $end, int $start[, string $timeunit])
     Returns the difference between the $end and $start timestamps, in the
     units indicated by $timeunit. If the third parameter is omitted, it
     defaults to "day".

             // return the number of whole days between the 2 timestamps
             echo $obj->_diff_time($end, $start);

             // return the number of whole minutes between the 2 timestamps
             echo $obj->_diff_time($end, $start, "minute");



   * 2000-06-10: Initial release of code
   * 2000-07-12: I finally wrote the documentation
   * 2000-07-16: Added support for PHPLIB's template class. Added support
     for use of the GET query string to generate various cached files from
     the same script (method use_get). Changed my e-mail in the code.
     Updated the documentation
   * 2000-07-18: Added support for external data validation (version not
   * 2000-07-30: Reorganization of the code to use a general method for
     filename generation (_gen_filename), modified the external data
     validation method, and modified the use of user variables when
     writing/reading/validating cache to be more general, added the methods
     use_vars and _gen_var_val, eliminated the internal variable $USEGET in
     favor of 2 new variables $USEVARS and VARSVAL.
   * 2000-07-31: Added an example on how to use use_vars, modified the
     examples of is_data_valid, some minor code cleaning and updated the
     documentation. Renamed the example files get_example[12].php to
     use_get_example[12].php. New release: version 1.2
   * 2000-08-02: Found a typo that was causing errors in the read_from_cache
     method. Only happened in some cases when using the "in_name" option of
     use_get or use_vars
   * 2000-09-05: Fixed mispelt "$release" var in code and the 13 typos in
     the previous documentation
   * 2000-10-07: Added locking mechanism to the class (version 1.3,
   * 2001-02-06: Merged (modified) contributed code from Matthieu Casanova,
     which allows reading from cached files into a variable. This can be
     used to cache blocks of content in a page (or pages). New release:
     version 1.4


Known bugs

Bugs? this code is prefect :-)

If you do find a bug, contact me at the address: hide@address.com
(indicate "CachedTemplate BUG" in the subject)



(from the GPL license)

     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License as
     published by the Free Software Foundation; either version 2 of the
     License, or (at your option) any later version.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
     02111-1307, USA.

[Go to the URL: http://www.fsf.org/copyleft/gpl.html for the full text of
the GPL License]



   * Get Everything
     The CachedTemplate class, the TemplateAdapter classes, and the examples
     as a .zip or a .tar.gz archive (documentation included, ZIP created
     using GNU infozip).

     Below is the list of files included in the distribution:
       1. CachedTemplate.html - this document
       2. class.CachedTemplate.php - the CachedTemplate class
       3. Files related to CDI's Fastemplate class
             + class.FastTemplate.php3 - the template class
             + class.FastTemplateAdaptor.php - the adaptor class
             + fasttemplate_example.php - example
             + templates/main.tpl - template used in the example
             + templates/table.tpl - template used in the example
             + templates/row.tpl - template used in the example
             + use_get_example1.php - example using use_get ("in_name"
             + use_vars_example.php - example using use_vars ("store"
             + datavalid_example1.php - example using external data
               validation ("timestamp")
             + partial_caching.php - example using get_from_cache to show
               how caching of sections of a page can be implemented
       4. Files related to Herman's HTML Template class
             + herman_template.inc - the template class
             + class.HTMLTemplateAdaptor.php - the adaptor class
             + herman_example.php - example
             + herman_template.html - template used in the example
       5. Files related to Stefan's QuickTemplate class
             + class.QuickTemplate.php3 - the template class
             + class.QuickTemplateAdaptor.php - the adaptor class
             + quicktemplate_ex.php - example
             + first_example.tpl - template used in the example
       6. Files related to Richard's Template class
             + class.template.inc - the template class
             + class.RHTemplateAdaptor.php - the adaptor class
             + richard_example.php - example
             + header-template.html - template used in the example
             + main-template.html - template used in the example
             + footer-template.html - template used in the example
       7. Files related to Barnabás' XTemplate class
             + xtpl.php - the template class
             + class.XTemplateAdaptor.php - the adaptor class
             + xtemplate_ex5.php - example
             + ex5.xtpl - template used in the example
       8. Files related to PHPLIB's Template class
             + phplib_template.inc - the template class
             + class.PHPLIBTemplateAdaptor.php - the adaptor class
             + phplibtemplate_example.php - example
             + ptpl_box.ihtml - template used in the example
             + ptpl_page.ihtml - template used in the example
             + get_example2.php - example using use_get ("store" option)
             + datavalid_example2.php - example using external data
               validation ("md5")
       9. Miscellaneous files
             + COPYING - the GPL license version
             + LICENSE - the copyright and license notice for this package
             + rows.dat - data used in the datavalid_example[12].php files

   * Get the documentation
     The document you are reading now in text, Postscript or PDF formats.



Thanks to the people who sent suggestions, requests and bug reports: (email
addresses mangled in HTML code, to prevent spambot harvesting)

   * Jackson Tsai (hide@address.com) - GET query string use (feature
   * DK Kim (hide@address.com) - typo in class.XTemplateAdaptor.php
   * Skirando C. Philipona (hide@address.com) - PHPLIB's
     Template support (feature request)
   * Brad Atkins (hide@address.com) - Idea/request that lead to
     adding the data validation methods
   * Toby Champion (hide@address.com) - for finding the mispelt "$release"
     var name in the class code, and for considering that 13 typos in the
     documentation consists sloppy typing, that will make me use ispell more
   * Andreas Mock (hide@address.com) - for realizing that there could be a
     race condition when updating a cached file, which prompted the creation
     of the locking methods
   * Matthieu Casanova (hide@address.com) - for contributing a method
     to read cached files into variables, allowing for partial caching of
     content in pages


Last mangled on: Tue Feb 6, 2001
Return current item: Generalized CachedTemplate class