The cache feature of CI is nice, but still limited. It could have a function to delete the cache file of the current controller. And a function to delete the entire cache as well.
But the main thing it could have is also control the browser cache. Please take a look at:
http://www.sitepoint.com/article/caching-php-performance
http://www.sitepoint.com/article/php-anthology-2-5-caching (optional reading)
I found this excellent tutorial which explains in detail how to control the client side cache with headers and implement a server-side cache as well. The examples are really useful to bring more performance to a web site, but pay attention that getallheaders() or apache_request_headers() only work when PHP is running as an Apache module. Unfortunatelly, these functions are not available when PHP run as a CGI/FastCGI. Another way to get this header is checking $_SERVER[‘HTTP_IF_MODIFIED_SINCE’].
I did some tests and it works, but more tests are necessary with different browsers. I tested with Firefox 2 and Internet Explorer 6 and I noticed that they work different. Firefox always checks the server and work nice, but IE 6 don’t. It display the page faster, but it is not accurate (may is necessary to send extra headers, I don’t know). My test code was something like this (note that it is just a test, not an adaptation for CI):
// at the end the application, creates the cached file:
// get the actual uri
if(isset($_SERVER['PATH_INFO'])) {
$pathinfo=$_SERVER['PATH_INFO'];
} else {
$pathinfo=@getenv('PATH_INFO');
}
// define the cache filename based on uri
$cache_file='cache/'.md5($pathinfo);
// get the page contents
$buffer=gzencode(ob_get_contents(),9);
// write page to the cache
$fp=fopen("{$cache_file}.gz",'w');
flock($fp, LOCK_EX);
fwrite($fp,$buffer);
flock($fp, LOCK_UN);
fclose($fp);
clearstatcache(); // is this line necessary?
// remove the old cache file, if exists
if(is_file("{$cache_file}.gz")) unlink("{$cache_file}.gz");
// rename the file to the final name (this is to prevent file locking problems)
rename("{$cache_file}_new.gz","{$cache_file}.gz");
// send headers to the browser
// updates the date of the file in the browser cache
header('Last-Modified: '.gmdate('D, d M Y H:i:s',time()).' GMT');
// set to gzip compression
header("Content-Encoding: gzip");
// the line below brings a little more performance to the browsers
header('Content-Length: '.strlen($buffer));
// send the cached page to the browser
echo $buffer;
// at the beggining of the application, checks for a cached page:
clearstatcache(); // is this line necessary?
// get the actual uri
if(isset($_SERVER['PATH_INFO'])) {
$pathinfo=$_SERVER['PATH_INFO'];
} else {
$pathinfo=@getenv('PATH_INFO');
}
// define the cache filename based on uri
$cache_file='cache/'.md5($pathinfo);
// check if a cached file exists
if(is_file("{$cache_file}.gz")) {
// get the date and time when the cache file was created
$lastModified=filemtime("{$cache_file}.gz");
// get the date and time of the browser cached file
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
$modifiedSince=strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
} elseif(function_exists('getallheaders')) {
$request=getallheaders();
if(isset($request['If-Modified-Since'])) {
$modifiedSince=explode(';', $request['If-Modified-Since']);
$modifiedSince=strtotime($modifiedSince[0]);
} else {
$modifiedSince=0;
}
} else {
$modifiedSince=0;
}
// if the browser cached file is not old, send a 304 code and exit
if($lastModified<=$modifiedSince) {
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('HTTP/1.1 304 Not Modified');
exit;
} else {
// send the cached file to the browser
$file_size=filesize("{$cache_file}.gz");
// updates the date of the file in the browser cache
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
// set to gzip compression
header("Content-Encoding: gzip");
// the line below brings a little more performance to the browsers
header("Content-Length: $file_size");
readfile("{$cache_file}.gz");
exit;
}
} else { // no cached page
header('Expires: Mon, 1 Jan 1990 00:00:00 GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('Cache-Control: post-check=0, pre-check=0',false);
session_cache_limiter('must-revalidate');
}
As you can see, in my test I did gzip the cache files (may is necessary to check if server have support to it too). This can give more speed to browsers to load cached files and also reduces the disk space used. The only problem is that gzip uses more CPU power to compact the pages, but I think with a good cache control, this will not be an issue. May this be optional instead.
Well, more tests must be done before implement anything. And with delete cache functions we can control the server cache at our application, when pages are updated. The client side cache control will be done automatically by the framework.
There are more headers in HTTP 1.1 protocol to control proxy caches, etc, but I don’t think we must go more further on this, except if some are necessary to make this cache system to work correctly in all browsers.
