Tuesday, November 3, 2009

Section 9.6. Dynamically Generated Buttons










9.6. Dynamically Generated Buttons






A popular use for dynamically generated images is to create images for buttons on the fly (this was introduced to you in Chapter 1 as well). Normally, a blank button background image is used and text is overlaid on top of it, as shown in Example 9-8.


Example 9-8. Creating a dynamic button



<?php
$font = 'times';
if (!$size) $size = 12;
$im = ImageCreateFromPNG('button.png');
// calculate position of text
$tsize = ImageTTFBBox($size,0,$font,$text);
$dx = abs($tsize[2]-$tsize[0]);
$dy = abs($tsize[5]-$tsize[3]);
$x = ( ImageSx($im) - $dx ) / 2;
$y = ( ImageSy($im) - $dy ) / 2 + $dy;
// draw text
$black = ImageColorAllocate($im,0,0,0);
ImageTTFText($im, $size, 0, $x, $y, $black, $font, $text);
header('Content-Type: image/png');
ImagePNG($im);
?>




In this case, the blank button (button.png) looks as shown in Figure 9-7.



Figure 9-7. Blank Button



The script in Example 9-8 can be called from a page like this:



<img src="button.php?text=PHP+Button">



This HTML generates the button shown in Figure 9-8.



Figure 9-8. Button with generated text label



The + character in the URL is the encoded form of a space. Spaces are illegal in URLs and must be encoded. Use PHP's urlencode( ) function to encode your button strings. For example:



<img src="button.php?text=<?php echo urlencode('PHP Button')?>">




9.6.1. Caching the Dynamically Generated Buttons


It is somewhat slower to generate an image than to send a static image. For buttons that will always look the same when called with the same text argument, a simple cache mechanism can be implemented.


Example 9-9 generates the button only when no cache file for that button is found. The $path variable holds a directory, writable by the web server user, where buttons can be cached. The filesize( ) function returns the size of a file, and readfile( ) sends the contents of a file to the browser. Because this script uses the text form parameter as the filename, it is very insecure (Chapter 12 explains why and how to fix it).


Example 9-9. Caching dynamic buttons



<?php
header('Content-Type: image/png');
$path = "/tmp/buttons"; // button cache directory
$text = $_GET['text'];

if($bytes = @filesize("$path/$text.png")) { // send cached version
header("Content-Length: $bytes");
readfile("$path/$text.png");
} else { // build, send, and cache
$font = 'times';
if (!$_GET['size']) $_GET['size'] = 12;
$im = ImageCreateFromPNG('button.png');
$tsize = ImageTTFBBox($size, 0, $font, $text);
$dx = abs($tsize[2]-$tsize[0]); // center text
$dy = abs($tsize[5]-$tsize[3]);
$x = ( imagesx($im) - $dx ) / 2;
$y = ( imagesy($im) - $dy ) / 2 + $dy;
$black = ImageColorAllocate($im,0,0,0);
ImageTTFText($im, $_GET['size'], 0, $x, $y, -$black, $font, $text);
ImagePNG($im); // send image to browser
ImagePNG($im,"$path/$text.png"); // save image to file
}
?>






9.6.2. A Faster Cache



Example 9-9 is still not quite as quick as it could be. There is a more advanced caching
technique that completely eliminates PHP from the request once an image has been generated.


First, create a buttons directory somewhere under your web server's DocumentRoot and make sure that your web server user has permissions to write to this directory. For example, if the DocumentRoot directory is /var/www/html, create /var/www/html/buttons.


Second, edit your Apache httpd.conf file and add the following block:



<Location /buttons/>
ErrorDocument 404 /button.php
</Location>



This tells Apache that requests for nonexistent files in the buttons directory should be sent to your button.php script.


Third, save Example 9-10 as button.php. This script creates new buttons, saving them to the cache and sending them to the browser. There are several differences from Example 9-9, though. We don't have form parameters in $_GET, because Apache handles error pages as redirections. Instead, we have to pull apart values in $_SERVER to find out which button we're generating. While we're at it, we delete the '..' in the filename to fix the security hole from Example 9-9.


Once button.php is installed, when a request comes in for something like http://your.site/buttons/php.png, the web server checks whether the buttons/php.png file exists. If it does not, the request is redirected to our button.php script, which creates the image (with the text "php") and saves it to buttons/php.png. Any subsequent requests for this file are served up directly without a line of PHP being run.


Example 9-10. More efficient caching of dynamic buttons



<?php
// bring in redirected URL parameters, if any
parse_str($_SERVER['REDIRECT_QUERY_STRING']);

$button_dir = '/buttons/';
$url = $_SERVER['REDIRECT_URL'];
$root = $_SERVER['DOCUMENT_ROOT'];

// pick out the extension
$ext = substr($url,strrpos($url,'.'));

// remove directory and extension from $url string
$file = substr($url,strlen($button_dir),-strlen($ext));

// security - don't allow '..' in filename
$file = str_replace('..','',$file);

// text to display in button
$text = urldecode($file);

// build image
if(!isset($font)) $font = 'times';
if(!isset($size)) $size = 12;
$im = ImageCreateFromPNG('button.png');
$tsize = ImageTTFBBox($size,0,$font,$text);
$dx = abs($tsize[2]-$tsize[0]);
$dy = abs($tsize[5]-$tsize[3]);
$x = ( ImageSx($im) - $dx ) / 2;
$y = ( ImageSy($im) - $dy ) / 2 + $dy;
$black = ImageColorAllocate($im,0,0,0);
ImageTTFText($im, $size, 0, $x, $y, -1*$black, $font, $text);

// send and save the image
header('Content-Type: image/png');
ImagePNG($im);
ImagePNG($im,$root.$button_dir."$file.png");
ImageDestroy($im);
?>




The only drawback to the mechanism in Example 9-10 is that the button text cannot contain any characters that are illegal in a filename. Nonetheless, this is the most efficient way to cache dynamically generated images. If you change the look of your buttons and you need to regenerate the cached images, simply delete all the images in your buttons directory, and they will be recreated as they are requested.


You can also take this a step further and get your button.php script to support multiple image types. Simply check $ext and call the appropriate ImagePNG( ), ImageJPEG( ), or ImageGIF( ) function at the end of the script. You can also parse the filename and add modifiers such as color, size, and font, or pass them right in the URL. Because of the parse_str( ) call in the example, a URL such as http://your.site/buttons/php.png?size=16 displays "php" in a font size of 16.













No comments: