HomePHP Page 3 - Caching Result Sets in PHP: A Content-Change Triggered Caching System
Another caching strategy: building a content-change triggered caching system - PHP
Caching within the context of PHP application acceleration can be triggered based on three possible categories: time expiry, content change, and manually. This article covers an application that triggers the caching mechanism based on a content change condition.
As I said before, there are different ways to develop a caching system. In this case we’ll create a script that triggers the caching process when a modification has been introduced to the data. We’ll reuse the functions previously defined in the first article, so the new script shouldn’t present any difficulties.
The logic of the script is very understandable, as you’ll see shortly. It simply runs randomly a SQL SELECT statement and verifies whether the newly obtained data is the same as the cached data. If it is not, then the new result set is retrieved and stored again in the cache file. Otherwise, the data is directly read from the cache file. This sounds simple and looks simple too. Therefore, let’s see the complete source code for the script and next proceed to explain each relevant section.
The content-change triggered cache script looks like this:
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 connectMySQL($host,$user,$password,$database){ if(!$db=mysql_connect($host,$user,$password)){ trigger_error('Error connecting to the server '.mysql_error()); exit(); } if(!mysql_select_db($database,$db)){ trigger_error('Error selecting database '.mysql_error()); exit(); } } function query($query){ if(!$result=mysql_query($query)){ trigger_error('Error performing query '.$query.mysql_error()); exit(); } return $result; } // define cache file $cacheFile='cacheFile.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 connectMySQL('host','user','password','databasename'); $result=query('SELECT * FROM users'); // store result set in array while($row=mysql_fetch_array($result,MYSQL_ASSOC)){ $data[]=$row; } // 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 connectMySQL('host','user','password','databasename'); $result=query('SELECT * FROM users'); // store result set in array while($row=mysql_fetch_array($result,MYSQL_ASSOC)){ $data[]=$row; } // store serialized data in cache file writeCache($data,$cacheFile); } // display data foreach($data as $key=>$row){ echo 'First Name :'.$row['firstname'].' Last Name :'.$row ['lastname'].'<br />'; }
As you can see, we’ve reused most of the existing functions for reading and writing the cache file, as well as the functions to connect to MySQL and run SQL queries. However, the noticeable difference compared to the previous method resides on the cache trigger. We use a random positive integer value to determine when to run the query and return a new result set, and then compare it to the cached data. The following snippet illustrates this condition:
if(rand(1,100)>90){ // read randomly data from MySQL connectMySQL('host','user','password','databasename'); $result=query('SELECT * FROM users'); // store result set in array while($row=mysql_fetch_array($result,MYSQL_ASSOC)){ $data[]=$row; } // 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); }
In this case, I’ve used the “rand()” function to obtain a random number between 1 and 100. If the value is greater than 90, the query is run. Next, the “fresh” data is compared to the cached data by performing a MD5 hashing algorithm on each value. If the data has changed, the cache is regenerated, and the new data is returned for processing. Otherwise, the data is simply read from the cache file.
The data comparison is carried out through the following expression:
Indeed, not only is the script fairly easy to be understood, but it supports many improvements. Feel free to make the proper modifications to suit your specific needs, as long as you’re using a procedural approximation.
Finally, the overall code might be wrapped into a pair of containing functions, in order to hide all of the internal processing, and focus on the handled data rather than the functions. Therefore, let’s complete the next step, by defining the new “readData()” and “readQueryResult()” function. Don’t you worry, it’s only a few lines away.