Caching PHP objects: how to do it in 4 easy steps

This week, my work for our top secret web app, has mainly revolved around caching. This is work I really enjoy. I can sense the user experience of future customers improving each time I create a useful cache of information that speeds up a page’s load time.

Screenshots:
Setting remote folder permissions in Transmit (OS X FTP client)

How to do it

Caching is easy, the way I do may not even be the easiest but here goes:

Step 1:

Create a readable, writeable, executable folder (folder permissions set to 777) - preferably called ‘cache’ or similar. You should be able to do this by right-clicking on the file in your FTP client.

Step 2:

Put this function somewhere that will be called whenever you need to cache something:

  1. /*
  2. removeHours()
  3. return the date given but with the given number of hours removed
  4. */
  5. function removeHours($date, $hours_added){
  6. $total_seconds = 3600*$hours_added;// 3600 seconds in an hour
  7. $date = strtotime($date);
  8. $new_date = $date-$total_seconds;
  9. $new_date = date('Y\-m\-d H\:i\:s',$new_date);
  10. return $new_date;
  11. }

This returns the time/date minus however many hours you choose.

Step 3:

Create the actual cache file.

  1. /*
  2. cache example
  3. */
  4.  
  5. // cache filename variables
  6. $cache_filename = 'object_data.inc';
  7. $cachefile_full_filename = $_SERVER['DOCUMENT_ROOT'].'/cache/'.$cache_filename;
  8.  
  9. // check for cache, if it exists and is less than 1 hour old grab it
  10. if(file_exists($cachefile_full_filename) && filemtime($cachefile_full_filename) > strtotime(removeHours(date('Y-m-d H:i:s'), 1))){
  11. $object_data = unserialize(file_get_contents($cachefile_full_filename));
  12. } // end if
  13. // cache is missing or too old
  14. else{
  15. // Initialise object
  16. $object_data = new Object($page_filter, '');
  17. // Create the cache for future use
  18. file_put_contents($cachefile_full_filename, serialize($object_data));
  19. } // end else
  20.  
  21. // Use $object_data variables for whatever you want.

This code checks to see if a cache file exists and if it does, whether it’s time stamp is less than an hour old. If it is, the variable $object_data is populated with the cached object data.

If the cache file does not exist or if it is older than an hour, the code looks to initialise a new object and create a cache with that newly grabbed data.

Step 4:

Ensure, Joe Nobhead can’t see your cached file add a .htaccess to your ‘cache’ folder (or whatever you called it) like this:

  1. <FilesMatch "\.(inc)$">
  2. Order Allow,Deny
  3. Deny from all
  4. </FilesMatch>

This stops anyone just viewing files in the cache folder that end in .inc and people get a HTTP error for their troubles if they attempt to view it.

Why bother caching objects

Our top secret web app (if I keep referring to it as that, will people start to get interested in it, I wonder), makes a lot of identical database calls for each page load. If the data doesn’t change that often, that’s a waste of everyone’s time. It slows down users, it puts pressure on the MySQL database and it needs calming measures.

Implementing this caching technique has reduced the load times of pages by well over half in some cases. The beauty of it is, if data within the database is updated it is relatively easy to delete a file from the cache to ensure no data is out of date - just call:

@unlink($_SERVER['DOCUMENT_ROOT'].'/cache/object_data.inc');

After updating your database and delete the relevant cache file.

Drawbacks of this technique

The way this works so far means, one user will suffer the slow page and multiple database connections before the cache is built (or rebuilt every hour) - which isn’t really fair but every user after them and even themselves (if they refresh) will gain the benefits of a quicker site once the cache file has been built.

Disclaimer

Yes, I know you’re a much better coder than me and your caching technique is far superior to mine. Good, I’d like to hear about it.

If you think my implementation warrants improving from an efficiency or security point of view, I’d love to hear from you.

More caching sources

Share and Enjoy

These icons link to social bookmarking sites where readers can share and discover new web pages.

Tags:

Responses

There have been 15 responses to Caching PHP objects: how to do it in 4 easy steps.

  1. Comment made by Zinni on

    Thanks for the great tutorial, I am working on a new web app as well and as a designer have almost no idea what I am doing other than a basic knowledge of PHP. This tutorial was well written and easy to follow so thanks!

  2. Comment made by Daron on

    Umm, nice work, but wahts wrong with using Memcache?

    http://www.php.net/manual/en/intro.memcache.php

  3. Why the heck would you use a .inc extension ?!?! That makes no sense whatsoever.

    Also, your method won’t work if you run a site with logins. This caching method has been shown elsewhere. . and they all work okay for sites w/o logins.

  4. Comment made by Drew on

    I would also suggest two things.

    Instead of going through the hassle of the writable directory and the .htaccess file, just save it to the /tmp directory (I’m assuming you’re on a *nix server, since you use OS X. Might be a bad assumption). The /tmp directory should by default be writable and is in fact meant to hold temporary files. On top of that it is outside of your document root, so it is not accessible to the web directly.

    One other thing. I’ve been burned by strange encoding issues with serializing, caching and deserializing large object structures in PHP. I would strongly suggest you also base64 encode the data before writing it, then decode it after reading it. This will ensure that it is kept in a basic ascii character set. The downfall is that the file will consume a little more file space (25%-30%), but the added data security is worth it, in my opinion.

    One last note about using the /tmp directory. Be sure to either use a directory in there or use a common file prefix, so that you can quickly identify cache files for the particular site. This is helpful in case you ever want to do a full cache purge for one part of the /tmp directory but not the rest.

    I manage about 10 custom built sites for the company I work for and generally use cron jobs to launch scripts at certain times to do things like full cache flushes. Keeping the files namespaced helps me identify what to delete for one site, without affecting another.

    Also, check out this Memcached (http://www.danga.com/memcached/). It was built by the livejournal folks to handle their caching needs. It is extremely fast a scalable and works great with PHP.

    Good luck!

  5. Comment made by kamil.szot on

    It’s better to keep cache in database table or in memecached. It’s faster, and it can be moved easily to some additional server when it’s necessary.

  6. Comment made by phil on

    Thanks for everyone’s comments and suggestions. Drew, that is exactly what I was after.

    I’ll be sure to check out Memcached, as recommended, but I’m not sure it’ll be applicable to my particular situation.

    @Brant: The example I give can quite easily be changed to not use a ‘.inc’ extension, you could use anything you want.

    @Drew: I try to namespace my cache files for the exact reasons you mention. It’s easy to loop through a directory and then delete all files with that namespace if required. Sadly, this web app won’t be able to make use of cron jobs (in most cases) but otherwise it’s a good approach and I’ll definitely check into using the /tmp file as the repository.

  7. Good article. I’d be very interested to see some benchmarks that quantify the benefits of this caching method; are these available?

    Also, this is nitpicking somewhat, but the “removeHours” function could perhaps be considered redundant. The standard strtotime function will interpret relative time descriptions, so you could just write:

    strtotime(’-1 hour’);

    … instead of removeHours([date], 1). To specify the start date, you’d write strtotime(’-1 hour’,strtotime([date])) or similar.

    See: http://uk.php.net/manual/en/function.strtotime.php

  8. Comment made by Dan on

    The yellow… it’s burning out my eyes!

  9. Comment made by Dominik on

    @Daron, Drew and phil:

    Actually Memcache is not so fast after all. I did some benchmarks of various “caching techniques” and was quite surprised that using the hard-disk for caching was considerably (about 30%) faster for reading and about 600 times faster for writing (in a local machine/one machine environment).

    Either I did something terribly wrong with my benchmarks, or I’ll probably go with using a Filesystem RAM-Disk for caching (haven’t tried it’s performance, yet).

  10. Comment made by phil on

    @Chris Gibson: In terms of benchmarking all I’ve done is to use microtime() at the start of each page (on the website) and than at the end of each page convert that figure into the number of seconds it has taken to load the page.

    An average(ish) figure is that for the site homepage, the load time was around 0.24 seconds and with the caching in place it became around 0.06 seconds. It also reduced the number of MySQL queries from 77 to 7.

    It’s worth pointing out the the reason for all these (77) queries was a number of recursive functions used to work out menus and see if certain pages were children of other pages. I haven’t used the caching as a way to speed up objects but rather, as a way to reduce the number of times each page calls the database. I’m not sure how effective my method would be as a way to simply cache an object that makes no use of a database.

  11. Comment made by josh on

    Thanks for saving the internet a few microseconds

  12. Comment made by karuna on

    i like the article… but the yellow… hurts after a while but if you like it well go ahead.

  13. Comment made by enzo on

    fs cache sucks by default,
    try memory storage

  14. Comment made by Jeff on

    Nice article Phil. Joe Nobhead, haha!

    I’m gonna hazard a guess that this is some sort of content management system?

  15. Comment made by phil on

    @Jeff: I love your inquisitive mind and your educated guess is very warm but it isn’t quite correct. I’ll be posting more details about the top secret web app in the future.