Tải bản đầy đủ (.pdf) (10 trang)

Giải pháp thiết kế web động với PHP - p 25 docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (614.16 KB, 10 trang )

GENERATING THUMBNAIL IMAGES

221

It doesnt matter which order your methods appear inside the class definition, but its common
practice to keep all public methods together after the constructor and to put protected
methods at the bottom of the file. This makes the code easier to maintain.
Insert the following definition between the constructor and the checkType() definition:
public function test() {
echo 'File: ' . $this->_original . '<br>';
echo 'Original width: ' . $this->_originalwidth . '<br>';
echo 'Original height: ' . $this->_originalheight . '<br>';
echo 'Image type: ' . $this->_imageType . '<br>';
if ($this->_messages) {
print_r($this->_messages);
}
}
This uses echo and print_r() to display the value of the properties.
6. To test the class definition so far, save Thumbnail.php, and add the following code block
above the DOCTYPE declaration in create_thumb.php (the code is in
create_thumb_win02.php and create_thumb_mac02.php in the ch08 folder):
<?php
if (isset($_POST['create'])) {
require_once(' /classes/Ps2/Thumbnail.php');
try {
$thumb = new Ps2_Thumbnail($_POST['pix']);
$thumb->test();
} catch (Exception $e) {
echo $e->getMessage();
}
}


?>
The name of the submit button in create_thumb.php is create, so this code runs only when
the form has been submitted. It includes the Ps2_Thumbnail class definition, creates an
instance of the class, passing the selected value from the form as an argument, and calls the
test() method.
7. Save create_thumb.php, and load it into a browser. Select an image, and click Create
Thumbnail. This produces output similar to Figure 8-1.
CHAPTER 8
222
Figure 8-1. Displaying the details of the selected image confirms the code is working.
If necessary, check your code against Thumbnail_01.php in the ch08 folder.
Although some properties have default values, you need to provide the option to change them by creating
public methods to set the maximum size of the thumbnail image, and the suffix applied to the base of the
filename. You also need to tell the class where to create the thumbnail. The formal term for this type of
method is a mutator method. However, because it sets a value, its commonly referred to as a setter
method. The next stage is to create the setter methods.
PHP Solution 8-2: Creating the setter methods
In addition to setting the value of protected properties, setter methods play an important role in ensuring
the validity of the value being assigned. Continue working with the same class definition. Alternatively,
use Thumbnail_01.php in the ch08 folder.
1. Begin by creating the setter method for the folder where the thumbnail is to be created. Add the
following code to Thumbnail.php after the constructor definition.
public function setDestination($destination) {
if (is_dir($destination) && is_writable($destination)) {
// get last character
$last = substr($destination, -1);
// add a trailing slash if missing
if ($last == '/') || $last == '\\') {
$this->_destination = $destination;
} else {

$this->_destination = $destination . DIRECTORY_SEPARATOR;
}
} else {
$this->_messages[] = "Cannot write to $destination.";
}
}
Download from Wow! eBook <www.wowebook.com>
GENERATING THUMBNAIL IMAGES

223
This begins by checking that $destination is a folder (directory) and that its writable. If it
isnt, the error message in the else clause at the end of the method definition is added to the
$_messages property. Otherwise, the rest of the code is executed.
Before assigning the value of $destination to the $_destination property, the code
checks whether the value submitted ends in a forward slash or backslash. It does this by
extracting the final character in $destination, using the substr() function. The second
argument to substr() determines the position from which to start the extract. A negative
number counts from the end of the string. If the third argument is omitted, the function returns
the rest of the string. So, $last = substr($destination, -1) has the effect of extracting
the last character, and storing it in $last.
The conditional statement checks whether $last is a forward slash or a backslash. Two
backslashes are needed because PHP uses a backslash to escape quotes (see
“Understanding when to use quotes” and “Using escape sequences” in Chapter 3).
Its necessary to check for both forward slashes and backslashes in $destination because
a Windows user might use backslashes out of habit. If the conditional statement confirms that
the final character is a forward slash or a backslash, $destination is assigned to the
$_destination property. Otherwise, the else clause concatenates the PHP constant
DIRECTORY_SEPARATOR to the end of $destination before assigning it to the
$_destination property. The DIRECTORY_SEPARATOR constant automatically chooses the
right type of slash depending on the operating system.

PHP treats forward slashes or backslashes equally in a path. Even if this results in adding the
opposite type of slash, the path remains valid as far as PHP is concerned.
2. The setter method for the maximum size of the thumbnail simply needs to check that the value
is a number. Add the following code to the class definition:
public function setMaxSize($size) {
if (is_numeric($size)) {
$this->_maxSize = abs($size);
}
}
The is_numeric() function checks that the submitted value is a number. If it is, its assigned
to the $_maxSize property. As a precaution, the value is passed to the abs() function, which
converts a number to its absolute value. In other words, a negative number is converted into a
positive one.
If the submitted value isnt a number, nothing happens. The propertys default value remains
unchanged.
3. The setter function for the suffix inserted in the filename needs to make sure the value doesnt
contain any special characters. The code looks like this:
CHAPTER 8
224

public function setSuffix($suffix) {
if (preg_match('/^\w+$/', $suffix)) {
if (strpos($suffix, '_') !== 0) {
$this->_suffix = '_' . $suffix;
} else {
$this->_suffix = $suffix;
}
} else {
$this->_suffix = '';
}

}
This uses preg_match(), which takes a regular expression as its first argument and
searches for a match in the value passed as the second argument. Regular expressions need
to be wrapped in a pair of matching delimiter characters—normally forward slashes, as used
here. Stripped of the delimiters, the regex looks like this:
^\w+$
In this context, the caret (^) tells the regex to start at the beginning of the string. The \w
matches any alphanumeric character or an underscore. The + means match one or more of the
preceding character, and the $ means match the end of the string. In other words, the regex
matches a string that contains only alphanumeric characters and underscores. If the string
contains spaces or special characters, it wont match.
As mentioned before, regexes can be difficult to learn, but theyre extremely useful in PHP,
JavaScript, and other web-related languages.
If the match fails, the else clause at the end of the method definition sets the $_suffix
property to an empty string. Otherwise, this conditional statement is executed.
if (strpos($suffix, '_') !== 0) {
The condition equates to true if the first character of $suffix is not an underscore. It uses
the strpos() function to find the position of the first underscore. If the first character is an
underscore, the value returned by strpos() is 0. However, if $suffix doesnt contain an
underscore, strpos() returns false. As explained in Chapter 3, 0 is treated by PHP as
false, so the condition needs to use the “not identical” operator (with two equal signs). So, if
the suffix doesnt begin with an underscore, one is added. Otherwise, the original value is
preserved.
Dont confuse
strpos()
and
strrpos()
. The former finds the position of the first matching
character. The latter searches the string in reverse.
GENERATING THUMBNAIL IMAGES


225

4. Update the test() method to display the values of the properties for which you have just
created setter methods. The revised code looks like this:
public function test() {
echo 'File: ' . $this->_original . '<br>';
echo 'Original width: ' . $this->_originalwidth . '<br>';
echo 'Original height: ' . $this->_originalheight . '<br>';
echo 'Image type: ' . $this->_imageType . '<br>';
echo 'Destination: ' . $this->_destination . '<br>';
echo 'Max size: ' . $this->_maxSize . '<br>';
echo 'Suffix: ' . $this->_suffix . '<br>';
if ($this->_messages) {
print_r($this->_messages);
}
}
5. Test the updated class by using the new setter methods in create_thumb.php like this:
$thumb = new Ps2_Thumbnail($_POST['pix']);
$thumb->setDestination('C:/upload_test/thumbs/');
$thumb->setMaxSize(100);
$thumb->setSuffix('small');
$thumb->test();
6. Save both pages, and select an image from the list in create_thumb.php. You should see
results similar to Figure 8-2.

Figure 8-2. Verifying that the setter methods work
7. Try a number of tests, omitting the trailing slash from the value passed to setDestination()
or selecting a nonexisting folder. Also pass invalid values to the setters for the maximum size
and suffix. An invalid destination folder produces an error message, but the others fail silently,

using the default value for the maximum size or an empty string for the suffix.
CHAPTER 8
226

If necessary, compare your code with Thumbnail_02.php in the ch08 folder.
You might not agree with my decision to fail silently when the values passed as arguments are invalid. By
now, though, you should have sufficient experience of conditional statements to adapt the code to your
own requirements. For example, if you want the setter method for the thumbnails maximum size to return
an error message instead of failing silently, check that the value is greater than zero, and add an else
clause to generate the error message. The else clause should also set the $_canProcess property to
false to prevent the class from attempting to create a thumbnail image. This is how you would adapt the
setMaxSize() method:
public function setMaxSize($size) {
if (is_numeric($size) && $size > 0) {
$this->_maxSize = $size;
} else {
$this->_messages[] = 'The value for setMaxSize() must be a positive number.';
$this->_canProcess = false;
}
}
Before you can create the thumbnail image, you need to calculate its size. The value set in the $_maxSize
property determines the width or height, depending which is larger. To avoid distorting the thumbnail, you
need to calculate the scaling ratio for the shorter dimension. The ratio is calculated by dividing the
maximum thumbnail size by the larger dimension of the original image.
For example, the original image of the Golden Pavilion (kinkakuji.jpg) is 270  346 pixels. If the
maximum size is set at 120, dividing 120 by 346 produces a scaling ratio of 0.3468. Multiplying the width of
the original image by this ratio fixes the thumbnails width at 94 pixels (rounded up to the nearest whole
number), maintaining the correct proportions. Figure 8-3 shows how the scaling ratio works.

Figure 8-3. Working out the scaling ratio for a thumbnail image.

GENERATING THUMBNAIL IMAGES

227

The base filename also needs to be split from the filename extension in preparation for inserting the suffix
indicating its a thumbnail.
PHP Solution 8-3: Final preparations for generating the thumbnail
This PHP solution adds three new methods to the Ps2_Thumbnail class: a public method that initiates the
generation of the thumbnail image, and two internal methods that calculate the thumbnails dimensions
and split the images base name from its filename extension. In PHP Solution 6-5, isolating the filename
extension was done by searching for a dot or period in the filename. This time, you know the file types in
advance, so a regular expression is used.
Continue working with your existing class definition. Alternatively, use Thumbnail_02.php in the ch08
folder.
1. Calculating the thumbnail dimensions doesnt require any further user input, so it can be
handled by an internal method. Add the following code to the class definition. Its a protected
method, so put it at the end of the file, just inside the closing curly brace.
protected function calculateSize($width, $height) {
if ($width <= $this->_maxSize && $height <= $this->_maxSize) {
$ratio = 1;
} elseif ($width > $height) {
$ratio = $this->_maxSize/$width;
} else {
$ratio = $this->_maxSize/$height;
}
$this->_thumbwidth = round($width * $ratio);
$this->_thumbheight = round($height * $ratio);
}
The dimensions of the original image are stored as properties of the Ps2_Thumbnail object,
so you could refer to them directly as $this->_originalWidth and

$this->_originalHeight. However, the method needs to refer to these values often, so I
decided to pass them as arguments to make the code easier to read and type.
The conditional statement begins by checking if the width and height of the original image are
less than or equal to the maximum size. If they are, the image doesnt need to be resized, so
the scaling ratio is set to 1.
The elseif clause checks if the width is greater than the height. If it is, the width is used to
calculate the scaling ratio. The else clause is invoked if the height is greater or both sides are
equal. In either case, the height is used to calculate the scaling ratio.
The last two lines multiply the original width and height by the scaling ratio, and assign the
results to the $_thumbwidth and $_thumbheight properties. The calculation is wrapped in
the round() function, which rounds the result to the nearest whole number.
2. Next, add the method that gets the filename and strips off the filename extension. The code
looks like this:
protected function getName() {
CHAPTER 8
228

$extensions = array('/\.jpg$/i', '/\.jpeg$/i', '/\.png$/i', '/\.gif$/i');
$this->_name = preg_replace($extensions, '', basename($this->_original));
}
The code inside the method is only two lines, but theres a lot going on. The first line creates
an array of regular expressions. As mentioned earlier, regexes are wrapped in delimiter
characters, normally forward slashes. The i after the closing slash of each regex tells it to
peform a case-insensitive search.
A period normally represents any character, but escaping it with a backslash makes the regex
look only for a period. The $ matches the end of the string. Everything else matches a literal
character. In other words, these regexes match .jpg, .jpeg, .png, and .gif in a case-
insensitive manner.
The second line uses the preg_replace() function, which performs a find and replace
operation using a regex or array of regexes. The first argument is the value(s) you want to

replace. The second argument is the replacement text—in this case, an empty string. The
third argument is the subject of the search and replace operation.
Here, the third argument is the value of the $_original property, which has been passed to
the basename() function. You met basename() in PHP Solution 4-3. It extracts the filename
from a path. So, the code in the second line searches the filename for an image filename
extension and replaces it with nothing. In other words, it strips off the filename extension, and
assigns the result to the $_name property.
3. These two methods need to be called by the method that creates the thumbnail image. Add the
following public method to the class definition above the protected methods:
public function create() {
if ($this->_canProcess && $this->_originalwidth != 0) {
$this->calculateSize($this->_originalwidth, $this->_originalheight);
$this->getName();
} elseif ($this->_originalwidth == 0) {
$this->_messages[] = 'Cannot determine size of ' . $this->_original;
}
}
This checks that $_canProcess is true and that the width of the original image is not 0. The
second test is necessary because getimagesize() sets the width and height to 0 if it cant
determine the size. This usually happens if the image format contains multiple images. The
method then calls the two internal methods you have just created. If the $_originalwidth
property is 0, an error message is added to the $_messages property.
4. To test the new methods, amend the test() method like this:
public function test() {
echo 'File: ' . $this->_original . '<br>';
echo 'Original width: ' . $this->_originalwidth . '<br>';
echo 'Original height: ' . $this->_originalheight . '<br>';
echo 'Image type: ' . $this->_imageType . '<br>';
GENERATING THUMBNAIL IMAGES


229

echo 'Destination: ' . $this->_destination . '<br>';
echo 'Max size: ' . $this->_maxSize . '<br>';
echo 'Suffix: ' . $this->_suffix . '<br>';
echo 'Thumb width: ' . $this->_thumbwidth . '<br>';
echo 'Thumb height: ' . $this->_thumbheight . '<br>';
echo 'Base name: ' . $this->_name . '<br>';
if ($this->_messages) {
print_r($this->_messages);
}
}
5. The call to create() needs to come before the test() method. Otherwise, the new values
wont have been generated. Amend the code in create_thumb.php like this:
$thumb = new Ps2_Thumbnail($_POST['pix']);
$thumb->setDestination('C:/upload_test/thumbs/');
$thumb->setMaxSize(100);
$thumb->setSuffix('small');
$thumb->create();
$thumb->test();
6. Test the updated class by selecting an image in create_thumb.php and clicking Create
Thumbnail. You should see the values displayed onscreen, as shown in Figure 8-4.

Figure 8-4. The class is now generating all the values needed to create the thumbnail image.
If necessary, check your code against Thumbnail_03.php in the ch08 folder.
After you have gathered all the necessary information, you can generate a thumbnail image from a larger
one. This involves creating image resources for the original image and the thumbnail. For the original
image, you need to use one of these functions specific to the images MIME type:
CHAPTER 8
230


• imagecreatefromjpeg()
• imagecreatefrompng()
• imagecreatefromgif()
The functions take a single argument: the path to the file. Because the thumbnail doesnt yet exist, you
use a different function, imagecreatetruecolor(), which takes two arguments—the width and height (in
pixels).
The function that creates a resized copy of an image is imagecopyresampled(), which takes no fewer
than ten arguments—all of them required. The arguments fall into five pairs as follows:
• References to the two image resources—copy first, original second
• The x and y coordinates of where to position the top-left corner of the copied image
• The x and y coordinates of the top-left corner of the original
• The width and height of the copy
• The width and height of the area to copy from the original
Figure 8-5 shows how the last four pairs of arguments can be used to extract a specific area, rather than
copy the whole image, using the following arguments to imagecopyresampled():
imagecopyresampled($thumb, $source, 0, 0, 170, 20, $thbwidth,$thbheight, 170, 102);

Figure 8-5. The imagecopyresampled() function allows you to copy part of an image.
The x and y coordinates of the area to copy are measured in pixels from the top left of the image. The x
and y axes begin at 0 at the top left, and increase to the right and down. By setting the width and height of
the area to copy to 170 and 102, respectively, PHP extracts the area outlined in white.
So, now you know how websites manage to crop uploaded images. They calculate the coordinates
dynamically using JavaScript or Flash, both of which are beyond the scope of this book. For the
Ps2_Thumbnail class, youll use the whole of the original image to generate the thumbnail.
After creating the copy with imagecopyresampled(), you need to save it, again using a function specific
to the MIME type, namely:
• imagejpeg()

×