Options

PHP Help: Caching images/stopping client-side loading?

MugenmidgetMugenmidget Registered User regular
For a website I'm part of we're using a modified image rotator PHP script to display our header banners for the day. Right now, it's functional but the image loads constantly. Is there a way to cache the image somehow after it's loaded once for a day? Here's the code right now:
<?php

/*

	AUTOMATIC IMAGE ROTATOR
	Version 2.2 - December 4, 2003
	Copyright (c) 2002-2003 Dan P. Benjamin, Automatic, Ltd.
	All Rights Reserved.

	http://www.hiveware.com/imagerotator.php
	
	http://www.automaticlabs.com/
	
	
	DISCLAIMER
	Automatic, Ltd. makes no representations or warranties about
	the suitability of the software, either express or
	implied, including but not limited to the implied
	warranties of merchantability, fitness for a particular
	purpose, or non-infringement. Dan P. Benjamin and Automatic, Ltd.
	shall not be liable for any damages suffered by licensee
	as a result of using, modifying or distributing this
	software or its derivatives.
	
	
	ABOUT
	This PHP script will randomly select an image file from a
	folder of images on your webserver.  You can then link to it
	as you would any standard image file and you'll see a random
	image each time you reload.
	
	When you want to add or remove images from the rotation-pool,
	just add or remove them from the image rotation folder.


	VERSION CHANGES
	Version 1.0
		- Release version
	
	Version 1.5
		- Tweaked a few boring bugs
	
	Version 2.0
		- Complete rewrite from the ground-up
		- Made it clearer where to make modifications
		- Made it easier to specify/change the rotation-folder
		- Made it easier to specify/change supported image types
		- Wrote better instructions and info (you're them reading now)
		- Significant speed improvements
		- More error checking
		- Cleaner code (albeit more PHP-specific)
		- Better/faster random number generation and file-type parsing
		- Added a feature where the image to display can be specified
		- Added a cool feature where, if an error occurs (such as no
		  images being found in the specified folder) *and* you're
		  lucky enough to have the GD libraries compiled into PHP on
		  your webserver, we generate a replacement "error image" on
		  the fly.
		
    Version 2.1
        - Updated a potential security flaw when value-matching
          filenames

    Version 2.2
        - Updated a few more potential security issues
        - Optimized the code a bit.
        - Expanded the doc for adding new mime/image types.

        Thanks to faithful ALA reader Justin Greer for
        lots of good tips and solid code contribution!


	INSTRUCTIONS
	1. Modify the $folder setting in the configuration section below.
	2. Add image types if needed (most users can ignore that part).
	3. Upload this file (rotate.php) to your webserver.  I recommend
	   uploading it to the same folder as your images.
	4. Link to the file as you would any normal image file, like this:

			<img src="http://example.com/rotate.php">

	5. You can also specify the image to display like this:

			<img src="http://example.com/rotate.php?img=gorilla.jpg">
		
		This would specify that an image named "gorilla.jpg" located
		in the image-rotation folder should be displayed.
	
	That's it, you're done.

*/




/* ------------------------- CONFIGURATION -----------------------


	Set $folder to the full path to the location of your images.
	For example: $folder = '/user/me/example.com/images/';
	If the rotate.php file will be in the same folder as your
	images then you should leave it set to $folder = '.';

*/


	$folder = '.';


/*	

	Most users can safely ignore this part.  If you're a programmer,
	keep reading, if not, you're done.  Go get some coffee.

    If you'd like to enable additional image types other than
	gif, jpg, and png, add a duplicate line to the section below
	for the new image type.
	
	Add the new file-type, single-quoted, inside brackets.
	
	Add the mime-type to be sent to the browser, also single-quoted,
	after the equal sign.
	
	For example:
	
	PDF Files:

		$extList['pdf'] = 'application/pdf';
	
    CSS Files:

        $extList['css'] = 'text/css';

    You can even serve up random HTML files:

	    $extList['html'] = 'text/html';
	    $extList['htm'] = 'text/html';

    Just be sure your mime-type definition is correct!

*/

    $extList = array();
	$extList['gif'] = 'image/gif';
	$extList['jpg'] = 'image/jpeg';
	//$extList['jpeg'] = 'image/jpeg';
	$extList['png'] = 'image/png';
	

// You don't need to edit anything after this point.


// --------------------- END CONFIGURATION -----------------------

$img = null;

if (substr($folder,-1) != '/') {
	$folder = $folder.'/';
}

if (isset($_GET['img'])) {
	$imageInfo = pathinfo($_GET['img']);
	if (
	    isset( $extList[ strtolower( $imageInfo['extension'] ) ] ) &&
        file_exists( $folder.$imageInfo['basename'] )
    ) {
		$img = $folder.$imageInfo['basename'];
	}
} else {
	$fileList = array();
	$handle = opendir($folder);
	while ( false !== ( $file = readdir($handle) ) ) {
		$file_info = pathinfo($file);
		if (
		    isset( $extList[ strtolower( $file_info['extension'] ) ] )
		) {
			$fileList[] = $file;
		}
	}
	closedir($handle);

	if (count($fileList) > 0) {
		sort($fileList);
		//$imageNumber = date("j");
		$img = $folder.$fileList[date("j", mktime(date("G")-5, 0, 0, date("m")  , date("d"), date("Y")))];
		//echo $folder.$fileList[date("j")];
		
	}
}

if ($img!=null) {
	$imageInfo = pathinfo($img);
	$contentType = 'Content-type: '.$extList[ $imageInfo['extension'] ];
	header ($contentType);
	readfile($img);
} else {
	if ( function_exists('imagecreate') ) {
		header ("Content-type: image/png");
		$im = @imagecreate (100, 100)
		    or die ("Cannot initialize new GD image stream");
		$background_color = imagecolorallocate ($im, 255, 255, 255);
		$text_color = imagecolorallocate ($im, 0,0,0);
		imagestring ($im, 2, 5, 5,  "IMAGE ERROR", $text_color);
		imagepng ($im);
		imagedestroy($im);
	}
}

?>

Any help would be great.

e3ORbfq.png
Mugenmidget on

Posts

  • Options
    RandomEngyRandomEngy Registered User regular
    edited July 2008
    Hmm, might try adding an Expires header that indicates the image expires at the end of the day.

    RandomEngy on
    Profile -> Signature Settings -> Hide signatures always. Then you don't have to read this worthless text anymore.
  • Options
    Woot427Woot427 Registered User regular
    edited July 2008
    RandomEngy wrote: »
    Hmm, might try adding an Expires header that indicates the image expires at the end of the day.

    I believe he may mean how PHP is having to recreate the image everytime the image is called. It's unnecessary processor use when the image could just be cached once seeing as it's not truly a rotator because it just returns the same image each time.

    What I would do is instead have a php script run every morning, at midnight, whatever, to save the image to a file that you then edit the expires header for to make sure that people get the new image the next day but PHP is only involved in creating the image once. Before I go into anymore detail, this is what you mean, right?

    Woot427 on
  • Options
    EchoEcho ski-bap ba-dapModerator mod
    edited July 2008
    Have a look at ETags.

    edit: better explanation.

    Echo on
  • Options
    JaninJanin Registered User regular
    edited July 2008
    Wow, that file is so...PHP.

    Setting an Expires head is the solution to your problem. Set it after calling header() for the content type. Here's a page for setting Expires from PHP.

    Also consider checking for a last-modified header from the client and sending back a 304 response if needed.
    Woot427 wrote: »
    RandomEngy wrote: »
    Hmm, might try adding an Expires header that indicates the image expires at the end of the day.

    I believe he may mean how PHP is having to recreate the image everytime the image is called. It's unnecessary processor use when the image could just be cached once seeing as it's not truly a rotator because it just returns the same image each time.

    What I would do is instead have a php script run every morning, at midnight, whatever, to save the image to a file that you then edit the expires header for to make sure that people get the new image the next day but PHP is only involved in creating the image once. Before I go into anymore detail, this is what you mean, right?

    It's not generating the image, it's calling readfile(). CPU usage with this script is not likely to be a worry.

    Janin on
    [SIGPIC][/SIGPIC]
  • Options
    Woot427Woot427 Registered User regular
    edited July 2008
    Ah, I thought it was dynamically creating an image, should have paid more attention, but wow, that's a lot of lines for what it does.

    Woot427 on
  • Options
    EchoEcho ski-bap ba-dapModerator mod
    edited July 2008
    Yeah, sending the image data isn't the best idea. When I write image rotators I make them redirect to the image URL, which lets the server and browser deal with the caching.

    Echo on
  • Options
    EchoEcho ski-bap ba-dapModerator mod
    edited July 2008
    Ha, seems the rotator I had lying around is actually the same code, but I changed it to redirect instead of streaming the image data.
    <?php
    
    
    	$folder = './pics/';
    
        $extList = array();
    	$extList['gif'] = 'image/gif';
    	$extList['jpg'] = 'image/jpeg';
    	$extList['jpeg'] = 'image/jpeg';
    	$extList['png'] = 'image/png';
    
    
    // You don't need to edit anything after this point.
    
    
    // --------------------- END CONFIGURATION -----------------------
    
    $img = null;
    
    if (substr($folder,-1) != '/') {
    	$folder = $folder.'/';
    }
    
    if (isset($_GET['img'])) {
    	$imageInfo = pathinfo($_GET['img']);
    	if (
    	    isset( $extList[ strtolower( $imageInfo['extension'] ) ] ) &&
            file_exists( $folder.$imageInfo['basename'] )
        ) {
    		$img = $folder.$imageInfo['basename'];
    	}
    } else {
    	$fileList = array();
    	$handle = opendir($folder);
    	while ( false !== ( $file = readdir($handle) ) ) {
    		$file_info = pathinfo($file);
    		if (
    		    isset( $extList[ strtolower( $file_info['extension'] ) ] )
    		) {
    			$fileList[] = $file;
    		}
    	}
    	closedir($handle);
    
    	if (count($fileList) > 0) {
    		$imageNumber = time() &#37; count($fileList);
    		$img = $folder.$fileList[$imageNumber];
    	}
    }
    
    if ($img != null) {
    	$imageInfo = pathinfo($img);
    [COLOR="DarkOrange"]	header("Location: " . $folder . $imageInfo['basename']);
    
    //	$contentType = 'Content-type: '.$extList[ $imageInfo['extension'] ];
    //	header ($contentType);
    //	readfile($img);[/COLOR]
    } else {
    	if ( function_exists('imagecreate') ) {
    		header ("Content-type: image/png");
    		$im = @imagecreate (100, 100)
    		    or die ("Cannot initialize new GD image stream");
    		$background_color = imagecolorallocate ($im, 255, 255, 255);
    		$text_color = imagecolorallocate ($im, 0,0,0);
    		imagestring ($im, 2, 5, 5,  "IMAGE ERROR", $text_color);
    		imagepng ($im);
    		imagedestroy($im);
    	}
    }
    
    ?>
    
    

    edit: highlighted the actual changes.

    Echo on
  • Options
    MugenmidgetMugenmidget Registered User regular
    edited July 2008
    Oh, that's a REALLY COOL and simple fix, thanks a bunch! :-)

    Mugenmidget on
    e3ORbfq.png
  • Options
    MugenmidgetMugenmidget Registered User regular
    edited July 2008
    If nobody minds I have another similar piece of code that suffers the same problem because of its usage of the readfile function, but it might be the only simple way to do it (if you couldn't tell already I really don't know).

    The idea here is to have 6 thumbnails created from the most recently modified images in a folder, and with Echo's referral code in place the thumbnails won't update until you force a refresh. That's really not all that bad, but it would be cool if the code could somehow be smart enough to recognize new pictures and repost the thumbnails accordingly. Maybe the code could record the last known date and compare it to the most recent file or something like that? Count all the files on each page load and only update the thumbnails if that number changes? I don't know what the most elegant solution is, unfortunately. Here's the code, though (it'll look VERY similar, except now this one recognizes files by "last modified" and has an offset):
    <?php
    
    
    
    	$folder = '.';
    	$offset = $_GET['offset'];
    
        $extList = array();
    	$extList['gif'] = 'image/gif';
    	$extList['jpg'] = 'image/jpeg';
    	//$extList['jpeg'] = 'image/jpeg';
    	$extList['png'] = 'image/png';
    	
    
    $img = null;
    
    if (substr($folder,-1) != '/') {
    	$folder = $folder.'/';
    }
    
    if (isset($_GET['img'])) {
    	$imageInfo = pathinfo($_GET['img']);
    	if (
    	    isset( $extList[ strtolower( $imageInfo['extension'] ) ] ) &&
            file_exists( $folder.$imageInfo['basename'] )
        ) {
    		$img = $folder.$imageInfo['basename'];
    	}
    } else {
    	$fileList = array();
    	$handle = opendir($folder);
    	while ( false !== ( $file = readdir($handle) ) ) {
    		$file_info = pathinfo($file);
    		if (
    		    isset( $extList[ strtolower( $file_info['extension'] ) ] )
    		) {
    			$fileList[] = $file;
    		}
    	}
    	foreach ($fileList as $value){
    
          $timelist[$value] = date ("mdYHis", filemtime($value));
    	asort($timelist);
    	//array_reverse($timelist);
    }
    	closedir($handle);
    	$lastnumber = (count($timelist)-1);
    
    	if (count($timelist) > 0) {
    	//	natsort($timelist);
    		//$imageNumber = date("j");
    		$img = $folder.KeyName($timelist, ($lastnumber-$offset));
    		//echo $folder.key($timelist);
    		
    	}
    }
    
    if ($img!=null) {
    	$imageInfo = pathinfo($img);
    	$contentType = 'Content-type: '.$extList[ $imageInfo['extension'] ];
    	header ($contentType);
    	readfile($img);
    } else {
    	if ( function_exists('imagecreate') ) {
    		header ("Content-type: image/png");
    		$im = @imagecreate (100, 100)
    		    or die ("Cannot initialize new GD image stream");
    		$background_color = imagecolorallocate ($im, 255, 255, 255);
    		$text_color = imagecolorallocate ($im, 0,0,0);
    		imagestring ($im, 2, 5, 5,  "IMAGE ERROR", $text_color);
    		imagepng ($im);
    		imagedestroy($im);
    	}
    }
    
    function KeyName($myArray,$pos) {
       // $pos--;
       /* uncomment the above line if you */
       /* prefer position to start from 1 */
    
       if ( ($pos < 0) || ( $pos >= count($myArray) ) )
             return "NULL";  // set this any way you like
    
       reset($myArray);
       for($i = 0;$i < $pos; $i++) next($myArray);
    
       return key($myArray);
    }
    
    ?>
    

    Something tells me in order to get this to work without incessant reloading I'd need a whole different solution which might be more help than someone is willing to lend, but if the solution comes to you easily enough then I'd definitely appreciate it.

    Mugenmidget on
    e3ORbfq.png
  • Options
    RandomEngyRandomEngy Registered User regular
    edited July 2008
    One approach you could take would be to keep up-to-date thumbnails of every image, then you'll always have any thumbnail you want. If you have code that controls what images get into the folder, you can create the thumbnail there, otherwise you can check to make sure your thumbnails are up to date on each request. Then getting thumbnails for the 6 most recently modified images would be easy.

    RandomEngy on
    Profile -> Signature Settings -> Hide signatures always. Then you don't have to read this worthless text anymore.
Sign In or Register to comment.