CachedTemplate: A generalized template caching class
Document: http://www.scripps.edu/~jesusmc/cachedtpl/CachedTemplate.html
Version: 1.4, Copyright © 2000-2001 Jesus M. Castagnetto
(jmcastagnetto@zkey.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
method.
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:
jmcastagnetto@zkey.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
Introduction
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
class.
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
packages:
* 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.
TOC
Class TemplateAdaptor
Description:
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
class.XTemplateAdaptor.php.
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):
<?php
/*
* Class TemplateAdaptor (FastTemplate version)
* by Jesus M. Castagnetto (jesusmc@scripps.edu)
* (c) 2000. Version 1.0
*
* Description:
* This class extends CDI's (cdi@thewebmasters.net) 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="") {
$this->FastTemplate($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:
init
Prototype:
void init ([mixed $par1 [, mixed $par2, ...]])
Definition:
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.
getTemplatesList
Prototype:
array getTemplatesList (void)
Definition:
This method returns an array with the names of the templates (if any)
that the main template script is using.
getParsedDoc
Prototype:
string getParsedDoc (void)
Definition:
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.
TOC
Class CachedTemplate
Description:
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
again
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.
Usage:
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
$ftpl->init("./templates");
// Define which templates will be used
// this needs to be done here so the validity of the
// cached file can be correctly ascertained
$ftpl->define(
array(
1main => "main.tpl",
table => "table.tpl",
row => "row.tpl"
)
);
// Check if we can send the cached file
if ($ftpl->valid_cache_file()) {
$ftpl->read_from_cache();
$end = $ftpl->utime();
$runtime = ($end - $start) * 1000;
echo "Completed in $runtime milliseconds<BR>\n";
exit;
}
// Otherwise process the page
(...)
Attributes:
This class has the following attributes (member properties):
$CACHEDIR
Directory to save cached files, default "./cache"
$CACHELENGTH
Length of caching, default: 30 units
$TIMEUNIT
Time unit for caching, default: "day"
$TIMEUNITSARR
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)
$USEVARS
a flag variable to indicate that the cache validation, reading and
writing methods will need to also use the contents of the $VARSVAL
variable.
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
$VARSVAL
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
$QUERY_STRING.
$USELOCK
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
variable.
$WAITLOCK
How many microseconds (default 100,000 microseconds = 100 milliseconds)
to wait until trying to read from, or write to, a locked cache file.
$MAXWAITLOCKCYCLES
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:
set_cache_dir
Prototype:
void set_cache_dir (string $dir)
Description:
Sets the directory to be used for caching parsed documents
Example:
// Set the caching directory to "../mycachedir"
$obj = new CachedTemplate();
$obj->set_cache_dir("../mycachedir");
set_cache_length
Prototype:
void set_cache_length (int $length)
Description:
Sets the maximum length (in time units) to keep cached documents
Example:
// Set the caching length to 12 hours
$obj->set_cache_length(12);
$obj->set_time_unit("hour");
set_time_unit
Prototype:
void set_time_unit (string $tunit)
Description:
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".
use_get
Prototype:
void use_get ([string $how])
Description:
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.
Example:
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();
$tpl->init();
// 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");
$tpl->use_get();
...
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:
...
$tpl->use_get("store");
...
use_vars
Prototype:
void use_vars ([mixed $vars, string $how])
Description:
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.
Example:
We will want to use some important values to generate unique cache
filenames:
$tpl = new CachedTemplate();
$tpl->init();
$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");
...
write_to_cache
Prototype:
void write_to_cache (string $data [, string $datacheck, string
$filename])
Description:
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 "_".
Example:
// 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");
read_from_cache
Prototype:
boolean read_from_cache ([string $filename])
Description:
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.
Example:
// read the current document from cache
$obj->read_from_cache();
get_from_cache
Prototype:
string get_from_cache ([string $filename])
Description:
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.
Example:
// get a cached block in this document
$header = $obj->get_from_cache("header_".$PHP_SELF);
is_cached
Prototype:
boolean is_cached (string $filename)
Description:
Returns true if the file $filename is cached, false otherwise.
Example:
if ($obj->is_cached("coolfile.parsed")) {
// do something
}
valid_cache_file
Prototype:
boolean valid_cache_file ([string $filename])
Description:
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
_gen_filename)
+ "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).
Example:
if ($obj->valid_cache_file()) {
echo $obj->read_from_cache();
} else {
// parse the document and save into cache
}
is_data_valid
Prototype:
boolean is_data_valid (string $datacheck[, string $type, string
$filename]
Description:
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
feasible.
Example
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();
mysql_select_db("documents");
$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.
set_use_lock()
Prototype:
void set_use_lock (void)
Description:
Activates the use of locking while reading/writing cache files
Example:
// use locking while processing cache files
$obj->set_use_lock();
unset_use_lock()
Prototype:
void unset_use_lock (void)
Description:
Deactivates the use of locking while reading/writing cache files
Example:
// do not use locking while processing cache files
$obj->unset_use_lock();
is_locked()
Prototype:
boolean is_locked (string 4filename)
Description:
Returns true if file lock exists, false otherwise
Example:
if ($obj->is_locked("mycachefilename")) {
// then do something;
} else {
// proceed as always
}
mk_lock()
Prototype:
boolean mk_locked (string 4filename)
Description:
Creates a lock file if one does not already exists, returning true if
succesful, false otherwise
Example:
if ($obj->mk_lock("mycachefilename"))
// then we can manipulate the cache file
} else {
// other process is locking the cache file, we need to wait
}
rm_lock()
Prototype:
boolean rm_locked (string 4filename)
Description:
Removes a lock file if one already exists, returning true if succesful,
false otherwise
Example:
// clean old lock files
if ($obj->is_locked("mycachefilename"))
$obj->rm_lock("mycachedfilename");
}
utime
Prototype:
int utime (void)
Description:
Returns timestamp including microseconds. Shamelessly *borrowed* from
CDI's FastTemplate class
Example:
$start = utime();
// after some code
$end = utime();
echo "Processing took: ".($end - $start) * 1000." milliseconds\n";
Private Methods:
_gen_var_val
Prototype:
_gen_var_val (array $vars)
Description:
Converts an associative array of $vars[name] = value into a url query
string of the form: name1=val1&name2=val2&...
_gen_filename
Prototype:
string _gen_filename (void)
Description:
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.
_key_in_array
Prototype:
boolean _key_in_array (string $key, array $arr)
Description:
Returns true if the key is in the array, false otherwise.
_is_valid_time_unit
Prototype:
boolean _is_valid_time_unit (string $timeunit)
Description:
Returns true if a the parameter passed is a valid time unit, false
otherwise.
_mktimestamp
Prototype:
int _mktimestamp (void)
Description:
Returns a timestamp in seconds. Wrapper for time().
_diff_time
Prototype:
int _diff_time (int $end, int $start[, string $timeunit])
Description:
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".
Example:
// 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");
TOC
History
* 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
released)
* 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,
unreleased)
* 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
TOC
Known bugs
Bugs? this code is prefect :-)
If you do find a bug, contact me at the address: jmcastagnetto@zkey.com
(indicate "CachedTemplate BUG" in the subject)
TOC
License
(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
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
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]
TOC
Download
* 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"
option)
+ use_vars_example.php - example using use_vars ("store"
option)
+ 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.
TOC
Acknowledgements
Thanks to the people who sent suggestions, requests and bug reports: (email
addresses mangled in HTML code, to prevent spambot harvesting)
* Jackson Tsai (jtsai@geotempo.com) - GET query string use (feature
request)
* DK Kim (dkong@kkee.co.kr) - typo in class.XTemplateAdaptor.php
* Skirando C. Philipona (claude.philipona@skirando.ch) - PHPLIB's
Template support (feature request)
* Brad Atkins (brad@digitalwebzone.com) - Idea/request that lead to
adding the data validation methods
* Toby Champion (tobych@gn.apc.org) - 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
often
* Andreas Mock (AMock@osram.de) - for realizing that there could be a
race condition when updating a cached file, which prompted the creation
of the locking methods
* Matthieu Casanova (mcasanova@gti-info.com) - for contributing a method
to read cached files into variables, allowing for partial caching of
content in pages
TOC
------------------------------------------------------------------------
Last mangled on: Tue Feb 6, 2001
|