This seems like a great time to subscribe my RSS !

After going through PhpThumb hell, I decided to develop one serious decent caching library for caching dynamically generated images.

Download

 

Download zipped source from Google Code

Or browse svn repository

Download source from svn using: svn checkout http://xdump.googlecode.com/svn/trunk/ xdump

Brief story

One of our previous sites (about 50k page-views per day) was using a little php script for resizing dinamically images to thumbnail size. Since we didn’t knew in advance the size we was gonna need, resize was made at run-time.

With site’s restyling the number of resized images on every page increased sensibly. As it’s easy to guess server load went sky-high. We tried to replace the package with PhpThumb but the situation got even worse (yes, worse was possible).

What’s wrong with PhpThumb

I have some problems with PhpThumb. Oh yes, I do.

  • It’s bloated. Even if PhpThumb is a class, the caching routines are written in a procedural file who reads $_GET superglobal to decide what to do. This means I had to simulate HTTP request by emptying manually $_GET and putting there only values PhpThumb needs. You don’t pass through that file.. you don’t cache.

  • Cache working.

    • Does it work? I couldn’t verify, the only thing I know is that the cache directory kept becoming bigger, with apparently no limit. Actually, it became so big to cause server load by itself.

    • I tried disabling cache in the config. Guess what? It kept caching, yes it did! I had to physically delete the cache path to block caching.

    • Cached file retrieving. It’s made using readfile() or header redirects. While that’s better than nothing I think we can do better, much better.

Strategy

The solution I choose is what some call generate on request. This trick consists in creating a dynamic script, making it the default Apache’s ErrorDocument for the current directory and let it handle the content generation.

The flow:

  1. Assume we store all downloadable wallpapers on our site are stored into /image/wallpapers directory.

  2. The website’s page will contain a non existant image path. Say something like: http://example.com/dynamic/40×30/images/wallpapers/britney.jpg

  3. At the time of the first visit to the page the resized image will not be there yet.

  4. When the image requests hits Apache, it will be redirected to our custom ErrorDocument (front_controller.php).

  5. front_controller.php will take care to read /images/wallpapers/britney.jpg generate our 40×30 nice thumbnail at /dynamic/40×30/images/wallpapers/britney.jpg path.

  6. Afterwars the user browser will be redirected to the generated image, making it appear on the page.

  7. Every other request to the page will find the image already at the path requested, without needing PHP to do anything.

The Code

Here’s some code boxes to let you know how code looks like.

.htaccess

The only purpose of .htaccess here is to instruct to consider our Front Controller the default error page for the generated images path.

The first time an image is requested the Front Controller manages to create it at the requested path, and to redirect the user to it.

The following requests will find the image already waiting for them. This means the file will be served by Apache itself with no php intermediation. This can lead to performance improvement around the order of magnitude.

ErrorDocument 404 /dynamic/front_controller.php

Front Controller

The purpouse of this file is to include libs, instantiate main class, decide which drivers are to be used and make thing run.

include ( '_libs/logger.php' );
include ( '_libs/cache_on_request.php' );

$FrontController = new GenerateOnRequest();

//decomment the following 2 lines only for debug purpouse
$FrontController->configuration['debug']=true; //prints logs (and hinibits header final)
//$FrontController->configuration['stopGeneration']=true; //checked by drivers, hinibits concrete file generation

//attacching drivers
$FrontController->attachDriver( new ResetController );
$FrontController->attachDriver( new GdImageResizer );
$FrontController->run( $_SERVER[ 'REQUEST_URI' ], dirname(__FILE__ ) );

,

1 Response

  1. Adam

    Very cool. This will come in quite handy. Thank you!

Leave a comment