Caching PHP

PHP speed boosters like Zend OPcache (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.

 

Want to learn PHP 7?

Hacking with PHP has been fully updated for PHP 7, and is now available as a downloadable PDF. Get over 1200 pages of hands-on PHP learning today!

If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!

Next chapter: PHP as a CGI or a module? >>

Previous chapter: PHP Accelerators

Jump to:

 

Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.