Caching Result Sets in PHP: The Barebones of a Caching Class - Chaining things along: a quick look at the procedural caching solution (
Page 2 of 5 )
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.
 |