Hudzilla.org - the homepage of Paul Hudson
Contents > Performance Wish List | Report Bug | About Me ]

18.5     Caching PHP

This is NOT the latest copy of this book; click here for the latest version.

PHP speed boosters like Zend Performance Suite and PHP Accelerator (often called "soft" cachers) work by caching your PHP code before it is executed, which provides a substantial speed boost. But what do you do if the PHP processing itself is taking longer than is acceptable? The answer is usually to cache the output of your pages after they have been output as HTML, a process often known as hard caching, which has two side effects.

Firstly, it will mean your pages will be static most of the time. This is obviously not an option for sites that need dynamic content all the time, such as shopping baskets and the like, but it is definitely possible for news sites where the content only changes once every five minutes. Even on messageboards, it is possible to print a message such as "your post will be displayed within the next five minutes", like Slashdot does.

Secondly, it will mean you get the biggest possible speed boost, because you are only outputting HTML for the majority of the time. All PHP has to do is run a check along the lines of "if content is more than five minutes old, regenerate it, otherwise send the old stuff".

To implement this system, you need to modify your PHP scripts so that they check for the cached copy being out of date, and generate a new version if needed. Here is an example of how to implement this, with a ten-second cache life to make the caching fairly apparent - you will probably want a five- or ten-minute cache life.

<?php
    $cachefile
= basename($_SERVER['PHP_SELF'], '.php') . '.cache';
    
clearstatcache();

    if (
file_exists($cachefile) && filemtime($cachefile) > time() - 10) { // good to serve!
        
include($cachefile);
        exit;
    }

    
ob_start();

    print
"This is some text.<BR />";
    print
"This is some text.<BR />";
    print
"This is some text.<BR />";
    print
"Last updated: " . date("H:i:s");

    
$contents = ob_get_contents();
    
ob_end_clean();

    
$handle = fopen("/var/www/public_html/$cachefile", "w");
    
fwrite($handle, $contents);
    
fclose($handle);

    include(
$cachefile);
?>

First, we use the basename() function, which strips out all path information about the $_SERVER['PHP_SELF'] variable, which holds the name of the current script. Basename() takes a second parameter, an extension, and if the filename passed as the first parameter has the file extension passed as the second parameter, basename() strips that off also. So, the first line will convert /home/gallery/picture1.php into just "picture1", to which we append ".cache" to get our cache file, so $cachefile will be set to "picture1.cache".

Then, clearstatcache() is called. PHP stores the result of all file checking functions in order to provide a speed boost, but as we're going to be checking file modification time regularly, it is best to clear this cache before we do any checks. The checks to perform are on the next line: first we check whether our cache file exists, and then we use filemtime() on $cachefile to check whether the cache file is out of date. Filemtime() returns its dates in a Unix timestamp, so we can compare that against the results of time() (the current time) minus 10 to get our 10-second cache life.

If the file exists and is not out of date, the cache file is good to be re-used, so we just include() and exit the script as our job is done. If it is not, it needs to be regenerated, and this is done using output buffering. Output buffering allows us to output content to a scratchpad, which we can choose to output to visitors or empty. Ob_start() starts the buffering, and then we output four lines of text, including a time stamp so we can be sure caching is working. We then call ob_get_contents(), which returns all the content in our output buffer awaiting full output, then we call ob_end_clean() to clear the buffer - that is, the only copy of the four lines of text is now sitting in $contents.

To generate the cache file, we use fopen() to open the write file for writing, fwrite() out the contents of our output buffer, then close the handle. Finally, we use include($cachefile) again so that users always see the output as if we hadn't had to generate it from scratch.

Caching pages as simple as just four lines of text is not going to help - in fact, it is likely to slow things down. But if you have a lot of work, such as reading the message list in a messageboard, it will help massively.





<< 18.4 PHP Accelerators   18.6 PHP as a CGI or a module? >>
Table of Contents
Want to see this stuff in print? PHP in a Nutshell takes the core topics covered here, adds in thousands of edits from the editorial team and myself, and combines them to make an unbeatable reference for PHP programmers at all levels.



My latest book has hundreds more tips on how to use PHP, Apache, and MySQL, plus Perl, Python, shell scripts, performance tuning, and more!



Top-right shadow
 
Bottom-left shadow Bottom shadow

Comments from other readers
Navi - 29 Aug 2008

Before you start to implement your own cache, check Smarty(.php.net), which has its own caching even of part of pages - so you can combine dynamic and (cached) static page.



Add comment
Please note that by posting a comment here you are committing it to the public domain. This is important so that others can make use of your code themselves, and also so that I can incorporate helpful notes directly into the main text. Comments are limited to 2000 characters in length.

If you are reporting an error in the content, please tell me directly.

Your name/email address:
Your comment:
 
Now, in order to verify that you're a real person, please answer this simple question: what is one plus two?
The answer is:
(please write in
numbers, eg 19)


Top-right shadow
 
Bottom-left shadow Bottom shadow