After reading JP's article, "Building your website with cached dynamic modules",
I decided I would rework many of my database-driven pages, incorporating his solution.
However, I realized that some functions were missing so I used his foundation and
adapted it with some additions of my own. Also, I realized that there were some
syntactical issues and fundamental usability that needed reworking.
I'll briefly list the specific functionalities that I added with explanation.
Spaces And Quoting
I needed the abililty to use spaces in the values of the arguments being passed
to the module. I also wanted the developer to be able to use any quoting
convention. Single or double quotes are valid around the value, but not both.
I used a template to generate cache files on the fly, and this was based on
dates and times. The cache's filename is generated on the value's being passed,
but some filesystems do not allow colons that would be found in a time format. I
realized I needed to allow the developer to name the cache file manually if desired.
Originally, this tag would have produced a cache file with a name such as,
'style_name=time now_time=12:32:00'. Now, this module would produce a cache file
with the simpler (valid) name: 'style_onTheAir'
'On The Hour' Cache Updating
JP's system allowed the developer to set the expires delay for a cache file. For
example, if the expires delay was set to 3600 seconds, the cache file would be
regenerated subsequent to the next hit after 3600 seconds have elapsed since the
creation of the cache file.
My site needed to work a little differently. I needed the ability to automatically
have the module refresh at specific intervals, such as, on the hour, or on the
quarter-hour, or on the half-hour, etc. So I went ahead and added some functions
to perform these evaluations and return whether the module was due to be updated or not.
<my-style name=test refresh='HALFHOUR'>
(this cache file updates itself every half-hour)
The available arguments for the refresh variable are: MINUTE, QUARTERHOUR, HALFHOUR, HOUR, HALFDAY, DAY, MONTH
Cache File Locking
I'm performing some complex SQL selects to generate some of these cached modules.
Some of them are taking longer than I would like, but priorities demand that I concentrate
on tasks other than database performance. I simply added some cron tasks to automate the
refreshing of modules in coordination with their tag REFRESH tag. The cron tasks simply
request the page from the web server.
However, I was concerned with multiple users hitting the server initiating redundant
SQL executions, which would unnecessarily increase the load on the database.
I used a simple locking solution. If the script determines that a cache file needs
to be updated, it creates a lock file that indicates the cache file is already being
generated. Subsequent requests for that same cache file notice that the lock file exists
and wait until that lock file is removed by the initial script and then grabs the cache
file when it finishes.
Hopefully, with these enhancements, this solution becomes even more powerful while
making it easier to implement for the developer.
You should be able to cut and paste it into your own solution.
<?php
//*********************************************************//
// function.parse_my_tags.php3
// Created By: "JP" <jprins@dds.nl>
// Modified By: "PHPBuilder Staff" <webmaster@phpbuilder.com>
// Last Modified Date: 11/21/1999
//
//*********************************************************//
// Special thanks to "JP" <jprins@dds.nl> for providing this
// concept and a solid source foundation for this solution.
// http://www.phpbuilder.com/columns/jprins20000201.php3
//*********************************************************//
// Reserved Tags: REFRESH, EXPIRES, CACHE_NAME
//
// 1) REFRESH -
// This tag will automatically check to see if this interval
// has passed and refresh the cache appropriately.
// I.E. QUARTERHOUR will automatically update the cache if
// the cache is older that the latest quarter hour
// arguments allowed: MINUTE, QUARTERHOUR, HALFHOUR,
// HOUR, HALFDAY, DAY, MONTH
// 2) EXPIRES -
// arguments allowed: Any interval in seconds
//
// Please note the differences between REFRESH and EXPIRES.
// EXPIRES - updates the content at the file's time of creation
// plus the interval
// REFRESH - updates the content on the interval hour.
// HALFHOUR is 11:00 and 11:30. The first refresh will occur
// at the first occurance that this interval is met after the
// cache as been created.
// I.E. a) Last cache creation @ 11:14
// b) Interval set at HALFHOUR
// c) Next refresh occurs at 11:30 (or the first
// request afterwards)
//
// refresh and expires may be used simultaneously if needed.
//
// 3) CACHE_NAME - this argument can be used to name the cache file
// in lieu of the default method of concatenating the arguments
// in the tag. This is useful when a tag contains characters
// that are unusable for filenaming like slashes or colins as found
// in date strings.
//
// Usage:
// <my-style name='test value' expires="3600" cache_name="myStyleCache">
//
// Note that code has been modified to use quoting (both, double and
// single quotes) and also modified to allow spaces to be used in values
//*********************************************************//
// Setup
$str_cache_file = "";
function parse_it ($str) {
global $arr_loaded;
global $str_cache_file;
$int_default_cache_time = 3600;
if (eregi ( "<[Mm][Yy]-([A-Za-z0-9]*) ([^>]*)", $str, $regs)) {
$str_tag = $regs[1];
if (!$arr_loaded[$str_tag]) {
// include("./res/$str_tag/$str_tag.php"); //For Unix
include("res\$str_tag\$str_tag.php"); // For Win
$arr_loaded[$str_tag] = 1;
}
$str_func = "handle_$str_tag";
$arr_list = explode ( " ", strtolower ($regs[2]));
// $cache_dir = "cache/$str_tag"; // For Unix
$cache_dir = "cache\$str_tag"; // For Win
$str_cache_file = $cache_dir;
$flag_quote = 0;
for ($i = 0; $i < count ($arr_list); $i++) {
// *Here we are handling the tag's value..
// *Checking for single and double quoting.
// A New Tag
if ($flag_quote == 0) {
if ($argname = strtok($arr_list[$i], "=")) {
$val = strtok ("=");
if ($val[0] == "'") {
$char_quotetype = "'";
$flag_quote = 1;
$arglist[$argname] = substr($val, 1);
} elseif ($val[0] == "\"") {
$char_quotetype = "\"";
$flag_quote = 1;
$arglist[$argname] = substr($val, 1);
} else {
$flag_quote = 0;
$arglist[$argname] = $val;
}
if (substr($arglist[$argname], -1) == $char_quotetype) {
$flag_quote = 0;
$arglist[$argname] = substr($arglist[$argname], 0, -1);
}
}
} elseif ($flag_quote == 1) {
if (substr($arr_list[$i], -1) == $char_quotetype) {
$arglist[$argname] .= " " . substr($arr_list[$i], 0, -1);
$flag_quote = 0;
} else {
$arglist[$argname] .= " " . $arr_list[$i];
}
}
// Generate the cache file's name
// We exclude any non-specific arguments for use in
// naming the cache file
if (($argname != "expires") &&
($argname != "refresh") &&
($argname != "cache_name")) {
$str_cache_file .= "_" . $argname . "=" . $arglist[$argname];
}
}
// if the developer set a cache_name, use it instead
// of concatenating the module arguments for a cache
// filename
if (isset($arglist["cache_name"])) {
$str_cache_file = $cache_dir . "_" . $arglist["cache_name"];
}
// Check to see if this tag is already being updated
// If so, wait until it is completed and then continue
clearstatcache();
// Here we perform some error correction. If a lock file
// is left over from a previous request, this ensures that
// it is deleted.
if (file_exists($str_cache_file . '.lock')) {
register_shutdown_function("remove_cache_lock");
}
while (file_exists($str_cache_file . '.lock')) {
sleep(2);
clearstatcache();
continue;
}
clearstatcache();
//*********************************/
// Some other functions
function remove_cache_lock() {
global $str_cache_file;
clearstatcache();
if (file_exists($str_cache_file . '.lock')) {
@unlink($str_cache_file . '.lock');
}
}
//*********************************/
// CACHE REFRESH CHECKING FUNCTIONS
// Each function checks the next interval higher for verification
function checkRefreshYear($systime, $filetime) {
$sysYear = date
var gDomain="www.qsstats.com";
var gDcsId="dcsee0dng10000sdxo14hwex8_4x4k";
var gFpc="WT_FPC";
var gConvert=true;
var gFpcDom = "phpbuilder.com";
if ((typeof(gConvert) != "undefined") && gConvert && (document.cookie.indexOf(gFpc + "=") == -1) && (document.cookie.indexOf("WTLOPTOUT=")==-1)) {
document.write("<\/SCR"+"IPT>");
}
function dcsAdditionalParameters() {}