ucantblamem

PHP: Self-served images

12th May 2007

Last weekend while working on the next version of the Order Posts wordpress plugin, I discovered a neat little solution to a trivial problem.

Most plugins in Wordpress are generally just single PHP files, making them super simple to install/upgrade. There is one problem of course, which is that if you do need to use images in your plugins, you generally have to make a folder for your plugin instead of just the single file. Thankfully wordpress doesn’t have any issue with this and is quite willing to drill down multiple levels of folder structure to grab your code, but it’s not incredibly elegant and definitely not very user friendly when it comes time for the non-techies to update a plugin.

So, back to what I was saying about working on my plugin; I wanted to replace the ‘+’ and ‘-’ buttons with something more meaningful, like up and down arrows. Simple! 5 minutes and 1 Kilobyte later I had two good looking gif’s and the unfortunate dilemma of messing up my one-file plugin.

Having done quite a bit of work manipulating images with PHP, I decided that the two half-Kb files could be justifiably embedded in the PHP file itself. So, how do you go about doing this? Well, I’m a bit of a trial and error type guy, so I just copied the contents of one of the gif’s into a string variable and hit refresh on the browser. Ummm… No, that didn’t work – the parser got upset by all the crazy characters; back to the drawing board.

About 5 minutes later I remembered my love-affair with base64 encoding and set about defining an encoded version of the string into my variable declaration. A quick refresh to check the parsing and all seemed okay. The next step was to actually turn this obscure looking string back into an image and serve it back to the browser when the right request variables were set.

What I ended up with was something along the lines of this:

<?php
$myimg = 'R0lGODlhWQANALMPAOGIiNhmZtx3d/vu7sIREeWZmdRVVe67u/bd3cszM+mqqvLMzM9ERMciIr4AAP///yH5BAEAAA8ALAAAAABZAA0AAAT/sLlHq2s150ul/mAojhzoPRJybhjZrmMsi+V3pjBVh2U+/7Nd5sSY+ISf3gTIDLZsyyJqyaoAJImD5UUdCCQNwUBTHD8UDkCF0Lo6CAEziqFoMB4lL+FSIBqpWxQGDgIFCQ4LOlwUA4cGAIN3GQAOCoIOCRQLhA9FDJAXZg1YAngtngABe1F/Dq6vHAdpjASSShQCDgUVuWoVCA4BFGwOY5QHBZwUyaVTzaYPlMIUsksGBFMB2tscAWkF4A3Y0FMdgMCSFaMPssm7DBhFCFVTchzyQ0sBGEfxsK+KzAWkx8sBgn1zBgQrl09goBwnEPZ7MEgOvRvnHKSjpivMg1xocLTgC8Qw4CGLJxAkmpjMl4ZbH2dR8LZLA4FDWmQlGCfNijIYHCg9q6Zp18ROGgFA0kLuxAAJjzyBGPRkj4EKqAYlEAWIw9Ok3vYMq3TUi4RaTGE+GKDqgksNaJ55s8ToywUx6rq2YLsngQJvggggiAAAOw==';if (isset($_GET['image']) && !empty($_GET['image'])) {
if (isset($_GET['image']) && !empty($_GET['image'])) {
 header(’Content-type: image/gif’);
 echo base64_decode($myimg);
 exit;
}
?>
<img src=”<?php echo $_SERVER['PHP_SELF']; ?>?image=example” alt=”An example of a self-served image” />

We set a variable with a (longish) base64 encoded string and if the ‘image’ request variable is set in the query string (of the URL) we echo out an un-encoded version of the string, sending the correct headers and then exiting immediately after so that we never get to the actual HTML of the file.

It’s a pretty simple idea and works a treat in my plugin. To really prototype this out in a real working app, I put together a little something we’ll call “Self-servedâ€?. It’s a little app that helps you to create base64 encoded strings and files from a string or file. The app itself is a single *.php file which serves its’ own images and makes use of some MooTools goodness (as always).

There are some downfalls to this technique of course:

  1. You shouldn’t use this for large images or even large app’s, wherever possible you should be using a good level of abstraction and keeping things separated out for maintainabilities’ sake; Looking through the code of my example isn’t pretty I assure you.
  2. Your single file is going to get large very quickly. The selfserved.php file in this example works out to be about 84Kb’s!
  3. I haven’t taken the time to performance test this technique, but they’re massive strings, so I’m guessing you’re putting the server under a substantial amount of unnecessary pressure to do things this way (if you were to do this on a high-traffic site) – however for our little app and my plugin, the performance loss is neglegable because this script doesn’t need to scale in any way, shape or form.

Here’s the app in case you missed the link.

Leave a Reply