How to create a simple and efficient PHP cache

by Jean. 25 Comments -

When working on php websites made from scratch and without a framework, speed can often be an issue. Caching is extremely useful in order to speed up PHP webpages. In this article, I’ll show you a super easy and efficient way to dynamically cache php pages that need to be faster.

Step one: Create the top-cache.php file

We need to create two files. Here’s the first one: Create a new file named top-cache.php and paste the code below in it.

<?php
$url = $_SERVER["SCRIPT_NAME"];
$break = Explode('/', $url);
$file = $break[count($break) - 1];
$cachefile = 'cached-'.substr_replace($file ,"",-4).'.html';
$cachetime = 18000;

// Serve from the cache if it is younger than $cachetime
if (file_exists($cachefile) && time() - $cachetime < filemtime($cachefile)) {
    echo "<!-- Cached copy, generated ".date('H:i', filemtime($cachefile))." -->\n";
    include($cachefile);
    exit;
}
ob_start(); // Start the output buffer
?>

So, what this code does? The first 5 lines create the cached file name according to the current php file. So, if you’re using a file named list.php, the cached file will be named cached-list.html.

Line 6 creates a $cachetime variable which determines the life of the cache.

Lines 9 to 13 are a conditional statement which look for a file named $cachefile. If the file is found, a comment is inserted (line 10) and the $cachefile file is included. Then, the exit statement stops the execution of the script and the file is sent to the client brother. Which means that if a static file is found, no php code is interpreted by the server.

Line 14 creates a buffer, if the $cachefile file isn’t found. That’s all for the top-cache.php file.

Step two: Create the bottom-cache.php file

Now, create a second php file, named bottom-cache.php and paste the code below in it.

<?php
// Cache the contents to a file
$cached = fopen($cachefile, 'w');
fwrite($cached, ob_get_contents());
fclose($cached);
ob_end_flush(); // Send the output to the browser
?>

If a file named $cachefile isn’t found on your server, this code is executed and create the file, so next time the page will be called, the $cachefile static file will be served to the client browser instead of executing the whole PHP file.

Step three: Include cache files on your page

Now that you have created the two necessary files, you simply have to include them on the php page you wish to cache. As you probably guessed, the top-cache.php file must be included in the beginning of your php page and the bottom-cache.php at the end, as shown below:

<?php

include('top-cache.php'); 

// Your regular PHP code goes here

include('bottom-cache.php');
?>

Now if you test the cache on a slow page, you’ll be amazed by how faster the page is. This easy cache is my favorite solution when working on “from scratch” PHP websites.

  • http://www.internetbuff.co.uk Simon

    Nice straight forward post. Thanks.

  • http://darkblue.sdf.org/ Jonathan Hollin

    Why use “fopen”, “fwrite” and “fclose” when you can simply file_put_contents(ob_get_contents())?

    • http://www.rahulprasad.com Rahul Prasad

      For performance gain. file_get_contents and file_put_contents are slower.

  • http://www.boiteaweb.fr Julio Potier

    What about $_POST, $_GET, $_COOKIE, $_REQUEST values ?
    list.php => cached.list.html
    list?php?id=123 => cached-list.html
    Same file ? o_O

    Also :
    /*****/
    $url = $_SERVER["SCRIPT_NAME"];
    $break = Explode(‘/’, $url);
    $file = $break[count($break) - 1];
    /*****/
    is equal to this simple line :
    /****/
    $file = basename( $_SERVER["SCRIPT_NAME"], ‘.php’ ); // is enought
    /****/

    • Sky

      Julio, i think the point of the directory parsing is that he must most probably use like a htaccess and not have many, or any vars with _GET. (i myself hardly never use _GET vars anymore…)

      You could just encode the REQUEST_URI to make a uniq identifier with _GET and all.

      For the _POST, its not a real problem because i dont think you want to cache that.

      Very nice script.
      Thx for sharing !

  • http://shahmirj.com Shahmir Javaid

    I believe apache does what you are doing using Disk Cache

    However If its data that is holding you down, I would use memcache. There is a php memcache client that is pretty usefull. I suppose both will require you to have access to install/configure memcache or apache. So your solution would be helpfull then.

  • David Runion

    I wrote a similar php cache about a year ago, found it to be so simple to build that I hesitated to put it into production use, thinking I must have missed something. We don’t get much traffic (~10k unique visitors / month) so I’ve kept this in my back pocket. I ended up implementing a fallback MySQL server which has been the weakest link when we get a flood of requests.

    https://gist.github.com/3812913

    My script handles $_GET variables and doesn’t return a cached page if $_POST is non-empty.

    I found it easy and satisfying to build, and looking over it today, surprisingly readable!

    If anyone looks at that gist, it’ll be the first time anyone has looked at my PHP code ever, so let me know what you think.

    One big difference with my setup is I’m using the PHP setting auto_prepend which allows you to have a file automatically include()d on every PHP page, and then I use PHP shutdown functions (which are passed the full text/html that is about to be output to the browser) to save the cache file. You can specify the shutdown function in the auto_prepend code and they’ll be run at the very end. You can also set the PHP variable auto_append which works as you would expect.

  • http://lutviavandi.com Lutvi Avandi

    Thanks for your sharing. I have a site using PHP from scratch but don’t have cache system. I think this tutorial is really what I need

  • http://blog.mostof.it Csaba Okrona

    Please, please use Varnish.

  • http://rommelxcastro.com Rommel

    really useful and simple!!!

  • http://blog.exodica.com.ar Exos

    Bottom cache file??? why? Yo can define a callback before push the buffer:

    ob_start(function ($buffer) {
    //save $buffer
    return $buffer;
    });

    I think we do all that effort, having already made tools is trying to reinvent the wheel.

    I works in a cache package for PHP with good results in hi-performs stages:

    https://github.com/exos/HybridCache

    There are a class called “PageCache”, with a very simple implementation, and supports disk cache, memcache and redis

    PD: Sorry my english!

  • http://petitchevalroux.net/ petitchevalroux

    Please use readfile instead of include, if i write a comment like this Your cache may redirect to mine ;)

  • http://aloanuntilpayday.com Valeriy

    Hey Jean-Baptiste Jung,

    Another awesome post. Had no idea that cache was so easy to make. Good stuff, combined with all of the comments. This results in a solid post. Thanks.

  • http://www.codingcollege.net Kristoffer

    My friend and I once made a speed test – MySQL vs raw files. I assume that if you need to get specific results from a MySQL DB, you’d be better off getting it from the MySQL DB if you needed to search for it as it is indexed rather than caching it. Right?

    Else, great post. Thank you. I learned a lot from it.

  • http://dan.cx/ Daniel15

    This cache is a good simple solution, but isn’t as fast as it could be, as the PHP file is still being parsed. The best caching mechanism would not go through PHP at all if a cached version is available. This is what the WP Super Cache and W3 Total Cache plugins for WordPress both do.

    This could be done using a rewrite rule, or via a caching proxy like Varnish or HAProxy.

  • http://www.handy-abhoeren.biz albin

    I made a version now where the bottom part is called via the callback function of ob_start. So there is just one file to be included. Not too difficult, nevertheless it was quite tricky, because of a php bug, where den cad (current directory) is changed within the callback function.

    One solution is to define a constant outside of the callback function with the directory in it and use that constant in the callback function

    define(“CACHEFILE”, str_replace(“\\”, “/”, realpath(dirname(__FILE__))).”/cache/”.$filename);

    albin

    p.s.: “bottom part”

    ob_start(“cache_it”); // Start the output buffer

    function cache_it($buffer) {
    $cached = fopen(CACHEFILE, ‘w’);
    fwrite($cached, $buffer);
    fclose($cached);
    return $buffer;
    }

  • http://potrebka.pl Piotr

    Probably this function is better:
    [php]
    $dir_cache = dirname(__FILE__) . ‘/wp-content/cache/’;
    if( !is_dir($dir_cache) ) mkdir( $dir_cache, 0777, true );
    $file = $_SERVER['REQUEST_URI'];
    if(strlen($_SERVER['REQUEST_URI']) > 100) {
    $file = substr($_SERVER['REQUEST_URI'], 0, 100);
    }
    $file = str_replace(array(‘/’, ‘.html’, ‘.htm’), array(‘-’, ”, ”), $file);
    $file = trim($file, ‘-.’);
    $file = preg_replace(“#[\"\']#”, ”, $file);
    $file = preg_replace(“#[^a-z0-9-.]#i”, ”, $file);
    if(empty($file)) $file = ‘index’;
    $cachefile = $dir_cache . ‘cached-’ . $file . ‘.html’;
    $cachetime = 18000;

    // Serve from the cache if it is younger than $cachetime
    if (file_exists($cachefile) && time() – $cachetime < filemtime($cachefile)) {
    echo "\n”;
    include($cachefile);
    exit;
    }
    ob_start(); // Start the output buffer
    [/php]

    Why? Bacouse cache is saving to the cache directory inside wp-content dir. You caching whole site but not only one file (index.php)

  • Donovan

    So what is the actual speed gain with this type of caching system?

  • Arnold Jose

    I just want to thanks, this is a usefull piece of code, it works for me, just a note I tried

    $file = basename( $_SERVER["SCRIPT_NAME"], ‘.php’ );

    as Julio Potier said but it didn’t work in mediatemple, it works with the original code.

    thanks for sharing.

  • okubax

    Thanks for this. It works and create a “cached-index.html” file on the first run, but on refrehing the page, it gives me an error “Parse error: syntax error, unexpected ‘version’ (T_STRING) in /home/****/public_html/cached-index.html on line 1

    Any help?

    • okubax

      Its was an error on one of the template files, now working perfectly. Many thanks

  • http://ruchie.com/ Alok Sharma

    Excellent trick.

    Just one thing. How to exclude a particular piece of code from being cached?

    For e.g. I have a rating code which also gets cached. When people are viewing the webpage, they are not able to rate because the entire code is cached. I have add the rating code using the include statement.

    Thanks in advance

  • Mittul Chauhan

    memcache is a good one .. but i like your thoughts as well .. looking forward to use this thing in future projects ..

    thanks.

  • Venky Kancharla

    i tried to add those files into my page but unfortunatly css styling lost…why…????

  • http://hispanomercadeo.com/ Lucas Calviño

    Thanks a lot!