HomePHP Page 3 - Caching Result Sets in PHP: The Barebones of a Caching Class
The object-oriented solution: developing a result set caching class - PHP
While procedural caching may be well-suited for small and even medium-sized applications, the picture changes for large projects. That's when object-oriented approaches come into their own. This article focuses on developing an object-based extensible caching solution.
Prior to developing the caching class, we need to specify some important prerequisites. First, the class will implement a time expiry-based caching trigger. However, this is not a big limitation, since a different caching trigger might be developed without making significant changes to the source code. This will be left as a possible additional feature.
Second, the class will aggregate a MySQL abstraction object, for having database connectivity within its scope, so if you’re not familiar with the concept of aggregation, feel free to visit Object Interaction in PHP Introduction to Aggregation part 1, where I’ve written an article series that discusses the topic in detail.
Finally, since the source code is highly portable, in a future article the whole set of classes will be updated to PHP 5, aimed specifically at those developers working with the latest version of PHP.
All right, having stated these few disclaimers, it’s time to look at the result caching class, which, not surprisingly, I’ve called “Cache.” Its definition is as follows:
class Cache{ var $mysql; // instance of MySQL object var $result; // instance of Result object var $expiry; // cache expire time in seconds var $cacheFile; // cache file var $data; // result set array // constructor function Cache(&$mysql,$expiry=86400,$cacheFile='default_cache.txt'){ $this->mysql=&$mysql; (is_int($expiry)&&$expiry>0)?$this->expiry=$expiry:$this->mysql- >isError('Expire time must be a positive integer'); $this->cacheFile=$cacheFile; $this->data=array(); } // if cache is valid, perform query and return a result set. Otherwise, get results from cache file function query($query){ // check if query starts with SELECT if(!preg_match("/^SELECT/",$query)){ $this->mysql->isError('Invalid query. Must start with SELECT'); } if(!$this->isValid()){ // read data from MySQL $this->result=$this->mysql->query($query); // write data to cache file $this->data=$this->write(); } else { // read data from cache file $this->data=$this->read(); } } // write cache file function write(){ if(!$fp=fopen($this->cacheFile,'w')){ $this->mysql->isError('Error opening cache file'); } if(!flock($fp,LOCK_EX)){ $this->mysql->isError('Unable to lock cache file'); } while($row=$this->result->fetchRow()){ $content[]=$row; } if(!fwrite($fp,serialize($content))){ $this->mysql->isError('Error writing to cache file'); } flock($fp,LOCK_UN); fclose($fp); unset($fp,$row); return $content; } // read cache file function read(){ if(!$content=unserialize(file_get_contents($this->cacheFile))){ $this->mysql->isError('Error reading from cache file'); } return $content; } // determine cache validity based on a time expiry trigger function isValid(){ if(file_exists($this->cacheFile)&&filemtime($this->cacheFile)>(time ()-$this->expiry)){ return true; } return false; } // fetch cache row function fetchRow(){ if(!$row=current($this->data)){ return false; } next($this->data); return $row; } // fetch all cache rows function fetchAll(){ if(count($this->data)<1){ $this->mysql->isError('Error accessing cache data'); } return $this->data; } // count cache rows function countRows(){ if(!$rows=count($this->data)){ $this->mysql->isError('Error counting cache rows'); } return $rows; } }
At first glance, and looking at the data member declaration, we can see that the “Cache” class aggregates a MySQL object and a “Result” object, where the first one is directly passed to the constructor. So, if you might want to see the complete code for each class involved, don’t feel concerned about this. We’ll see in turn, the proper definition for all of the additional classes that we’re working with.
For the moment, let’s focus our attention on the code for the “Cache” class, which will be explained in detail over the next few lines.