HomePHP Page 2 - Caching Result Sets in PHP: The Barebones of a Caching Class
Chaining things along: a quick look at the procedural caching solution - 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.
Before jumping directly into the process of writing some PHP classes and implementing an object-based caching system, let’s take a brief look at the procedural method developed in the second part of this series, which properly carries out the cache generation based on a content change trigger. The full code for this solution is listed as follows:
function writeCache($data,$cacheFile='default_cache.txt'){ if(!$fp=fopen($cacheFile,'w')){ trigger_error('Error opening cache file'); exit(); } if(!flock($fp,LOCK_EX)){ trigger_error('Unable to lock file'); exit(); } if(!fwrite($fp,serialize($data))){ trigger_error('Error writing to cache file'); exit(); } flock($fp,LOCK_UN); fclose($fp); } function readCache($cacheFile){ if(!file_exists($cacheFile)){ trigger_error('Invalid cache file'); exit(); } return unserialize(file_get_contents($cacheFile)); } function readQueryResult($options=array()){ // check parameters if(count($options)<5){ trigger_error('Invalid number of parameters'); exit(); } // create connection variables foreach($options as $key=>$value){ ${$key}=$value; } // connect to MySQL if(!mysql_connect($host,$user,$password)){ trigger_error('Error connecting to the server '.mysql_error()); exit(); } // select database if(!mysql_select_db($database)){ trigger_error('Error selecting database '.mysql_error()); exit(); } // check if query starts with SELECT if(!preg_match("/^SELECT/",$query)){ trigger_error('Invalid query. Must start with SELECT'); exit(); } if(!$result=mysql_query($query)){ trigger_error('Error performing query '.$query.mysql_error()); exit(); } while($row=mysql_fetch_array($result,MYSQL_ASSOC)){ $data[]=$row; } return $data; } function readData($options=array(),$cacheFile='default_cache.txt'){ // check to see if cache file is valid (content changed triggered caching) if(file_exists($cacheFile)){ if(rand(1,100)>90){ // read randomly data from MySQL $data=readQueryResult($options); // read cache data $cacheData=readCache($cacheFile); // compare cached data with fresh data // if content has changed then force new cache generation (md5(serialize($cacheData))==md5(serialize($data)))? $data=$cacheData:writeCache($data,$cacheFile); } else{ // read data from cache file $data=readCache($cacheFile); } } else{ // read data from MySQL $data=readQueryResult($options); // store data in cache file writeCache($data,$cacheFile); } return $data; }
And below, there is a simple implementation, where the cached result set is returned from a sample “users” database table:
// read data from MySQL-cache file $options=array('host'=>'host','user'=>'user','password'=>'password', 'database'=>'databasename','query'=>'SELECT * FROM users'); $data=readData($options,'cache_file.txt'); // display data foreach($data as $key=>$row){ echo 'First Name :'.$row['firstname'].' Last Name :'.$row['lastname'].'<br />'; }
Coding an example for putting this caching system to work is really easy and fairly understandable. Seemingly, the script is doing its thing without major problems. However, for large environments, this solution simply doesn’t scale properly. Despite the fact that we’ve built a couple of functions that hide all of the internal processing to read and write the cache file, as well as run SELECT statements, the overall code is not so easy to follow. Moreover, we’re not delineating clearly the tasks of each function, which results in hard-to-maintain code and reduced portability.
Thus, in order to address properly these limitations, we need to look at an object-oriented approximation that allows us to rapidly implement a caching system in production environments, with minor modifications.
In the next section of this article, we’ll see how to develop an expansible result set caching class, which can be easily “plugged” into an existing application. Keep on reading, because it’s really worthwhile.