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
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:
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:
- when reading a cache file
- if a lock file is found, wait for $WAITLOCK microseconds
and check again
- 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())
- when the lock goes away (if there was one) read the cached info
- when writing a cache file
- 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:
- "in_name" - appends the output of rawurlencode on the
$VARSVAL variable to the names of cached and
control files
- "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:
-
If a cached file exists (uses method is_cached) go to
the next step, otherwise return false and exit method.
-
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
-
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.
-
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.
-
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:
- CachedTemplate.html - this document
- class.CachedTemplate.php - the CachedTemplate class
- 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
- 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
- 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
- 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
- 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
- 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")
- 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