A Simple PHP Caching System

Sometimes you need to cache data, most of the time you'll be wishing you could cache data easily. Server-side data caching is an incredibly complex topic, and one that has a multitude of possible solutions. But a lot of the time you just need a caching system that's simple and works.

To address this issue in a search engine that I am working on, IntraSITE Search, I wrote a very simple and versatile caching system that only requires that your PHP script has disk write access. The code works beautifully and transparently, adding caching to a pre-existing system may only require the addition of 4-5 lines of code!

Using the Caching System:

Creating a data cache: Simply call the create_data_cache() function like so:

// Create some test data
$test_data = file_get_contents( 'http://php.net/' );

// Store the test data in a cache file called 'test_cache.cac'
$cache_created = create_data_cache( 'test_cache.cac', $test_data );

// Alert the user if the cache could not be created
if( !$cache_created )
	{
	echo 'ERROR: Data cache could not be created!';
	}

The create_data_cache() function will return true if it was able to create the cache, it will return false otherwise.

Retrieving data from the cache: call open_data_cache() like so:

// Set the cache timeout in seconds: 43200 = 12 hours
$cache_timeout = 43200;

// Try to retrieve the data from the cache
$cache_opened = open_data_cache( 'test_cache.cac', $cache_timeout );

// Alert the user if the cache was too old or could not be found
if( !$cache_opened )
	{
	echo 'ERROR: The cache was too old or could not be found';
	}

The open_data_cache() function will return the cached data on success, if the cache is older than the supplied timeout or is unable to be found it will return false.

A Note on Security:

Remember: If your cache files are stored anywhere within your server's document root then anyone will be able to view them! My recommendation is to store all your cache files somewhere outside of your document root. Even seemingly innocous data will reveal the names of your variables and if 'register_globals' is enabled on your server that could cause you a lot of pain later on...

The Code:

// Coding and Concept by Aaron Gough http://www.aarongough.com

// This work is licensed under the Creative Commons Attribution-Share Alike 3.0 
// Unported License. To view a copy of this license please visit:
// http://creativecommons.org/licenses/by-sa/3.0/


// ****************************************************************************************************
// ****************************************************************************************************
// this function saves the current crawl data to the data cache and marks it with the current UNIX timestamp
function create_data_cache ( $filename,  $data )
	{
	// create the data cache file
	$data_cache_file = fopen($filename, 'w');
	
	// generate the string that contains all the data that needs to go into the cache file
	$data_cache = time() . "[datacache_start]" . serialize( $data );
	
	// write the data cache to the cache file
	fwrite( $data_cache_file, $data_cache );	
		
	// close the data cache file
	fclose( $data_cache_file );
	
	// if the cache was able to be created then we return true
	if( file_exists( $filename ))
		{
		return true;
		}
	// if there was an error creating the cache then we return boolean false
	else
		{
		return false;
		}
	}
	
	
// ****************************************************************************************************
// ****************************************************************************************************
// this function looks for the data-cache file. If the file is older then the specified cache-time
// or doesn't exist then it returns boolean false
function open_data_cache ( $filename, $cache_time )
	{
	// check to see if the data-cache file actually exists
	if( file_exists( $filename ))
		{
		// open the data cache file
		$data_cache_file = fopen( $filename, "r");
		
		// read all of the data from the data cache
		$data_cache = fread( $data_cache_file, filesize($filename));
		
		// close the data cache file
		fclose( $data_cache_file );
		
		// extract the data cache timestamp
		$data_timestamp = strpos( $data_cache, "[datacache_start]");
		$data_timestamp = substr( $data_cache, 0, $data_timestamp );
		
		// convert the textual timestamp into an integer for comparison with the current timestamp
		$data_timestamp = intval( $data_timestamp );
		
		// check to see if the data cache is older than cache_time specifies
		if( (time() - $data_timestamp) > $cache_time )
			{
			// because the data-cache is too old to use we need to return false
			// to the calling code
			return false;
			}
		// if the data cache is recent enough that we can use it then we need to load it into RAM as an array
		else
			{
			// find the start of the data within the cache file
			$data_start = ( strpos( $data_cache, "[datacache_start]") + strlen("[datacache_start]"));
			
			// separate the data from the timestamp and data separator
			$data_cache = substr( $data_cache, $data_start );
			
			// unserialize the data into the $data variable
			$data = unserialize( $data_cache );
			
			// return the array to the calling code
			return $data;
			}
		}
	// if the data cache file does not already exist then we need return boolean false
	else
		{
		return false;
		}
	}