Implementing an image cache for PHP GD

 
Published on 2004-12-24 by John Collins.

Introduction

One potential issue when GD is used in web applications is that it can be quite resource intensive, especially for complex graphics that require a lot of mathematically calculations, or for sites that experience heavy traffic. This is particularly problematic for shared commercial web servers, where your web site may be sitting on the same physical server as several dozen other web sites.

One way to ease the load on your server when GD is used is to implement an image cache. The idea is to firstly check the cache for the image that the user is requesting, and if it exists there, then send the cached image, otherwise use GD to draw the image and incur the associated processing cost.

The main advantage of such a caching policy is that commonly requested images that do not change very often will only have to be drawn once using GD, then stored in the image cache and all subsequent requests for that image will receive the cached version; so GD usage is kept to a minimum to improve performance.

When to use a cache

If your GD graphics are very dynamic in nature, for example charts for an up to the minute poll, then using a cache would not be appropriate. Where images are relatively static in nature a cache is ideal. For example, this web site uses GD to re-sample the logo on the homepage based on the resolution of the user's monitor. Only one original copy of the image file, logo.pnp, needs to be used as GD will do all of the work in scaling and re-sampling the image for the various resolutions.

We can safely assume that there are a finite amount of screen resolutions in use today, with the popular ones for this site being 1024x768 (52%), 1152x864 (18%) and 800x600 (15%). Therefore, GD is being used to generate the same size logo over and over again, which is wasteful.

Building a cache

Here is the code that is used to maintain the image cache for the logo of this site. A folder named image-cache was created on the server to store the different image logo files: remember that as we will be creating new image files in this folder, our script must have the necessary write file permissions on that directory to allow it to create the new image files.

<?php
// set the PNG header type for the request
header("Content-Type: image/png");
 
// see if a width and height for the image have been provided in GET VARS
// if not, use some defaults
if ($_GET["logo_width"]){
    $width = $_GET["logo_width"];
    $height = $_GET["logo_height"];
}else{
    $width = 466;
    $height = 100;
}
 
// now check if an image created for of these dimensions
// is in the image cache
// if not, then we will have to make a new one
 
$image_path = "../image-cache/logo_".$width."x".$height.".png";
 
if (file_exists($image_path)) {
    // send the cached image to the output buffer (browser)
    readfile($image_path);
}else{
    // create a new image using GD functions
 
    // the original PNG logo image
    $image = ImageCreateFromPNG('logo.png');
    // the new image we will create with GD to the new dimensions
    $new_image = ImageCreateTrueColor($width,$height);
 
    // copy and re-sample the original image to make the new one
    ImageCopyResampled($new_image, $image, 0, 0, 0, 0, $width, $height, 466, 100);
 
    // send the new image to the browser
    ImagePNG($new_image);
    // now save a copy of the new image to the cache directory
    ImagePNG($new_image, $image_path);
 
    // destroy the image in memory to free up server resources
    ImageDestroy($new_image);
}
 
?>

As you can see in the code above, the image files created in the image cache directory are incorporating the resized image dimensions in their file names, which allows use to check for their existence later. After hitting on the page containing this script a number of times using different resolutions, the contents of the image-cache directory should look something like this:

logo_364x78.png
logo_466x100.png
logo_728x156.png
...

The naming convention in use leaves no room for ambiguity: from a glance we can see the name of the original source image and the dimensions of the resized images.

Conclusion

If you read my previous article on generating graphics with GD, you will have already come across many of the GD functions used in the script above. For those new ones introduced, I refer you to the PHP manual, but hopefully you should find the code is pretty self explanatory. While an image cache is not appropriate for every situation, for those where it is appropriate it offers significant performance benefits, for very little additional code.


Updated 2021 : note that the above post is out-of-date, given this post was originally published in 2004, but is left here for archival purposes.