Displaying A Random Image From A Directory Via PHP

Recently asked on Yahoo! Answers:

How would one code a rotating image (using PHP) that can be hosted on Geocities Pro for, say, a forum?

In the past, I’ve been able to host a rotating image using another method by naming a folder ’something.png’ to fool a forum into thinking the folder is a picture and it reads the index.php file, which in turn pulls up one of the images I have coded. Now that I’m on Geocities Pro, I cannot make such a folder but would like to still use my rotating image. Is there another way I can code this and still be able to use it with forum BBCode?

Basically, this questioner wants a PHP script to look through the contents of an image directory, randomly select one of the images in that directory, and pass the script itself off as the image.

That’s fairly simple to do in PHP, but it does require a number of steps and a few tricks to overcome some limitations of PHP’s built-in filesystem functions.

This question also gives me a chance to explain working with the filesystem in PHP, and to demonstrate how loops and arrays can be used to produce some really neat results, so I’ll expand on that in a follow-up post.

As always, a working demo and source code will be available for download at the end of this article.

Step 1: Declare Basic Variables

We’ll start by declaring two variables:

  • The path, relative to the script, of the folder containing the images we want.
  • A regular expression that gives us the allowed extensions for our images.

For those who are new to the term, a regular expression is basically a text pattern you want to find. It uses special characters and syntax to indicate what you want to find; in our case, we’re going to search for file extensions.

Don’t worry if you don’t understand the expression; trust me that this one will allow you to use image files with the extension .jpg, .jpeg, .gif or .png; the test will also be case-insensitive, so .JPG is the same thing as .jpg.

More on all that later. Let’s look at the opening code.

$img_path = "images/";
$exts = "/(.jpg)|(.jpeg)|(.gif)|(.png)$/i";

Step 2: Open The Directory And Iterate Through The Files

Our next step is to make sure the image directory we declared actually exists. If it does, we want to go ahead and open the directory, then begin looking through the files.

An aside on the difference between evaluating equivalence vs. identity: Notice, in the code below, that we use the identity operator (===), and not the equivalence operator (==), to test whether we have read all the files in the directory.

We do that because, as the PHP documentation notes, readdir() may not return a Boolean “false,” but may return another value that is the same as false.

The identity operator allows us to compare two variables not on the basis of them having the same value (equivalence), but being the same thing.

If we just use the equivalence operator, and readdir() returns a value that means the same as Boolean false, that won’t be equal to Boolean false; our loop would continue and we’d get an error. If we use the identity operator, we know that whatever false value readdir() actually returns, it means the same, for all intents and purposes, as Boolean false; our loop will stop executing. (End of aside.)

if(is_dir($img_path)) {
	//open directory
	$img_handle = opendir($img_path);

	//iterate through all items in directory
	while (false !== ($img_file = readdir($img_handle))) {

Step 3: Put All Image Files Into An Array

We’re going to read the names of all image files into a dynamic array, the reasons for which will be clearer in a moment. For now, you’ll notice we use preg_match, the preferred method for running a regular expression match against a string, to make sure that the file we’re currently looking has an extension that’s in our allowable extensions list.

An added bonus of this approach is that it resolves problems with subdirectories in PHP.

Basically, in PHP, if you are iterating through a directory and it has subdirectories, the built-in functions in PHP will treat all subdirectories as files; you can’t even evaluate if the subdirectory is a directory.

Needless to say, that’s a major problem, and the only effective way to work around it, via the filesystem, is to read each “file,” then see if trying to enter it, as if it were a directory, works. If so, you’ve found a subdirectory, not a file.

That’s pretty wasteful, in my opinion, and possibly dangerous. It makes much more sense, and is far less resource-intensive, to simply look at the name of the file and see if it matches the kind of file we want.

Note that not all Web hosts will have compiled support for Perl-Compatible Regular Expressions (PCRE). Virtually all do, but yours may not have; if not, this script won’t be of much use to you.

Lastly, once we’re done looping through the files in the directory, we want to close the directory to free up the considerable resources PHP uses to work with the filesystem.

if (preg_match($exts, $img_file)) {
	$img[] = $img_file;
}

closedir($img_handle);

Step 4: Select The Random Image Via A Random Number

Here’s the magic: Since we’ve dumped all the file names into an array, all we need to do is randomly generate an index number, and pull out from the array the name of the file that is at that index.

So, our task list for this step is:

  • Initialize our random number generator;
  • Get the total number of items in the array and subtract one, which will give us the index of the last item in the array. If there are no items in the array, we’ll die and report that fact;
  • Generate a random number between 0 and the integer we get from the step above, which gives us a random index for the array;
  • Append the file path to the array cell contents at the index we generated, which gives us a full file path to one of the images in the directory.
mt_srand();

if(count($img) == 0) {
	die("no images in image directory");
}
else {
	$len = count($img) - 1;
}

$index = mt_rand(0, $len);
$img_src = $img_path . $img[$index];

Step 5: Get The Content-Type

The way we’re going to make this PHP script act like an image is by forging the HTTP header’s content-type value. Instead of outputting “text/html”, which Web pages send, we’ll send one of the image types, depending on our file’s extension.

So first, we need to get the extension from our file.

  • We set the basic part of the content-type string as a variable.
  • We split the file’s name into an array, around the periods.
  • We then remove the last item of the array, which will contain our extension. We know that because by virtue of the regular expression we used to sort files earlier, we know all files will end with one of our four image file extensions.
  • We cast the extension down to lower-case, to make sure we get a case-insensitive match.
  • We run the extension through a switch that assumes the file is a JPEG, but changes the content-type to GIF or PNG if that’s what we actually have.
$ct = "Content-type: image/";

$temp = explode(".", $img_src);
$temp = array_pop($temp);
$temp = strtolower($temp);

switch($temp) {
	case "png":
		$ct .= "png";
		break;
	case "gif":
		$ct .= "gif";
		break;
	default:
		$ct .= "jpeg";
}

Step 6: Echo Out The Forged Header And The Image

Outputting our image is as simple as sending the forged header (via the cleverly-named header() function) and then dumping the image file’s contents directly to the output buffer via readfile().

header($ct);
readfile($img_src);

And, as a final step, we’ll add an else statement to the opening if statement; if the path we specified in Step 1 doesn’t lead to a directory, we’ll die the script and report that.

else {
	die("no such directory!");
}

And that’s all there is to it. You can see the script in action here:

http://www.dougv.com/demo/php_random_image/index.php

Just reload the page to see a different image.

A final aside on randomness: When selecting a random number, the larger the range, the more random the number. Thus, the more images that act as a source for this script, the more random your results. I’m using 15 images. (End of aside.)

You can download the source code here:

Displaying A Random Image From A Directory Via PHP Source Code

I distribute all code under the latest version of the Creative Commons Attribution / Share-Alike License.


One Response to “Displaying A Random Image From A Directory Via PHP

Leave a Reply