Simple OO PHP caching
Often sites are database driven because it makes it easy to add huge volume of content that is easy to maintain. After the initial creation, often the bulk of the content remains unchanged and there is little point in generating pages from scratch using the DB for every request. This page shows how to set up a very simple cache to ease the load on the server and, hopefully, make your site run faster. It assumes you are running PHP4.x on a *nix server.
This page has an example of a simple OO cache that will:
- Generate static HTML pages to behave as the server cache
- Send the cached page if it is current, or create a new page if the database has been updated
- Decide if a 304 response is appropriate, and send one if it can
- Terminate the script if the cache can be used instead of building the dynamic page
Requirements
- PHP4.x
- A location outside of the web root to use as a cache this needs to be world writeable ( chmod 777 cache_directory, 757 should work too, but it's not really any more secure )
- A way of telling if your database content has changed - I use a TIMESTAMP field in content tables to provide this
NOTE:
- this elementary cache does not check if the php 'template' ( the page that produces the dynamic pages ) has been altered since the cached page was created. It would be pretty trivial to add in many cases, but in the case of nested classes and includes gets to be more demanding, it's easier to just delete the cached files when you make a change to the backend
- the cache doesn't take account of changes in the 'family tree' of documents. For example if you have your files grouped into a hierarchy and remove, add, or edit one of the siblings or parents this will not be reflected in the cache. Again, it's far easier to delete the cache than add the hit to the server of checking the records ( although, as I type this I may be changing my mind, this page may change )
The PHP code
class cache {
var $good=FALSE;
var $filename=NULL;
var $lastModified=NULL;
var $contentLength=NULL;
var $request_type=NULL;
var $etag=NULL;
function cache($filename, $db_time){
$this->request_type=$_SERVER['REQUEST_METHOD'];
$this->filename=PT_CACHE.$filename.".html";
if (file_exists($this->filename)) {
$time_diff=$db_time-filemtime($this->filename);
if ($time_diff<0) {
$this->good=TRUE;
$this->lastModified=gmdate('D, d M Y H:i:s', filemtime($this->filename)) . ' GMT';
$req_date=(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']))?
$_SERVER['HTTP_IF_MODIFIED_SINCE']
:"";
$etag=(isset($_SERVER['HTTP_IF_NONE_MATCH']))?
$_SERVER['HTTP_IF_NONE_MATCH']
:"";
$this->etag=md5($this->lastModified.$this->contentLength);
if ($req_date == $this->lastModified or $etag == $this->etag) {
header('HTTP/1.0 304 Not Modified');
exit;
} else {
$this->contentLength=filesize($this->filename);
header("Last-Modified: ".$this->lastModified);
header("Content-Length: ".$this->contentLength);
header("ETag: ".$this->etag);
if ($this->request_type=="GET") {
readfile($this->filename);
}
exit;
}
}
$this->good=FALSE;
ob_start();
}
function writeCache() {
if ($this->filename) {
$content=ob_get_flush();
$cache_file=fopen($this->filename,'w');
fwrite($cache_file, $content);
fclose($cache_file);
}
}
}
What to send in - the constructor arguments
This is written in PHP 4.x and uses the contructor to set up the cache.
- $filename is the cache filename - ie the file to create if the cache is not usable and to be sent out if it is good. This is pretty easy to get with "Search Engine Friendly URLs"
- $db_time is the date of the most recent change to take account of - it should be in unix epoc format ( easily obtained from UNIX_TIMESTAMP in MySQL )
What to do & how to use the cache
- Save the code above into a file ( I normaly name such files good_name.class.inc ) and upload it to your server, it should, ideally, be out of the web root as well.
- Create your cache directory and make it writeable
- Add a constant to the script
( define("PT_CACHE", "/path/to/cache/on/server/"); ). You can do this with $_SERVER['DOCUMENT_ROOT'] and realpath() to make it more flexible.
- Include or require the class into your dynamic page. Once your dynamic script has the most recent altered date and filename together call the cache ( $myCache=new cache($file_name, $last_updated); )
- At the end of your dynamic file place $myCache->writeCache(); As you'd expect this ends the output buffering, sending the page to the client, and creates a file in the cache directory
What's missing?
- Proper error checking - it fails well on most production servers - the worst thing that can happen is that the cache directory isn't writeable, if error displaying is turned off ( as it really should be! ) the file will be sent to the client, but not cached.
Feedback on this page would be very gratefully received, my email is at the bottom of the page. I'd appreciate a copy of any enhanced version as well, thanks.
If you came looking for a great and fully featured php cache then try Cache_lite
Useful external links:
© 2004
Dana & Andy Dopleach
info@bylandwaterandair.com
Site Map