Note: The exercise files are free to all members. The code is commented to enhance your learning, but you will need database connectivity for some files to run as intended.
Skill Level Intermediate
- [David] Hi, I'm David Powers and welcome to this week's edition of PHP Tips and Tricks designed to help you become a smarter, more productive PHP developer Choose Cheese. Well, tempting though that prospective is the focus this week is on these icons of national flags of various countries. At the moment they're separate images and each one requires a separate request to the server. To reduce the load on the server they could be combined into a CSS sprite but that involves troublesome calculations to get the correct background position.
The alternative is to use what's known as a data URI. This involves encoding the graphic data for each image as a string ready to embed it in either a style sheet or into HTML. Using PHP to batch process images to data URIs is quick and easy, so that's what we'll do this week. The format of a data URI is simple. It begins with data followed by a colon. That's followed by the MIME type and a semicolon.
So, if it's a JPEG, the MIME type is image/jpeg. Next comes the encoding which is base64 and a comma and finally the image data encoded as base64. PHP has a built-in function to encode data as base64, so all you need to do is to find the MIME type of each image and build the data URI string following this format. So, let's take a look at the code.
In this file, I've defined a function called images2URI that's capable of batch processing An array of images and convert them to data URIs. The first argument images is an array of path names of the images to be processed. OutputFile is the name of the file where the data URIs will be created and the third argument overwrite sets the write mode of the output file. By default it's false making it optional. This means that data URIs will be appended to the end of the file rather than overwriting existing ones.
We'll begin by initializing an empty array for errors. Then we set the write mode for the output file. If overwrite has been set to true, the mode is set to W which deletes any existing content in the output file. Otherwise it's set to A which appends new content and that'll be the default behavior for this function. Next, the output file is open for writing and PHP will create the file if it doesn't already exist.
Before each loop that begins on line 10, processes each elements in the images array. The loop begins by getting the current file's MIME type using getImageSize and array D referencing. On line 14, OK is an array of acceptable MIME types. We're accepting gif, JPEG and PNG. If the MIME type is in the OK array, we get the contents of the current image file and assign it to data.
Then on line 20 we begin the output. We start off by getting the file name and concatenating onto the end of that a line break, a string of plus signs and another line break. This simply enables us to identify the image in the output file and by the way, PHP_EOL is a PHP constant that inserts the correct type of line break for the server's operating system. It's on line 22 that the real business takes place.
We concatenate onto the end of output the data URI. So, it's data followed by a colon, the MIME type followed by a semicolon, base64 followed by a comma and then we pass the data to base64 in code built-in PHP function that encodes it as base64. The loop then writes the data URI for the current files of the output file followed by two line breaks.
The else block adds the current file name to the error's array if it's not one of the acceptable MIME types and when the loop comes to an end, we close the output file and the function displays processing complete. If there are any errors, the errors array is displayed as a comma separated string and that completes the function definition. So, let's test this. So, it's images2URI.
Then we need an array. I'm going to have the first two images here, france.gif and germany.png. They're both in the images folder, so we need to add that to our path. And then I'm going to have the output file. I'll call that data_uris.txt. Then we'll just save that and run this script by loading the file into a browser, processing complete.
Let's go back to the editing program and there is data_uris.txt that has been created. If we just open that, there at the top france.gif followed by the string of plus signs and then we've got the data URI. Now, it looks as though there are line breaks in there but if you look at the line number in the gutter here, it's actually all on one line and everything needs to be on one line, no line breaks to be able to embed it into CSS and I think you can probably understand now why I've added this identification here because there's absolutely no way that you'd be able to understand from the data URI what the image contains.
So, if I just click to the side there and scroll down to select and we need to make sure we get everything right at the end. Just copy that to my clipboard, then go to the style sheet and here instead of images/france.gif, we'll just delete that, it goes inside this URL function. We need a pair of quotes and then just paste everything in there. It's very long but it will work.
Just save that and if we go back to the browser, add our data URIs, refresh the page and there is that france.gif now being displayed using the data URI, so there's absolutely no difference whatsoever but the big difference is that it no longer requires a separate request to the server. It's now embedded in the style sheet. So, if I were to convert the other images to data URIs and add them to the style sheet, instead of having five requests to the server, we'd have just the one.
So, this can be really useful in reducing the load on your server but the technique is best used only for smaller images such as icons because the browser needs to pass the whole style sheet before it can render the page but it's certainly a lot easier than loading icons as a CSS sprite than having to work out the background position of each one and now you've got a useful utility function to batch process gifs, JPEGs and PNGs to data URIs. If you're wondering why I haven't included SVG among the acceptable types of images, it's because encoding SVG as base64 results in much bigger files.
You need a different approach for SVG data URIs and we'll look at that in the next edition of PHP Tips and Tricks. Until then, this is David Powers thanking you for watching.