PHP Solution 8-4: Generating the thumbnail image This PHP Solution completes the Ps2_Thumbnail class by creating the image resources, copying the thumbnail, and saving it in the destina
Trang 1231
• imagepng()
• imagegif()
Each function takes as its first two arguments: the image resource and the path to where you want to save
it
The imagejpeg() and imagepng() functions take an optional third argument to set the image quality For imagejpeg(), you set quality by specifying a number in the range of 0 (worst) to 100 (best) If you omit the argument, the default is 75 For imagepng(), the range is 0 to 9 Confusingly, 0 produces the best quality (no compression)
Finally, once you have saved the thumbnail, you need to destroy the image resources by passing them to imagedestroy() In spite of its destructive name, this function has no effect on the original image or the thumbnail It simply frees the server memory by destroying the image resources required during processing
PHP Solution 8-4: Generating the thumbnail image
This PHP Solution completes the Ps2_Thumbnail class by creating the image resources, copying the thumbnail, and saving it in the destination folder
Continue working with your existing class definition Alternatively, use Thumbnail_03.php in the ch08 folder
1 The image resource for the original image needs to be specific to its MIME type, so start by
creating an internal method to select the correct type Add the following code to the class definition Its a protected method, so put it at the bottom of the page (but inside the classs closing curly brace)
protected function createImageResource() {
if ($this->_imageType == 'jpeg') {
return imagecreatefromjpeg($this->_original);
} elseif ($this->_imageType == 'png') {
return imagecreatefrompng($this->_original);
} elseif ($this->_imageType == 'gif') {
return imagecreatefromgif($this->_original);
}
}
The checkType() method that you created in PHP Solution 8-1 stores the MIME type as jpeg, png, or gif So, the conditional statement checks the MIME type, matches it to the appropriate function, and passes the original image as an argument The method then returns the resulting image resource
2 Now its time to define the internal method that does all the hard work It contains a lot of code,
so Ill break it into sections Start by defining the createThumbnail() method like this:
protected function createThumbnail() {
$resource = $this->createImageResource();
$thumb = imagecreatetruecolor($this->_thumbwidth, $this->_thumbheight); }
Trang 2232
This calls the createImageResource() method that you created in step 1, and then creates
an image resource for the thumbnail, passing the thumbnails width and height to imagecreatetruecolor()
3. The next stage in creating the thumbnail involves passing both image resources to imagecopyresampled() and setting the coordinates and dimensions Amend the createThumbnail() method like this:
protected function createThumbnail() {
$resource = $this->createImageResource();
$thumb = imagecreatetruecolor($this->_thumbwidth, $this->_thumbheight);
imagecopyresampled($thumb, $resource, 0, 0, 0, 0, $this->_thumbwidth, $this->_thumbheight, $this->_originalwidth, $this->_originalheight);
}
The first two arguments are the image resources you have just created for the thumbnail and original image The next four arguments set the x and y coordinates for both the copy and the original to the top left corner Next come the width and height calculated for the thumbnail, followed by the original images width and height By setting arguments 3–6 to the top left corner and both sets of dimensions to the full amounts, this copies the whole original image to the whole of the thumbnail In other words, it creates a smaller copy of the original
Note that you dont need to assign the result of imagecopyresampled() to a variable The scaled down image is now stored in $thumb, but you still need to save it
4 Complete the definition of createThumbnail() like this:
protected function createThumbnail() {
$resource = $this->createImageResource();
$thumb = imagecreatetruecolor($this->_thumbwidth, $this->_thumbheight); imagecopyresampled($thumb, $resource, 0, 0, 0, 0, $this->_thumbwidth, $this->_thumbheight, $this->_originalwidth, $this->_originalheight);
$newname = $this->_name $this->_suffix;
if ($this->_imageType == 'jpeg') {
$newname = '.jpg';
$success = imagejpeg($thumb, $this->_destination $newname, 100); } elseif ($this->_imageType == 'png') {
$newname = '.png';
$success = imagepng($thumb, $this->_destination $newname, 0);
} elseif ($this->_imageType == 'gif') {
$newname = '.gif';
$success = imagegif($thumb, $this->_destination $newname);
}
if ($success) {
$this->_messages[] = "$newname created successfully.";
} else {
$this->_messages[] = "Couldn't create a thumbnail for "
basename($this->_original);
}
imagedestroy($resource);
Trang 3233
imagedestroy($thumb);
}
The first line of new code concatenates the suffix to the filename stripped of its filename
extension So, if the original file is called menu.jpg and the default _thb suffix is used,
$newname becomes menu_thb
The conditional statement checks the images MIME type and appends the appropriate
filename extension In the case of menu.jpg, $newname becomes menu_thb.jpg The scaled down image is then passed to the appropriate function to save it, using the destination folder and $newname as the path where it is saved For JPEG and PNG images, the optional quality argument is set to the highest level: 100 for JPEG and 0 for PNG
The result of the save operation is stored in $success Depending on the outcome, $success
is either true or false, and an appropriate message is added to the $_messages property Finally, imagedestroy() frees the server memory by destroying the resources used to create the thumbnail image
5 Update the definition of the create() method to call the createThumbnail() method:
public function create() {
if ($this->_canProcess && $this->_originalwidth != 0) {
$this->calculateSize($this->_originalwidth, $this->_originalheight); $this->getName();
$this->createThumbnail();
} elseif ($this->_originalwidth == 0) {
$this->_messages[] = 'Cannot determine size of ' $this->_original; }
}
6 You no longer need the test() method You can either delete it from the class definition or
comment it out If you plan to experiment further or make enhancements to the class,
commenting it out saves the effort of creating it again from scratch
7 Up to now, you have used the test() method to display error messages Create a public
method to get the messages:
public function getMessages() {
return $this->_messages;
}
8 Save Thumbnail.php In create_thumb.php, replace the call to the test() method with a
call to getMessages(), and assign the result to a variable like this:
$thumb->create();
$messages = $thumb->getMessages();
9 Add a PHP code block just after the opening <body> tag to display any messages:
<?php
if (isset($messages) && !empty($messages)) {
Trang 4234
echo '<ul>';
foreach ($messages as $message) {
echo "<li>$message</li>";
}
echo '</ul>';
}
?>
Youve seen this code in previous chapters, so it needs no explanation
10 Save create_thumb.php, load it in a browser, and test it by selecting an image from the list
and clicking Create Thumbnail If all goes well, you should see a message reporting the
creation of the thumbnail, and confirm its existence in the thumbs subfolder of upload_test,
as shown in Figure 8-6
Figure 8-6 The thumbnail has been successfully created in the destination folder
11 If the thumbnail isnt created, the error message generated by the Ps2_Thumbnail class
should help you detect the source of the problem Also, check your code carefully against Thumbnail_04.php in the ch08 folder If the tests in the previous PHP solutions worked, the error is likely to be in the create(), createImageResource(),or createThumbnail() method definitions The other place to check is, of course, your PHP configuration The class depends on the GD extension being enabled Although GD is widely supported, its not always
on by default
Trang 5235
Resizing an image automatically on upload
Now that you have a class that creates a thumbnail from a larger image, its relatively simple to adapt the Ps2_Upload class from Chapter 6 to generate a thumbnail from an uploaded image—in fact, not only from
a single image, but also from multiple images
Instead of changing the code in the Ps2_Upload class, its more efficient to extend the class and create a subclass You then have the choice of using the original class to perform uploads of any type of file, or the subclass to create thumbnail images on upload The subclass also needs to provide the option to save or discard the larger image after the thumbnail has been created
Before diving into the code, lets take a quick look at how you create a subclass
Extending a class
A major advantage of using classes is that theyre extensible To extend a class, you simply include the original class definition and define the subclass using the extends keyword like this:
require_once('OriginalClass.php');
class MyNewClass extends OriginalClass {
// subclass definition
}
This creates a new subclass or child class called MyNewClass from the original or parent class,
OriginalClass The parent-child analogy is apposite, because the child inherits all the features of its parent, but can adapt some of them and acquire new ones of its own This means that MyNewClass shares the same properties and methods as OriginalClass, but you can add new properties and methods You
can also redefine (or override) some of the parents methods and properties This simplifies the process
of creating a class to perform a more specialized task The Ps2_Upload class you created in Chapter 6 performs basic file uploads In this chapter, youll extend it to create a child class called Ps2_ThumbnailUpload that uses the basic upload features of its parent, but adds specialized features that create thumbnail images
Like all children, a child class often needs to borrow from its parent This frequently happens when you override a method in the child class, but need to use the original version as well To refer to the parent version, you prefix it with the parent keyword followed by two colons like this:
parent::originalMethod();
Youll see how this works in PHP Solution 8-5, because the child class defines its own constructor to add
an extra argument, but also needs to use the parent constructor
This description of inheritance covers only the bare minimum you need to understand PHP Solution
8-5 For a more detailed insight into PHP classes, see my PHP Object-Oriented Solutions (friends of
ED, 2008, ISBN: 978-1-4302-1011-5)
So, lets create a class capable of uploading images and generating thumbnails at the same time
Trang 6236
PHP Solution 8-5: Creating the Ps2_ThumbnailUpload class
This PHP solution extends the Ps2_Upload class from Chapter 6 and uses it in conjunction with the Ps2_Thumbnail class to upload and resize images It demonstrates how to create a child class and override parent methods To create the child class, you need Upload.php from Chapter 6 and Thumbnail.php from this chapter Copies of both files are in the classes/completed folder
1 Create a new file called ThumbnailUpload.php in the classes/Ps2 folder It will contain
only PHP code, so strip out any HTML inserted by your script editor, and add the following code:
<?php
require_once('Upload.php');
require_once('Thumbnail.php');
class Ps2_ThumbnailUpload extends Ps2_Upload {
}
This includes the definitions of the Ps2_Upload and Ps2_Thumbnail classes, and declares that the Ps2_ThumbnailUpload class extends Ps2_Upload All subsequent code needs to be inserted between the curly braces
2 The child class needs three properties: for the folder where the thumbnail is to be saved, a
Boolean that determines whether to delete the original image, and for the suffix to be added to the thumbnail The last of these is required in case you dont want to use the default suffix defined in Ps2_Thumbnail Add the following property definitions inside the curly braces: protected $_thumbDestination;
protected $_deleteOriginal;
protected $_suffix = '_thb';
3 When you extend a class, the only time you need to define a constructor method is when you
want to change how the constructor works The Ps2_ThumbnailUpload class takes an extra argument that determines whether to delete the original image, giving you the option to retain only the thumbnail or to keep both versions of the image When testing locally, a
Ps2_Thumbnail object can access the original image on your own hard drive But generating the thumbnail is a server-side operation, so it wont work on a website without first uploading the original image to the server
The constructor also needs to call the parent constructor to define the path to the upload folder Add the following definition to the class:
public function construct($path, $deleteOriginal = false) {
parent:: construct($path);
$this->_thumbDestination = $path;
$this->_deleteOriginal = $deleteOriginal;
}
Trang 7237
The constructor takes two arguments: the path to the upload folder and a Boolean variable that determines whether to delete the original image The second argument is set to false in the constructor signature, making it optional
The first line of code inside the constructor passes $path to the parent constructor to set the destination folder for the file uploads The second line also assigns $path to the
$_thumbDestination property, making the same folder the default for both images
The final line assigns the value of the second argument to the $_deleteOriginal property Because the second argument is optional, its automatically set to false and both images are retained unless you set it explicitly to true
4 Create the setter method for the thumbnail destination folder like this:
public function setThumbDestination($path) {
if (!is_dir($path) || !is_writable($path)) {
throw new Exception("$path must be a valid, writable directory.");
}
$this->_thumbDestination = $path;
}
This takes a path as its only argument, checks that its a folder (directory) and is writable, and assigns the value to the $_thumbDestination property If the value passed as an argument
is invalid, the class throws an exception
Instead of creating a setter method for the thumbnail destination folder, I could have added an extra argument to the constructor However, my choice simplifies the constructor for occasions when you want to save the thumbnail and original image in the same folder Also, I could have silently used the original upload folder instead of throwing an exception if theres a problem with the thumbnail destination I decided that a problem with the destination folder is too serious to ignore Decisions like this are an integral part of writing any script, not just designing a class
5 Apart from the name, the setter method for the thumbnail suffix is identical to the one in
Thumbnail.php It looks like this:
public function setThumbSuffix($suffix) {
if (preg_match('/\w+/', $suffix)) {
if (strpos($suffix, '_') !== 0) {
$this->_suffix = '_' $suffix;
} else {
$this->_suffix = $suffix;
}
} else {
$this->_suffix = '';
}
}
Trang 8238
You need to define the method here because the class inherits from Ps2_Upload, not
Ps2_Thumbnail A PHP class can have only a single parent
6 Next, create a protected method to generate the thumbnail using the following code:
protected function createThumbnail($image) {
$thumb = new Ps2_Thumbnail($image);
$thumb->setDestination($this->_thumbDestination);
$thumb->setSuffix($this->_suffix);
$thumb->create();
$messages = $thumb->getMessages();
$this->_messages = array_merge($this->_messages, $messages);
}
This takes a single argument, the path to an image, and creates a Ps2_Thumbnail object The code is similar to create_thumb.php, so it shouldnt need explanation
The final line uses array_merge() to merge any messages generated by the Ps2_Thumbnail object with the $_messages property of the Ps2_ThumbnailUpload class Although the properties you defined in step 2 dont include a $_messages property, the child class
automatically inherits it from its parent
7 In the parent class, the processFile() method saves an uploaded file to its target
destination The thumbnail needs to be generated from the original image, so you need to override the parents processFile() method, and use it to call the createThumbnail() method that you have just defined Copy the processFile() method from Upload.php, and amend it by adding the code highlighted in bold
protected function processFile($filename, $error, $size, $type,
$tmp_name, $overwrite) {
$OK = $this->checkError($filename, $error);
if ($OK) {
$sizeOK = $this->checkSize($filename, $size);
$typeOK = $this->checkType($filename, $type);
if ($sizeOK && $typeOK) {
$name = $this->checkName($filename, $overwrite);
$success = move_uploaded_file($tmp_name, $this->_destination $name);
if ($success) {
// don't add a message if the original image is deleted
if (!$this->_deleteOriginal) {
$message = $filename ' uploaded successfully';
if ($this->_renamed) {
$message = " and renamed $name";
}
$this->_messages[] = $message;
}
// create a thumbnail from the uploaded image
$this->createThumbnail($this->_destination $name);
// delete the uploaded image if required
if ($this->_deleteOriginal) {
Trang 9239
unlink($this->_destination $name);
}
} else {
$this->_messages[] = 'Could not upload ' $filename;
}
}
}
}
If the original image has been uploaded successfully, the new code adds a conditional
statement to generate the message only if $_deleteOriginal is false It then calls the
createThumbnail() method, passing it the uploaded image as the argument Finally, if
$_deleteOriginal has been set to true, it uses unlink() to delete the uploaded image, leaving only the thumbnail
8 Save ThumbnailUpload.php To test it, copy create_thumb_upload_01.php from the ch08
folder to the gd folder, and save it as create_thumb_upload.php The file contains a simple form with a file field and a PHP block that displays messages Add the following PHP code block above the DOCTYPE declaration:
if (isset($_POST['upload'])) {
require_once(' /classes/Ps2/ThumbnailUpload.php');
try {
$upload = new Ps2_ThumbnailUpload('C:/upload_test/');
$upload->setThumbDestination('C:/upload_test/thumbs/');
$upload->move();
$messages = $upload->getMessages();
} catch (Exception $e) {
echo $e->getMessage();
}
}
Adjust the paths in the constructor and setThumbDestination() method, if necessary
9 Save create_thumb_upload.php, and load it in an HTML5-compliant browser Click the
Browse or Choose File button, and select multiple images When you click the Upload
button, you should see messages informing you of the successful upload and creation of the thumbnails Check the destination folders, as shown in Figure 8-7
Trang 10240
Figure 8-7 The thumbnails are created in the same operation as the images are uploaded
10 Test the Ps2_ThumbnailUpload class by uploading the same images again This time, the
original images and thumbnails should be renamed in the same way as in Chapter 6 through the addition of a number before the filename extension
11 Try different tests, changing the suffix inserted into the thumbnail names, or deleting the
original image after the thumbnail has been created If you run into problems, check your code carefully against ThumbnailUpload.php in the ch08 folder
In older browsers that dont support the multiple attribute on form fields, the class uploads a single image and creates a thumbnail from it To support multiple uploads from older browsers, create multiple file fields in the form, and give them all the same name attribute followed by an empty pair of square brackets like this: name="image[]"
Using the Ps2_ThumbnailUpload class
The Ps2_ThumbnailUpload class is easy to use Just include the class definition in your file, and pass the path to the upload folder to the constructor as an argument like this:
$upload = new Ps2_ThumbnailUpload('C:/upload_test/');