Tuesday, August 11, 2009

USING THE PAGE NAVIGATOR CLASS Part II

In this chapter we’ll use the PageNavigator
class to step through a directory of images reduced on the fly using the ThumbnailImage class. We’ll use all three of the classes you have developed so far:

The DirectoryItems class stores a list of filenames of images.
The ThumbnailImage class reduces the dimensions of each image.
The PageNavigator class steps through these images in an orderly fashion.

We’ll also look at how to use CSS classes to adjust the appearance of the page navigator; this will greatly improve the reusability of the class. (This isn’t directly related to object-oriented programming [OOP], but if a class’s appearance cannot blend with various different designs, then its usefulness—and reusability—is greatly compromised. A web development language should integrate well with other web technologies.)

DirectoryItems Change

Fortunately, because the list of images in the DirectoryItems class is an array, you can use the ready-made PHP function to return a portion of an array— array_slice. All you need to do is wrap this function inside a method. Here is the additional method you require:

public function getFileArraySlice($start, $numberitems){
return array_slice($this->filearray, $start, $numberitems);
}

The $start variable passed to this method performs the same function as the start variable in the Google query string discussed in Chapter 7. The
$numberitems variable sets the number of items you wish to display per page. In a way, the entire PageNavigator class is an answer to the question, “How can you pass values to the getArraySlice method so that you can step through the list of images in an orderly fashion?”

CSS and Reusability

No matter how reusable an object is, it won’t be reused if it can’t be adapted to fit to a variety of page designs. Unlike the DirectoryItems class, which does its work on the server, the navigator is client-side HTML—it is a series of enabled or disabled hyperlinks. It’s important to control the page navigator’s appearance, because it’s a component of a web page. Figure 8-1 shows that page navigator again.

Figure 8-1: The page navigator

Recall that in order to control the navigator’s appearance, you wrapped it in a div tag and set the class attribute of the div tag to navigator. One way to display this component is to shrink the font by setting the font-size property to smaller and to use the text-align property to center the text. Here’s how the CSS code to produce that effect might look:

div.navigator{
font-size:smaller;
padding:5px;
text-align:center;
}

This CSS code will ensure that the navigator is centered and that the font size of its buttons is smaller than the surrounding text.

The div tag of the class, totalpagesdisplay, manipulates the appearance of the total page count in the following way:

div.totalpagesdisplay{
font-style:italic;
font-size:8pt;
text-align:center;
padding-top:15px;
}

A different font style and size are appropriate for displaying the current page and the total page count (page 3 of 6, as shown in Figure 8-1). Increased padding at the top separates the page number display from the navigator proper, which improves readability.
You’ll make the anchor tags within your navigator distinctive by assign- ing style characteristics to them. Because the inactive spans will share some of those characteristics, you can define them here as well. Those shared properties might look something like the following:

.navigator a, span.inactive{
margin-left:0px;
border-top:1px solid #999999; border-left:1px solid #999999; border-right:1px solid #000000; border-bottom:1px solid #000000; padding: 0px 5px 2px 5px;
}

Using a lighter color for the top and left borders and then a darker color for the bottom and right borders outlines the links and creates the illusion of depth.
Assign properties to the anchor pseudo-classes in order to override the default behavior—they should be different from other anchors on this page:

.navigator a:link, .navigator a:visited,
.navigator a:hover,.navigator a:active{
color: #3300CC;
background-color: #FAEBF7;
text-decoration: none;
}

Because these hyperlinks look like buttons, it makes sense to assign the same characteristics to each of the different states represented by the pseudo-classes: link, visited, hover, and active.
Finally, you differentiate inactive links from active ones by changing the background and the font style. For example, in Figure 8-1, because page 3 is the current page, it is disabled and has a gray background and italic font style.

span.inactive{
background-color :#EEEEEE;
font-style:italic;
}

You can, of course, style your own navigator much differently, using different CSS styles and, really, that’s the whole point.

Paging with Class

In Chapter 6, we created a web page to loop through a directory of images and display a thumbnail of each image. We’re going to do the same thing here, but this time we’ll incorporate the page navigator in order to display a limited number of images per page.
The very first thing you need to do is include the classes you’ll be using. This is done with two require statements:

require 'PageNavigator.php';
require 'DirectoryItems.php';

The PERPAGE variable defines how many images to display on each page. Define it as a constant (5), because it is used in a number of different places on this page and you don’t want to change its value accidentally:

//max per page define("PERPAGE", 5);

Recall that within the PageNavigator, the variable called $firstparam is assigned a default value of offset—the name for the first name/value pair of the query string associated with the URL of each hyperlink in the navigator. Each page needs to retrieve the offset value in order to determine which group of images to display:

//name of first parameter in query string define( "OFFSET", "offset");
/*get query string - name should be same as first parameter name
passed to the page navigator class*/
$offset = @$_GET[OFFSET];

Like PERPAGE, OFFSET is defined as a constant because you do not want its value to change. You want to ensure that the variable you’re requesting matches the variable passed into this page by the navigator.
You also want the flexibility to open this page even when no query string has been passed. For this reason, you should check the value of $offset:

//check variable if(!isset($offset)){
$totaloffset = 0;

}
else{
//then calculate offset
$totaloffset = $offset * PERPAGE;
}

If no query string is passed into the page, you want to begin displaying images at the beginning of your list, so you set $totaloffset to 0. If $offset does have a value, multiplying it by the PERPAGE value calculates the start position within the array of image filenames.
The name of the directory you want to use is assigned to the variable
$directory:

$directory = "graphics";
$di = new DirectoryItems($directory);

Listing 8-1: Hard-coded directory name

Because you want to display the directory of images in the graphics directory, pass the value graphics to the constructor of the DirectoryItems class.
The imagesOnly method filters out all non-images, and the method naturalCaseInsensitiveOrder ignores case and orders numerically where appropriate.

$di->imagesOnly();
$di->naturalCaseInsensitiveOrder();

In Chapter 6, when you displayed all the thumbnails on one page, you retrieved the entire list of filenames from the DirectoryItems class instance. Since the page navigator controls the starting position and since you can retrieve a slice of the array, you need only retrieve a specific number of items here. Do this by passing the getArraySlice method a start position and the number of items you wish to display.

//get portion of array
$filearray = $di->getFileArraySlice($totaloffset, PERPAGE);

Displaying an Array Slice

You retrieve each filename and pass it to the getthumb.php file so it can serve as the file source for an img tag. You don’t need to make any changes to the version of the getthumb.php file you used in Chapter 6—it includes the ThumbnailImage class and uses it to create a reduced image.
The code to loop through the thumbnail images hasn’t changed from
Chapter 6 either. For ease of reference, it’s reproduced in Listing 8-2.

echo "<div style=\"text-align:center;\">";
echo "Click the file name to view full-sized version.<br />";
$path = "";
//specify size of thumbnail
$size = 100;
foreach ( $filearray as $key => $value){
$path = "$directory/".$key;
/*errors in getthumb or in class will result in broken links
- error will not display*/
echo "<img src=\"getthumb.php?path=$path&amp;size=$size\" ". "style=\"border:1px solid black;margin-top:20px;\" ".
"alt= \"$value\" /><br />\n";
echo "<a href=\"$path\" target=\"_blank\" >";
echo "Title: $value</a> <br />\n";
}
echo "</div><br />";

Listing 8-2: Code to loop through thumbnail images

This code differs from the code in Chapter 6 only in that the $filearray variable that contains the image filenames is the portion of the total array retrieved by the getArraySlice method and not all the filenames.

Creating the PageNavigator Object

In order to create the page navigator, you need the current page name and also the total number of image files; the global $_SERVER array supplies the name of the current page and getCount the total number of images.

$pagename = basename($_SERVER["PHP_SELF"]);
$totalcount = $di->getCount();

You only need to create the navigator if there is more than one page, so calculate that number first, as shown in the code in Listing 8-3.

$numpages = ceil($totalcount/PERPAGE);
//create if needed if($numpages > 1){
//create navigator
$nav = new PageNavigator( $pagename, $totalcount, PERPAGE, $totaloffset);
//is the default but make explicit
$nav->setFirstParamName(OFFSET);
echo $nav->getNavigator();
}

Listing 8-3: Creating the navigator if there’s more than one page

When constructing the PageNavigator instance, you pass it the four required parameters and let the two additional parameters—$maxpagesshown and $params—default to 4 and an empty string, respectively. This means that the navigator will show links to a maximum of four pages and that there are no additional name/value pairs for the query string. (As promised in Chap- ter 7, you’ll learn more about $params in Chapter 9. However, you may already have surmised that this variable can be used to replace the hard-coded directory name given in Listing 8-1.)
You do not need to set the first parameter name; it has a default value of offset. However, by setting the name here, you make it clear that this is the name of the one required name/value pair, and that it can be changed if desired.
Finally, the HTML code that makes up the navigator is returned and displayed in the web page.

Where to Go from Here

Using the PageNavigator class solves two problems: it alleviates the demand on server resources and it improves the aesthetics of the web page display. Only a limited number of images are displayed at any one time, thus reducing the demands on the server. Aesthetic requirements are satisfied by reduced web page length.
As noted on many occasions, the real value of objects is in their reusability. Through the use of CSS, you’re able to adjust the appearance of the page navigator to match it to a variety of situations. By using span and div tags, you can manipulate the look and feel of the navigator so that it blends easily with any design. The number of items shown on each page and the number of pages accessible at any one time can be set to any number desired.
We’ve seen that the PageNavigator class’s design is adaptable and that you can use it to step through an array of images, but what about its use in other situations? A navigator is much more commonly required with database queries that return a large number of records. In the next chapter, we’ll develop a database class and then see how well the PageNavigator class can handle a large result set. The ability to reuse the navigator class in various and different circumstances will be a true test of its robustness.

BUILDING THE PAGE NAVIGATOR CLASS

When there are a large number of images in a directory, it’s not desirable to display
all of them on one web page because doing so will probably create a very large and long page.
Web pages should be of reasonable length and should not take too long to download. Rather than dumping all your images onto one page, use a page navigator to step through them in an orderly fashion. This chapter will take on the task of creating a navigator class; Chapter 8 will use this class in conjunction with the DirectoryItems class.
Before you can create a page navigator, you need to determine how it should behave. Keep its design flexible and make sure that its appearance is easily configurable so that it can blend with the style of any particular page.

How Will the Navigator Behave?

A good starting point is to look at the navigator at the bottom of a Google query page. When searching Google, the default settings show 10 results per page and the navigator appears across the bottom of the page. One navigates by clicking the Previous or Next links, by choosing a page number, or by

clicking one of the many “o”s in Google. If your query returns a large number of pages, not all pages are shown in the page navigator. Records are ordered by relevance to the search criteria. Given this ordering scheme, there is little incentive to move to the last page of results and, in fact, there is no easy way of doing so.

Different Kinds of Searches

However, in many cases, searches return a relatively small number of items, and records are often ordered alphabetically. In situations such as this there should be an easy way to move to the beginning and the end pages, in addition to being able to move Previous and Next. Too, as with Google, the ability to configure the number of items shown per page is also desirable.
You should also limit the number of pages or links shown at any one time by your page navigator and make this option configurable to accommodate different needs and situations. For example, if you have 2,000 items to display and you’re showing 10 items per page, it’s probably not advisable to show all
200 links across the bottom of one page. But at the same time, you should show the total number of pages and identify the current page so that the user is not left in the dark.
Finally, the display style of navigation buttons should be configurable so that they match the design of an existing page. The best way to do this is to assign them a class name and manipulate their style using Cascading Style Sheets (CSS).

What Will It Look Like?

In sum, you will design a page navigator that will look something like
Figure 7-1.

Figure 7-1: Your page navigator design

In this particular example, the maximum number of pages shown by your navigator is 7. The total number of pages is 12, and the link to the current page, 4, is disabled (indicated above by an italicized number). Each button and page number functions as a hyperlink (except for that of the current page). The button labeled |< displays the first page, and the button labeled >| displays the last page. In this particular example, the Next button displays page 5, and the Prev button displays page 3.
Now look at Figure 7-1 again, and note that pages 8 through 12 are not displayed. You can go directly to page 12 by clicking the >| button, but there is no way to go directly to pages 8 through 11. At what point should links to these pages become visible?
Apply this question to the Google navigator, and you’ll see that the answer is not very straightforward. Among other things, it depends on the
direction you want to move and the number of items your search returns.

In some situations, the number of page links shown doubles. You probably don’t want to emulate this behavior, because your navigator will be used in a variety of situations, and in some cases space will be at a premium.
If you look more closely at a Google query, you can get a few hints about how to implement other desired behavior. For instance, try the following: Perform a Google query and put your mouse over one of the page number links in the navigator. If you look at the status bar of your browser, you see a query string that includes the variable start. If you haven’t changed Google’s default setting of 10 items per page, the value assigned to this variable is always a multiple of 10 that increases by 10 as the page numbers increase.
You’ll use a similar technique in your page navigator. Your navigator will be a series of hyperlinks, each including a query string containing a page num- ber indicating an offset from the start.

The Code

Go ahead and download the code for your page navigator, and look it over. Notice that there are considerably more data members than in other classes discussed so far. Names have been chosen in an attempt to make the purpose of the variable explicit, and related variables have been grouped. We’ll discuss these variables in the order in which they appear in the class file.

private $pagename;

The variable $pagename is the name of the page that will contain the page navigator control. It could be replaced by $_SERVER['PHP_SELF'], but by using a variable, you can accommodate situations where the Apache module mod_rewrite is being used to rewrite URLs. It’s designed to hold a string.

private $totalpages;

$totalpages is a convenient way to refer to the total number of pages required to display all the items in your list of images. It is calculated from the total number of items and the number of items shown per page. Its value will be an integer.

private $recordsperpage;
private $maxpagesshown;

$recordsperpage is the number of items shown on a page and $maxpagesshown is the maximum number of links to additional pages shown on any one page. The former affects the length of the page, while the latter affects the width of the navigator. Again, these are intended to be integer variables.

private $currentstartpage; private $currentendpage; private $currentpage;

$currentstartpage, $currentendpage, and $currentpage are best understood using a visual example. Refer back to Figure 7-1; these would be 1, 7, and 4, respectively.

The next four variables are string variables that hold the HTML code necessary to display inactive links.

//next and previous inactive private $spannextinactive; private $spanpreviousinactive;
//first and last inactive
private $firstinactivespan;
private $lastinactivespan;

If you are currently on the first page, moving to a previous page or to the first page itself wouldn’t make sense. These variables will be used in place of active hyperlinks. Inactive links will be enclosed by span tags. Assigning a CSS class name to these spans allows their appearance to be manipulated by a style sheet.
$firstparamname and $params are data members that will form the query string in each hyperlink in your navigator.

//must match $_GET['offset'] in calling page private $firstparamname = "offset";
//use as "&amp;name=value" pair for getting
private $params;

$firstparamname is assigned a default value of “offset.” While the additional parameters contained in $params may or may not be added to the query string, the use of the “offset” parameter is not optional; this variable’s name must always be matched by a $_GET['offset'] in the page that contains your navi- gator. The $firstparamname will perform the same function as start in a Google query string—you will always need to know where the current page is relative to the start page. The variable $params will hold any other name/value pairs that may be needed as part of a query string. (You’ll learn more about this in Chapter 9.)
The next set of variables are string values that hold the CSS class names for the page navigator and its elements.

//css class names
private $divwrappername = "navigator";
private $pagedisplaydivname = "totalpagesdisplay";
private $inactivespanname = "inactive";

You’ve assigned default values to each of these variables, but they all have set and get methods so a client programmer can change them in order to match existing CSS classes if need be. $divwrappername is the name of the div tag that encloses the complete navigator. $pagedisplaydivname allows you to separately manipulate the display of the message relating the current page and total number of pages, such as page 4 of 12. You only need one class name for all of your inactive spans, because you want them all to have the same look.

The remaining four variables are simply text strings that label the con- trols used in the navigator, and they can be changed as the user sees fit:

//text for navigation
private $strfirst = "|&lt;"; private $strnext = "Next"; private $strprevious = "Prev"; private $strlast = "&gt;|";
//for error reporting private $errorstring;

The use of variables for the navigation text means that a client program- mer can configure these values—the look of the navigator is not fixed and can be adjusted to accommodate different visual layouts. The final data member is a string variable used for error reporting.

The Constructor

Now let’s see how the class is constructed. The constructor accepts six arguments, two of which have default values. Here is the constructor declaration:

public function __construct($pagename, $totalrecords, $recordsperpage,
$recordoffset, $maxpagesshown = 4, $params = "")

Four of the parameters to the constructor are simply copied into their class equivalents, and all have been discussed in the previous section on the data members.

$this->pagename = $pagename;
$this->recordsperpage = $recordsperpage;
$this->maxpagesshown = $maxpagesshown;
//already urlencoded
$this->params = $params;

Note that $params (the variable that contains any additional parameters as a name/value pair) is not URL-encoded within the class. If it is used, it will need to be URL-encoded before it is sent.
The constructor finishes with calls to a number of private class methods:

//check recordoffset a multiple of recordsperpage
$this->checkRecordOffset($recordoffset, $recordsperpage) or die($this->errorstring);
$this->setTotalPages($totalrecords, $recordsperpage);
$this->calculateCurrentPage($recordoffset, $recordsperpage);
$this->createInactiveSpans();
$this->calculateCurrentStartPage();
$this->calculateCurrentEndPage();

Let’s look at each of these method calls in turn.

Ain’t Misbehavin’

If you want your navigator to behave properly, you can check some of the values passed to the constructor; that’s exactly what the checkRecordOffset method does. It terminates construction of your object if it returns false. Let’s see why.

private function checkRecordOffset($recordoffset, $recordsperpage){
$bln = true;
if($recordoffset%$recordsperpage != 0){
$this->errorstring = "Error - not a multiple of records per page.";
$bln = false;
}
return $bln;
}

The $recordoffset variable passed to the constructor tells the navigator where it is currently positioned.
Since you are paging through your list while keeping the number of items shown per page constant, the record offset must be a multiple of the number of items shown per page. If it’s not, the navigator may still function but its behavior will be erratic. For this reason, the error message variable is set, a value of false is returned, and the application terminates. Terminating the application and identifying the reason saves having to debug a misbehav- ing application.

Other Constructor Method Calls

The five remaining private method calls made from the constructor aren’t quite as interesting as the checkRecordOffset method, but a few comments are appropriate.

Determining the Total Number of Pages
Since your navigator always allows you to move to the last item, you need to know the total number of pages:

private function setTotalPages($totalrecords, $recordsperpage){
$this->totalpages = ceil($totalrecords/$recordsperpage);
}

You use the ceil function to round up, because your final page may be a partial page.
For example, if you have 101 items to display, and you are showing 10
items per page, the first 10 pages will each show 10 items while the 11th page will show only one.

Determining the Current Page
In addition to the total number of pages, you also need to know the current page, so you have a calculateCurrentPage method:

private function calculateCurrentPage($recordoffset, $recordsperpage){
$this->currentpage = $recordoffset/$recordsperpage;
}

Simply dividing the record offset by the records per page gives you the current page. Notice that if you’re at the beginning of your list, the value of $recordoffset is 0, so the first page is also 0. This makes sense from a programming point of view, but before displaying the current page to a user, it’s incremented by 1.

Inactive Spans
The following method—createInactiveSpans—prepares the HTML code necessary to display inactive Next, First, Previous, and Last links:

private function createInactiveSpans(){
$this->spannextinactive = "<span class=\"".
"$this->inactivespanname\">$this->strnext</span>\n";
$this->lastinactivespan = "<span class=\"".
"$this->inactivespanname\">$this->strlast</span>\n";
$this->spanpreviousinactive = "<span class=\"".
"$this->inactivespanname\">$this->strprevious</span>\n";
$this->firstinactivespan = "<span class=\"".
"$this->inactivespanname\">$this->strfirst</span>\n";
}

While setting these variables is not strictly necessary (there may, in fact, not be any inactive spans on a particular page), by creating a method to prepare inactive links beforehand, you unclutter your code and make the logic of the most important method—getNavigator—clearer.

Finding the Start Page
Since links to all pages are not always shown, page 1 is not always the first link on a page. For this reason you need to determine the current start page. For example, if the total number of items is 100 with 5 items per page, and you are showing 4 links in your navigator and the current page is 6, the current start page for the navigator will be 5.

private function calculateCurrentStartPage(){
$temp = floor($this->currentpage/$this->maxpagesshown);
$this->currentstartpage = $temp * $this->maxpagesshown;
}

Calculating the Current End Page
The last page displayed in the navigator is easily calculated once the first page has been determined:

private function calculateCurrentEndPage(){
$this->currentendpage = $this->currentstartpage + $this->maxpagesshown;
if($this->currentendpage > $this->totalpages){
$this->currentendpage = $this->totalpages;
}
}

The current end page is the current page plus the maximum number of pages shown, unless that number is greater than the total number of pages, in which case, the end page is equal to the total number of pages.

The getNavigator Method

We’ve covered the data members, the constructor, and some related private methods of the page navigator class, but it’s the public methods that allow you to use it.
The get and set methods basically allow manipulation or retrieval of the CSS class names for the various components in the navigator, so we won’t spend time on them. The method that performs most of the work in this
class is the getNavigator method. It returns a string of the HTML-encoded links that make up your navigator. The navigator (shown in Figure 7-1) is created by starting at the left with the Move First link and finishes on the right with the Move Last link. We’ll discuss the code piece by piece and relate it back to this figure. The declaration of this method is:

public function getNavigator()

The very first responsibility of this method is to wrap the entire navigator in a div tag and assign a class name to this div. Doing so allows you to manip- ulate the appearance of your navigator via CSS:

$strnavigator = "<div class=\"$this->divwrappername\">\n";

Move First and Move Previous

The first element displayed is the hyperlink that allows you to move to the very first page of items. It’s disabled if the current page is the first page; if the current page is not the first page, you call a private class method— createLink—to create the hyperlink.

//output movefirst button if($this->currentpage == 0){
$strnavigator .= $this->firstinactivespan;

}else{
$strnavigator .= $this->createLink(0, $this->strfirst);
}

The createLink method to create the hyperlink is as follows:

private function createLink($offset, $strdisplay ){
$strtemp = "<a href=\"$this->pagename?$this->firstparamname=";
$strtemp .= $offset;
$strtemp .= "$this->params\">$strdisplay</a>\n";
return $strtemp;
}

This method constructs a hyperlink that includes a query string contain- ing the required offset parameter and any additional parameters that may be needed. For a Move First button, this link appears as |< if the default value of the variable—$strfirst—has not been altered.
The same logic applies to the Move Previous link, which is disabled if the current page is the first page:

//output moveprevious button if($this->currentpage == 0){
$strnavigator .= $this->spanpreviousinactive;
}else{
$strnavigator .= $this->createLink($this->currentpage-1, $this-
>strprevious);
}

Main Body of the Navigator

The main body of the navigator (see Listing 7-1) is created by looping through the pages, starting with the current start page.

//loop through displayed pages from $currentstart
for($x = $this->currentstartpage; $x < $this->currentendpage; $x++){
//make current page inactive if($x == $this->currentpage){
$strnavigator .= "<span class=\"$this->inactivespanname\">";
$strnavigator .= $x + 1;
$strnavigator .= "</span>\n";
}else{
$strnavigator .= $this->createLink($x, $x+1);
}
}

Listing 7-1: The main body of the navigator

This for loop creates hyperlinks for all the pages except the current page, and the number of iterations is determined by the $currentendpage data member.
As with the Move First button, the current page will be inactive, but all other pages will be hyperlinks.

Move Next and Move Last

Finally, create the Move Next and Move Last buttons in the same manner as the Move First and the Move Previous buttons, as shown in Listing 7-2.

//next button
if($this->currentpage == $this->totalpages-1){
$strnavigator .= $this->spannextinactive;
}else{
$strnavigator .= $this->createLink($this->currentpage + 1, $this->strnext);
}
//move last button
if($this->currentpage == $this->totalpages-1){
$strnavigator .= $this->lastinactivespan;
}else{
$strnavigator .= $this->createLink($this->totalpages -1, $this->strlast);
}

Listing 7-2: Creating the Move Next and Move Last buttons

Current and Total Number of Pages

The navigator proper is complete, but information about the current page and the total number of pages helps orient the user:

$strnavigator .= "</div>\n";
$strnavigator .= $this->getPageNumberDisplay();
return $strnavigator;

A terminating div tag ( ) encloses the navigator, and a call to getPageNumberDisplay creates the HTML code to display the current page and the total number of pages.

private function getPageNumberDisplay(){
$str = "<div class=\"$this->pagedisplaydivname\">\nPage ";
$str .= $this->currentpage + 1;
$str .= " of $this->totalpages";
$str .= "</div>\n";
return $str;
}

NOTE The string that displays the current page and the total number of pages is enclosed within a separate div tag in order to easily manipulate its placement and appearance.

Where to Go from Here

You’ve developed a page navigator class that implements behavior similar to the Google navigator. You’ve learned how to set the number of items shown per page and adjust the width of the navigator. The major components of the navigator have been assigned CSS class names, allowing manipulation of the navigator’s appearance. Chapter 8 will demonstrate how to use the page navigator in conjunction with the DirectoryItems class and the ThumbnailImage class, and how to configure its appearance.

THE THUMB NAIL IMAGE CLASS

Images are often of unequal dimensions, and their file sizes can vary greatly. This
inconsistency creates problems when down- loading and displaying a group of images because
one large image can take up the entire screen, dwarfing smaller images. Also, if image files are large, they can slow downloading to an unacceptable pace.
One solution to the problem of inconsistently sized images is to create a thumbnail image class, which creates small images (thumbnails) of equal size. By reducing the quality of an image, this class will be able to further reduce file size and hence download times.
Since we intend to use the DirectoryItems class with directories of images, this additional supporting class takes the next step toward improving the utility of the DirectoryItems class. Furthermore, developing this class should give you a good idea of when a method should be private, how to limit access to data members with set and get methods, how to set default values for data members upon declaration, and how to ensure that resources are disposed of properly.

What Does a Designer Do?

To determine what the DirectoryItems class should do, consider how web designers create thumbnail images. They typically open an image file in a graphic editor, reduce its dimensions (and perhaps its quality), and then resave the file under a different name.
For ease of placement on the screen, images in a group are usually reduced to approximately the same thumbnail size as each other. In other words, the thumbnail for a large image is roughly the same size as the thumbnail for a medium-sized image. The image is typically reduced to a predetermined maximum dimension while constraining its proportions so as not to distort it. This maximum dimension is usually applied to the width or the height, depending upon whether the image’s orientation is landscape or portrait. While this maximum will vary, the maximum size of a thumbnail should be such that it is small enough to download quickly, but large enough for the viewer to form an accurate impression of the full-sized picture. Once created, the thumbnail is typically displayed in a web page, where it might function as a hyperlink to its full-sized counterpart.

Mimicking the Designer

Having considered how the web designer handles thumbnails, we can better determine how a thumbnail class should behave. We will build a class that mimics the way a designer creates a thumbnail but with certain improvements.
When the designer creates a thumbnail, he writes a separate file to disk. While it might make sense in some cases for a class to create thumbnail images only once and then save them to disk, we will create them on the fly, as needed, and output them to the browser without saving them. This approach allows us to create a simpler, and in some respects, a more flexible class. We won’t have to worry about where to store the thumbnails or whether a thumbnail has already been created for a specific image.

NOTE The downside to this approach is that it requires more server-side processing, and, if there are a large number of images per web page, it may degrade server performance. We’ll solve this problem in Chapter 7.

Help from PHP Functions

When creating the thumbnail image class, the equivalent of the designer’s graphic editor is the existing PHP image function library, which contains the tools you need to manipulate common image types. PHP’s imagecreatefrom functions return a resource making it possible to programmatically copy an image, reduce its dimensions, and also reduce its quality if necessary.
Thus, you have your editor and you know how your class should behave.
You know too that you want to preserve the proportions of an image when you create a thumbnail and that you want to reduce all images to the same approximate thumbnail size. You’re all set to start coding.

NOTE The code used in this chapter requires a minimum version 2.0.1 of the GD graphics librar y. This will not be a problem if you are using PHP 5, but it may be if you are following along and creating a class in PHP 4. To determine which version you have, use the phpinfo function or the more specific gd_info.

The ThumbnailImage Class

In the following sections, you’ll examine the entire ThumbnailImage class, interspersing the code with comments.

Data Members

As always, the data members are private. The variable $image holds the actual thumbnail image itself.

private $image;
private $quality = 100;
private $mimetype;
private $imageproperties = array();
private $initialfilesize;

Since, in some cases, you may want to vary the quality of the thumbnail, you create the attribute $quality. For very large files (large in terms of byte count rather than just dimensions), you may need to reduce the quality of an image as well as its size. Give $quality a default value of 100 to indicate no reduction in quality, because in most cases, you will retain the quality of the original image.

NOTE The assignment of a value to $quality shows that data members may be initialized upon declaration, but they must be initialized with constant values. You could not, for instance, invoke a PHP function call or a method.

If you are going to output an image, you need to know whether it’s a
.jpeg, .gif, or .png file, hence the need for a MIME type attribute. Finally, add
$imageproperties, principally to capture the dimensions of the original image. Initialize it as an array (although there is no requirement to do so), because doing so is a nice way to document the data type of this variable.

NOTE Knowing the MIME type makes it easier for the browser to display an image.

Deconstructing the Constructor

As you saw in Chapter 5, the constructor is a magic method that begins with a double underscore and is invoked whenever a new instance of a class is created. The constructor for the ThumbnailImage class is shown in Listing 6-1.

public function __construct($file, $thumbnailsize = 100){
//check file

is_file($file) or die ("File: $file doesn't exist.");
$this->initialfilesize = filesize($file);
$this->imageproperties = getimagesize($file) or die ("Incorrect file_
type.");
// new function image_type_to_mime_type
$this->mimetype = image_type_to_mime_type($this->imageproperties[2]);
//create image
switch($this->imageproperties[2]){
case IMAGETYPE_JPEG:
$this->image = imagecreatefromJPEG($file);
break;
case IMAGETYPE_GIF:
$this->image = imagecreatefromGIF($file);
break;
case IMAGETYPE_PNG:
$this->image = imagecreatefromPNG($file);
break;
default:
die("Couldn't create image.");
}
$this->createThumb($thumbnailsize);
}

Listing 6-1: The constructor for the ThumbnailImage class

The code first checks that the $file passed in is legitimate, and, if so, it retrieves the properties of the image. In addition to file dimensions, the built-in PHP function filesize returns a constant integer that indicates the file’s MIME type. This PHP constant can be converted to a string value by using the image_type_to_mime_type function.

NOTE This function is new to PHP 5, so if you are working in PHP 4, the code needs to be different. This work has been done for you. Download the version 4 files of Chapter 6 to see how the same results are achieved by looking at file extensions. Knowing the MIME type will be necessary when you want to output your image.

The appropriate, image-specific imagecreatefrom function ( ) is called and a resource is returned. The actual thumbnail is created by manipulating this resource in the createThumb method.
Two parameters are passed to the constructor. The parameter $file is required; $thumbnailsize is optional because it has a default value. The $file variable tells your class where to find the image that is to be reduced, and
$thumbnailsize indicates the dimension that it will be reduced to.

Two Ways to Construct an Object

When discussing constructors in Chapter 5, you saw how default values can be assigned to parameters, thus providing flexibility and improving ease of use. The assignment of the value 100 to the variable $thumbnailsize means that the default size of your thumbnail will be 100 pixels.

Because this variable has a default value, you can create a class instance in two different ways. To accept the default thumbnail size, create an object like so:

$thumb = new ThumbnailImage("graphics/My_Picture.jpg");

In this case, the maximum dimension of the thumbnail will be the default value.
To construct a thumbnail of different dimensions, do the following:

$thumb = new ThumbnailImage("graphics/My_Picture.jpg", 250);

Assigning a default value to an argument to the constructor is simply a convenience for users of your class.

NOTE When assigning default values to arguments to a method, you may have as many default values as you wish. However, arguments without a default value should not fol- low those that have a default value; in this particular class, the $path variable should not follow the $thumbnailsize variable.

Internal Behavior—Private Methods

So far you have seen only private data members, but here you encounter your first private method. Private methods relate to the internal workings of a class and can be invoked only from within the class itself. The method that performs the image reduction (see Listing 6-2) is a private method—createThumb— called from within the constructor.

private function createThumb($thumbnailsize){
//array elements for width and height
$srcW = $this->imageproperties[0];
$srcH = $this->imageproperties[1];
//only adjust if larger than max
if($srcW > $thumbnailsize || $srcH > $thumbnailsize){
$reduction = $this->calculateReduction($thumbnailsize);
//get proportions
$desW = $srcW/$reduction;
$desH = $srcH/$reduction;
$copy = imagecreatetruecolor($desW, $desH);
imagecopyresampled($copy,$this->image,0,0,0,0,$desW, $desH, $srcW,
$srcH) or die ("Image copy failed.");
//destroy original imagedestroy($this->image);
$this->image = $copy;
}
}

Listing 6-2: The createThumb method

In this listing, createThumb checks the width and height of the image to determine whether it is greater than the targeted size. If it is, the method creates a reduced copy and overwrites the original image with the copy.
This private method for image reduction is called from the constructor and may only be invoked from within the class. By calling it from within the constructor, it need not be called directly, and the client programmer benefits by having a fully-formed and usable object immediately upon construction.

Must It Be Private?

Suppose for a moment, though, that your intention was to make a number of different-sized reductions of the same image, just as a photographer often makes different-sized copies of the same picture. In this case, it might make sense to save the original image and make the createThumb method public. As such, an image could be recreated at different sizes by repeatedly calling this method and passing the method different values.
In fact, with minimal change to the code and the interface, you could make your class accommodate this scenario and still fulfill its original intention.

A Helper Method

From within the createThumb method, you call another private method,
calculateReduction, shown in Listing 6-3.

private function calculateReduction($thumbnailsize){
$srcW = $this->imageproperties[0];
$srcH = $this->imageproperties[1];
//adjust
if($srcW < $srcH){
$reduction = round($srcH/$thumbnailsize);
}else{
$reduction = round($srcW/$thumbnailsize);
}
return $reduction;
}

Listing 6-3: The calculateReduction method

The calculateReduction method determines whether the height or width of the image is larger and then calculates the percentage reduction based on the targeted thumbnail size. In other words, it determines whether the image’s orientation is landscape or portrait and reduces the image on the appropriate dimension.

NOTE Unlike the createThumb method, the calculateReduction method is inherently private.
It is a helper method that returns the percentage reduction to the createThumb method.

Public Methods

Following the constructor is another public method with the name __destruct. This method is known as a destructor. The double underscore ( ) in front of the function name indicates that this method, like the constructor, is another magic method. Again, it is a method newly introduced in PHP 5. (Recall from Chapter 5 that magic methods happen in the background, like magic.)

public function __destruct(){
if(isset($this->image)){
imagedestroy($this->image);
}
}

While the use of destructors is new with PHP 5, anyone familiar with other OO languages has probably already come across them. As its name suggests, a destructor is the opposite of a constructor. A constructor initializes an object, while a destructor frees up resources that a class may have allocated. Generally, PHP does a good job of cleaning up after itself so destructors are often not strictly necessary. It’s used here to ensure that the image resource is disposed of.

Garbage Collection

Like Java, PHP employs a garbage collector to automatically clean up resources. Because the programmer is not responsible for allocating and freeing memory (as he is in a language like C++, for example), an automated program must free up resources. The garbage collector determines when objects are no longer used and then disposes of them, for example, when they go out of scope. However, using destructors can act as a cue that speeds up garbage collection. In the case of the ThumbnailImage class, the destructor disposes of the reduced image copy by calling the imagedestroy function.
You can call the destructor directly but, as with other magic methods the destructor is designed to be invoked in the background without any inter- vention by the programmer.

Displaying the Image

Next to the constructor, getImage (see Listing 6-4) is the most important method in the ThumbnailImage class, because it actually sends a reduced image to the browser. Its logic is simple: A header is sent to the browser announcing what to expect, followed by the appropriate content.

public function getImage(){
header("Content-type: $this->mimetype");
switch($this->imageproperties[2]){

case IMAGETYPE_JPEG:
imagejpeg($this->image, "", $this->quality);
break;
case IMAGETYPE_GIF: imagegif($this->image); break;
case IMAGETYPE_PNG:
imagepng($this->image, "", $this->quality);
break;
default:
die("Couldn't create image.");
}
}

Listing 6-4: The getImage method

Because .png and .jpeg image types support a reduction in quality, the quality argument is included when the images are output. The proper MIME type is sent to the browser first, and subsequently the image is sent in binary format.

Get and Set Methods

Chapter 5 introduced the concept of private data members and discussed how they create a need for accessor methods (also referred to as get and set methods), which retrieve and change the value of data members. The getMimeType method retrieves the MIME type of your thumbnail image. (Recall that the value returned is a copy and not the original.)

public function getMimeType(){
return $this->mimetype;
}

You need to retrieve the value of the private variable mimetype when you display your thumbnail. Merely retrieving the MIME type can do no harm, but the same cannot be said of setting this value. The MIME type is set in the constructor by looking at an image’s properties. Since this information can be determined programmatically and since an image’s MIME type does not change, there is no need to set this value. Hence, there is no set method to match the get method. To say the same thing in another way, $mimetype is a read-only value and having only a get method contributes to data protection.

Image Quality

On the other hand, it makes sense to have both a set and get method for image quality. The quality property of a ThumbnailImage object is quite different from an image’s MIME type and is not something that must remain fixed. In fact, getting and setting the quality of an image is one of the requirements that you set out to achieve when you designed this class. Let’s first look at the method that sets image quality in Listing 6-5.

public function setQuality($quality){
if ($quality > 100 || $quality < 1){
$quality = 75;
if($this->imageproperties[2] == IMAGETYPE_JPEG || $this-
>imageproperties[2] == IMAGETYPE_PNG){
$this->quality = $quality;
}
}

Listing 6-5: The setQuality method

As you can see in this listing, negative values and values greater
than 100 are prohibited because they are not valid values for image quality. Furthermore, .gif images don’t support alterations of the image quality, so
the second if statement checks for the appropriate image type before changing the quality. A set method is superior to direct access to an object’s properties because values can be tested and rejected, if need be, before they are assigned. A set method allows you to restrict how the variable quality is changed by screening out illegal values.
While the need to control the way in which object properties are
changed is somewhat obvious, retrieving object properties through an accessor method is also superior to directly accessing a public data member. Because you can’t alter the quality of a GIF, there is no need to retrieve it, and the getQuality method (see Listing 6-6) reflects this.

public function getQuality(){
$quality = null;
if($this->imageproperties[2] == IMAGETYPE_JPEG || $this-
>imageproperties[2] == IMAGETYPE_PNG){
$quality = $this->quality;
}
return $quality;
}

Listing 6-6: The getQuality method

Just as the setQuality method restricted changes to the quality of a .gif image, the getQuality method only returns a legitimate value if the image is a .jpeg or .png. Otherwise, null is returned.
Accessor methods are superior to direct access to public data members because they restrict how a variable is changed and how it is retrieved. They help ensure the integrity of your data and the functionality of the class as a whole. Get and set methods allow you to ignore the fact that .gif images don’t support a quality attribute in a way that unfettered public access cannot.

When to Change the Quality

In order to determine if the quality of an image needs reducing, it’s helpful to know a bit more about the image. The getInitialFileSize function returns the image’s original size in bytes. This information helps you decide whether to reduce the quality of an image and, if so, by how much.

public function getInitialFileSize(){
return $this->initialfilesize;
}

The code in this chapter doesn’t actually call this method, but you can imagine the circumstances in which it might be useful.

Displaying a Thumbnail

The process of outputting a series of thumbnail images to the browser occurs in two steps. First, you create a script that outputs an image; then you use this script file as the source for an img tag.
The code in Listing 6-7 shows the script file for outputting an image. It retrieves the path and size from a query string and uses these values to con- struct a thumbnail and then display it (in this chapter’s downloads, this is the file getthumb.php).

<?php
//this file will be the src for an img tag require 'ThumbnailImage.php';
$path = $_GET["path"];
$maxsize = @$_GET["size"];
if(!isset($maxsize)){
$maxsize = 100;
}
if(isset($path)){
$thumb = new ThumbNailImage($path, $maxsize);
$thumb->getImage();
}
?>

Listing 6-7: Constructing and displaying a thumbnail image

When passed a query string describing the path to an image file and the desired image size, this code outputs a thumbnail directly to the browser. The getImage method ( ) tells the browser the MIME type to expect and then sends the image file in binary format.

NOTE Typing getthumb.php?path=graphics/filename.jpg into the browser address bar is equivalent to pointing your browser directly at an image file. However, because you want to output a series of pictures and control their position, you will use this file as the src attribute of an img tag.

Putting It All Together

The short piece of code in Listing 6-8 uses the DirectoryItems class together with the ThumbnailImage class to display all images within a directory, at reduced sizes.

<?php
require 'DirectoryItems.php';

$dc = new DirectoryItems('graphics');
$dc->imagesOnly();
$dc->naturalCaseInsensitiveOrder();
$path = "";
$filearray = $dc->getFileArray();
echo "<div style=\"text-align:center;\">";
echo "Click the filename to view full-sized version.<br />";
//specify size of thumbnail
$size = 100;
foreach ($filearray as $key => $value){
$path = "graphics/".$key;
/*errors in getthumb or in class will result in broken links
- error will not display*/
echo "<img src=\"getthumb.php?path=$path&amp;size=$size\" ". "style=\"border:1px solid black;margin-top:20px;\" ".
"alt= \"$value\" /><br />\n";
echo "<a href=\"$path\" target=\"_blank\" >";
echo "Title: $value</a> <br />\n";
}
echo "</div><br />";
?>

Listing 6-8: Displaying all the images in a directory at reduced size

As shown in Listing 6-8, you first construct a DirectoryItems object and pass it the directory named graphics. You filter non-image files with the imagesOnly function, and the path is passed as a query string to the getthumb.php file, which, in turn, is assigned to the src attribute of an img tag.
This may seem strange at first, but the getthumb.php file contains all the information that the browser needs to display an image. However, if there are any errors in this file or in the thumbnail class file, the image will fail to display, and there will be no warning or error message, regardless of how you have configured your php.ini file. The error message will simply be interpreted as the binary output expected by the img tag.

NOTE In order to see error messages and warnings when debugging the ThumbnailImage class file, you need to call the getthumb.php file directly and not set it as the src for an img tag. Do this by hard-coding an image filename directly into the getthumb.php file and typing getthumb.php in the browser address bar.

Where to Go from Here

Using the ThumbnailImage class enhances your ability to display a directory of images by reducing the size of the images. This is a definite improvement in both aesthetics and performance, because small images download faster and use up less screen real estate.
But what if the image directory contains a few hundred or even a few thousand images? Showing a large number of image files, even if they’re only thumbnails, places unacceptable demands on server and client resources, and creates a web page that is far too long. You need to limit the number of images that display at any one time. Chapter 7 tackles this problem.

MODULE CLASS

Chapter 4 left us with some clear objec- tives. We need to add functionality to the DirectoryItems class, and we need to upgrade it
to take advantage of the changes introduced in
PHP 5. And that’s exactly what we’ll do in this chapter. We’ll upgrade the syntax of the DirectoryItems class first; then we’ll improve its functionality by adding methods.
Keeping in mind that we plan to use the DirectoryItems class to display images, we’ll add a method that ignores all non-image files, so we don’t need to worry if other file types occur within a directory containing mostly images. We’ll also broaden the scope of the class so that we can filter the contents of a directory and focus on a specific file type.

Upgrading to PHP 5

As you’re aware, the major change to PHP with version 5 is improved support for OOP. In this regard, two of the most important changes are the introduc- tion of access modifiers and changed syntax for class construction. Both of these changes will have an impact on the DirectoryItems class.

Access Modifiers

Next to the concept of a class, access modifiers are arguably the most important feature of an OO language. The principal use of access modifiers is to describe and constrain data members and methods. The access modifiers we are con- cerned with in this chapter are public and private. The modifier private is used to modify or describe matters relating to the internal behavior of a class. The modifier public is used to describe the external behavior of a class or, if you prefer, a class’s interface.
As far as syntactic changes to the DirectoryItems class are concerned, this means replacing the keyword var with private, so that

var $filearray = array();

becomes

private $filearray = array();

As you’ll recall, $filearray is the sole data member of the DirectoryItems class. In most cases (except static classes, which we will discuss in Chapter 11), you should make all data members private, because by doing so, you are protecting the integrity of your data by restricting access to it.
To better understand access modifiers, it’s useful to think of data
members or instance variables as though they are data in a database. In order to maintain the integrity of the data in a database, it’s wise to limit access and restrict the ways in which data can be added or changed. A programmer might well write an application to achieve this result, requiring users to log in and implementing controls on the way in which data are formatted. For instance, you may want dates stored in a particular format and enforce this through the use of a masked textbox.
Since access modifiers are nonexistent in PHP 4, changing the value of a variable only requires a simple assignment. You could modify the
$filearray variable in the following way:

$di->filearray[0] = "anyfile.jpg";

It’s a disadvantage to do things this way because changes to $filearray are not controlled and allowing direct access to data members greatly increases the risk of contaminating your data. If you use the keyword private, direct access is no longer possible.

NOTE In terms of the preceding database analogy, making an instance variable private means that access to the data is only permitted through use of the programmer’s application or front end.

But wait, it’s your code, right? You won’t change it improperly, so why should you care? Because OO programming assumes that other programmers may use your objects and vice versa.
Bruce Eckel refers to this as client programmers using objects created by class creators.1 Even if you are a lone developer and don’t expect other pro- grammers to use your code, access modifiers are still an important safeguard. Why? How many times have you returned to your own code, even after only a
short period of time away from it, and had trouble trying to figure out what exactly you were trying to achieve? Clearly, in this situation, even though you are the class originator, you are also, at the same time, a client pro- grammer. The use of access modifiers forces the programmer to make his or her intentions explicit. If a particular data member or method relates to the internal behavior of a class, then applying the appropriate access modifier documents this intention. If nothing else, we all need access modifiers to protect our code from that most presumptuous of client programmers— ourselves.
When first encountering the private keyword, there is sometimes a mis-
taken tendency to view it solely as a security measure and then point out its ineffectiveness by showing how easily a malicious user programmer could subvert it. Even more so with a non-compiled language like PHP, because it’s an easy matter to change a modifier from private to public. It’s better to view the use of access modifiers as indicative of the originating program- mer’s intentions—as a form of internal documentation. (However, the use of access modifiers does add to security insofar as any well thought out and well documented class is a more secure class.)
The private keyword can be applied to methods as well as to data members. You’ll see an example of a private method later in this chapter, but for the moment, let’s look at the use of the modifier public when applied to a method.
Once the need for the keyword private is apparent, so also is the need for a public method or interface so that private data members may be accessed in a controlled fashion. Now that the $filearray variable is private, you no longer have any kind of access to it. For this reason, you need a public method, sometimes called an accessor method, in order to retrieve that private variable:

public function getFileArray(){
return $this->filearray
}

In the previous chapter, you directly accessed this data member thus:
$di->filearray. You might well wonder what the difference is and conclude that direct access is preferable because it is more succinct. However, the impor- tant difference is that when you directly access a data member, you are working

1 Bruce Eckel, Thinking in Java (Prentice Hall, 1998), 30.

with the original, but when you use a method to retrieve a data member, you retrieve a copy of that original. When working directly with the original, you risk changing its value, inadvertently or otherwise. When working with a copy, there is no such danger because, should the copy be changed, the original will remain intact. In other words, what’s returned from the getFileArray method is returned by value, not by reference. Changing the copy won’t have any effect on the original.
It is perhaps clearer now how a public method is an interface. A public method mediates between a data member and a user programmer in the same way that the front end of a database mediates between a user and the data. Controlled access to the data simplifies how a class is used and, in so doing, helps preserve its integrity.

The Constructor

In Chapter 4, you saw how the class name functioned as a special method called the constructor. However, PHP 5 changes the way that objects are constructed. Specifically,

function DirectoryItems($directory){ ... }

becomes

public function __construct($directory){ ... }

Methods beginning with a double underscore are magic methods. They are given this name because they are not (usually) called directly. This new method for constructing objects is invoked in exactly the same way as a con- structor is invoked under PHP 4. Creating an instance of the DirectoryItems class still uses the keyword new along with the class name:

$di = new DirectoryItems("graphics");

The syntax for creating an object is the same, but in PHP 5, the construct
method is executed rather than a method bearing the class name.

NOTE In PHP 5, you need not return the object created by the constructor (or any method for that matter) by reference. The reason for this is explained in Chapter 13 in the section “__clone” on page 116.
Altering the constructor may seem like an unnecessary change to those of you familiar with constructors in other OO languages, but there are advan- tages that you’ll see when we discuss inheritance. Without getting into the details of inheritance at this early stage, let’s just say that having a fixed name for the constructor in every class allows you to avoid hard-coding class names unnecessarily. This in turn will of course make your code easier to maintain.

NOTE The access modifier public is optional when applied to a constructor (or any other method, for that matter), but it certainly doesn’t hurt to use it. You may still create a constructor using the class name, but adopting the style of PHP 5 now will avoid any future backward-compatibility issues.

Modifying Your Class

You’ve upgraded the syntax of your code to PHP 5 standards, but you still need to improve the functionality of your DirectoryItems class. This involves rewriting the constructor to make it do a bit more work and adding more methods to the class. The additional methods will improve the flexibility of the class by filtering for specific file types.

Reconstructing the Constructor

Currently, the constructor for the DirectoryItems class uses an array to keep track of filenames. The underutilization of the capabilities of an array suggest changes to the constructor.
Arrays in PHP are very flexible—they can be either numerical or associa- tive. The current constructor simply stores the filenames in a numeric array, but if you change this to an associative array, you can make better use of the data member $filearray. Since all operating systems require that each filename within a directory be unique, the filename is ideal for acting as a key in an associative array. Let’s see how you might take advantage of this.
When properly ordered and created, a directory and its subdirectories can function like a database and its tables; in fact, for some databases, a table is a directory and its contents.
If you consider the DirectoryItems class as a table and the files in the array as “records,” then, if you set things up in just the right way, filenames can function as the “title” field for each file in that database.
You can implement this by using a strict naming convention for all your files. For example, if all your files are formatted using underscores to separate words (Lady_of_Shallott.jpg, for instance), then by replacing underscores with spaces and stripping out filename extensions, the filename alone can serve as the title for each image when it is displayed.
I won’t reproduce the original code for the constructor here, but look back at Chapter 4 if you need to refresh your memory. The code for the new constructor and a private method called from within the constructor is shown in Listing 5-1.

public function __construct($directory, $replacechar = "_"){
$this->directory = $directory;
$this-> replacechar=$replacechar;
$d = "";
if(is_dir($directory)){
$d = opendir($directory) or die("Failed to open directory.");
while(false !== ($f=readdir($d))){
if(is_file("$directory/$f")){
$title = $this-> createTitle($f);
$this->filearray[$f] = $title;
}

}else{

}
closedir($d);

//error

die("Must pass in a directory.");
}
}
private function createTitle($title){
//strip extension
$title = substr($title,0,strrpos($title, "."));
//replace word separator
$title = str_replace($this->replacechar," ",$title);
return $title;
}

Listing 5-1: The constructor and the createTitle method

The original constructor for this class accepted only one parameter—a directory name. You are now passing an additional parameter, $replacechar, and it has a default value of “_”. This parameter will function as the character in a filename and will be replaced by a space in order to make a readable, English “title” from the filename.
By assigning a default value to $replacechar, users of the DirectoryItems
class have three options. They can:

1. Use another replacement character by passing a second value to the constructor (a hyphen, perhaps)
2. Let the second value default to an underscore

3. Simply ignore the existence of this parameter (if they don’t want to use a title)

Next, you copy the character used as a word separator into an instance variable, because you need to reference it not only in the constructor but also in the createTitle method.
In the original version of this class, you did not need to keep track of the directory name passed to the constructor because once it was used in the constructor, it was no longer needed. Because you intend to filter filenames, you now need to preserve the directory name, so you copy it into an instance variable. How you use the variable $directory will become apparent when we discuss the removeFilter method later in this chapter.

NOTE Local variables can have the same name as instance variables because the pseudo- variable $this allows you to distinguish one from the other.

The method createTitle ( ) creates the title for each image by remov- ing the filename extension and replacing the underscores with spaces. This method is reproduced in full starting at .
Notice the use of the access modifier private. This method, the only private method in the entire class, is private because there is no reason to access it except from the constructor. The createTitle method affects the internal behavior of the DirectoryItems class and identifying it as private allows you to indicate that this behavior is internal and hidden rather than external and exposed.

To briefly return to our earlier discussion of access modifiers, another way of describing the difference between public and private access, as far as methods are concerned, is to say that they separate the interface from the implementation. A user programmer need only concern himself with the public methods of a class in order to use it efficiently. In other words, he need not worry about private functions because they represent the inner workings of a class’s implementation. For this reason, you can say that the separation of public and private methods simplifies the use of a class.
In the original version of the constructor presented in Chapter 4, you assigned each filename to an array element, effectively creating a numeric array. In the revised constructor, however, you have created an associative array, with the filename functioning as the key and the title as the value. As noted earlier, you can’t have files in the same directory with duplicate names, so the filename can function as a unique key.

Filtering Content

To this point, you have changed the DirectoryItems class to take advantage of changes to the syntax of PHP, namely by using access modifiers and the “magic” constructor. You’ve also changed the internal workings of the con- structor in order to create a “title.” All that remains is to create the methods that relate to filtering the contents of a directory.
However, there’s no point in filtering if you don’t have to; a directory may already contain only the file type you are interested in. Hence, you need a method to loop through all files and determine whether they are the same type. Listing 5-2 contains this method.

public function checkAllSpecificType( $extension){
$extension = strtolower($extension);
$bln = true;
$ext = "";
foreach ($this->filearray as $key => $value){
$ext = substr($key,(strpos($key, ".") + 1));
$ext = strtolower($ext);
if($extension != $ext){
$bln = false;
break;
}
}
return $bln;
}

Listing 5-2: The checkAllSpecificType method

Listing 5-2 is a simple modification of the method developed in Chap- ter 4—checkAllImages. You can check that a directory contains only a specific file type by passing an $extension to this method. For instance, you can determine if a directory holds only Portable Document Format (PDF) files by passing the value pdf to this method. This method returns true if all file extensions in this directory match pdf.

But in the real world, things aren’t usually quite so tidy. Often a directory holds a variety of file types. If you call the method checkAllSpecificType and it returns false, you know you need to filter the contents of a directory, and that’s what the code in Listing 5-3 does.

public function filter($extension){
$extension = strtolower($extension);
foreach ($this->filearray as $key => $value){
$ext = substr($key,(strpos($key, ".")+1));
$ext = strtolower($ext);
if($ext != $extension){
unset ($this->filearray[$key]);
}
}
}

Listing 5-3: The filter method

If you use the example of Portable Document Format files again, passing the file extension pdf to the filter method removes all other files from
$filearray. This is done by looping through the array and unsetting ele ments that don’t match. If there are a variety of files in a directory and you invoke the filter method, you no longer have a complete list of files.
While this isn’t going to be a problem in some situations, suppose you have a mixture of .pdf files and images in a specific directory and you want to download all the .pdf files and after that, display all the images. Once you have filtered for .pdf files, you need to reset the file array to its original values so that you can view the images. You need a method to remove the filter from a directory.

Resetting the Array

An alternative to resetting the file array would be to construct another instance of the DirectoryItems object. The less radical approach, shown in Listing 5-4, is to remove the filter.

public function removeFilter(){
unset($this-> filearray);
$d = "";
$d = opendir($this-> directory) or die("Couldn't open directory.");
while(false !== ($f = readdir($d))){
if(is_file("$this->directory/$f")){
$title = $this->createTitle($f);
$this->filearray[$f] = $title;
}
}
closedir($d);
}

Listing 5-4: The removeFilter method

This removeFilter method first empties (unsets) the $filearray variable and then repeats the process that occurred in the constructor; namely, it recreates the $filearray variable.
As mentioned earlier when discussing the constructor, the original version of this class discarded the directory name after it was used in the constructor. It’s now apparent that you need this value because you may have to reconstruct the file array from scratch.
You have an existing method—checkAllImages—that reports whether all the files within a directory are image files, but you also require a method that filters out all non-image files. The checkAllSpecificType method won’t do because it filters for one extension only, and there are a variety of different extensions for image files. Hence the need for the imagesOnly method in Listing 5-5 that removes all non-image files from the array instance variable.

public function imagesOnly(){
$extension = "";
$types = array("jpg", "jpeg", "gif", "png");
foreach($this->filearray as $key => $value){
$extension = substr($key,(strpos($key, ".") + 1));
$extension = strtolower($extension);
if(!in_array($extension, $types)){
unset($this->filearray[$key]);
}
}
}

Listing 5-5: The imagesOnly method

This code performs exactly the same function as the checkAllSpecificType method, but it retains files with the four different extensions associated with images rather than just one file type. This is done by looping through all the filenames, extracting the extension and examining whether it appears in
the $types array. Again, to restore the file array to its original state, use the
removeFilter method.

Summary of Changes

In this chapter, we’ve built upon the simple DirectoryItems class that was introduced in Chapter 4 to produce an expanded and upgraded class. As you’ve seen, you needed to make surprisingly few changes to this class in order to implement some of the key changes introduced with PHP 5.
Certainly, the changes described in this chapter are not the only changes to PHP’s OO capabilities; however, one of them—the use of access modifiers— is possibly the most important. The single most glaring shortcoming of OO programming in PHP 4 is this lack of access modifiers. While disciplined use and careful documentation can take you part of the way toward mitigating this deficiency, it’s much better to rely on the structure of the language to enforce the appropriate use of an object.

Not only have you upgraded the DirectoryItems class, but you’ve also expanded its functionality with a view to using it to display a series of images. The ugly duckling class is well on its way to becoming a full-fledged swan.
The DirectoryItems class was created in order to display a directory of images. Further changes are needed to perform this task properly but these changes require the creation of additional classes. In Chapter 6 let’s look at creating a thumbnail image class to produce thumbnails of images.

SHOW ALITTLE CLASS

Introductory books on object-oriented programming (OOP) often use examples of objects taken from the real world. For example, you may be asked to imagine a “dog” class. We are all familiar with dogs, of course, so it’s relatively easy to describe a dog’s attributes. Most dogs have hair, four legs, and a tail. A dog’s behavior is equally easy to describe. Dogs bark, jump, run, roll over, dig, and, when passing fire hydrants . . . I don’t mean to belittle this approach, but the objects that a web developer deals with are not often objects “out there” that one can point to. They are more likely to be conceptual rather than physical objects, and these are a little harder to identify. Once identified, it is not easy to describe the objects’ attributes and behavior. With that in mind, the class I propose you create is a list of files. (I know, it’s not terribly exciting, but by keeping things simple, we can easily deal with some of the basic concepts of OOP.) This class certainly won’t bark or jump, but by the time we’re finished, it may roll over and do a few tricks. NOTE We’ll use the syntax of PHP 4 to help ease into OOP. Starting with PHP 4 will also be helpful for those who have already used OOP with PHP and want to upgrade their code. I’ll show you how to do this in Chapter 5, and for convenience, I have also included an appendix on this topic. (PHP 4 style code will run just fine under PHP 5 but will raise warnings if error reporting is set to E_STRICT in the php.ini file. See Appendix A for the OO configuration options of the php.ini file.) Design OOP doesn’t eliminate the need for systems analysis. It’s easy to forget about this step and to just start coding, especially when dealing with a fairly simple task. However, a little forethought during the design stage will reap benefits later on, so make sure you have a clear idea of what you want to do. Defining the Problem You often need to look at and manipulate the files in a specific directory, and you often want to do this with directories that hold resources such as photos or images, .pdf files, or files that are compressed for downloading. Probably the simplest approach, if your web server is Apache, is not to use any code at all and simply put a .htaccess file containing the directive Options +Indexes into the appropriate directory. By using a .htaccess file, you can simply point your browser to the directory that contains this file to see a list of its contents. Of course, if this were your only goal, then building a class to mimic this functionality would be entirely superfluous. However, you want to do a bit more than just list files. You want to have control over the order in which they appear and the file types that are listed, and you may also want to know the number of files. Consider this fairly specific task: Suppose you have some cleanup work that needs doing on directories that contain graphics. You need to remove deadwood, but before you can do so, you need to view the images. Rather than open each picture individually using an application such as Photoshop or GIMP, you want to open all the files at once in your browser. Not only do you want to see the image, you also want to note the filename of the image in case you decide to remove it. This is not a situation that requires an object-oriented (OO) solution. If you are familiar with PHP, you’ve probably already formulated a rough algo- rithm of how to solve this problem and determined which functions you need to use. If you are a programmer but not familiar with OOP, a procedural approach will doubtless seem more natural and be easier to execute, especially when approaching a straightforward problem. However, remember that we are deliberately trying to keep things simple to begin with. Stick with me at least until the end of the next chapter—you won’t be disappointed. At this early stage, our simple class may not convince you of the utility of OOP, but it will highlight the fact that OOP doesn’t do away with the need for procedural programming. The logic required for OOP is every bit as pro- cedural as the functions you’re used to creating and using. Not the Da Vinci Code We’ll reproduce the code here and intersperse it with comments. (If you would like an overview of the entire class, now would be a good time to download the code for this chapter from the companion website at http:// objectorientedphp.com.) In order to create a class, use the keyword class and an appropriate name: class DirectoryItems{ ... } Braces enclose all the elements of a class, indicated by the ellipsis in the preceding line of code. We discussed the concept of a class in Chapters 2 and 3, but a bit of repeti- tion here won’t be amiss. In its simplest form, a class can simply encapsulate a variety of data types, the way a struct does in C or a type in Visual Basic. This class will encapsulate data types, but it will also contain functions or methods. Like PHP’s built-in classes, we’ll use Java-style naming conventions for the class name—not underscores, but uppercase letters for the start of each word, otherwise known as studly caps. We’ll use the same naming convention for files that contain class definitions. For example, the file that holds the DirectoryItems class will be called DirectoryItems.php. This naming convention is not a requirement but helps readily identify classes and their files. The first statement inside the class is the declaration of the variable $filearray. Upon declaration, this variable is initialized as an array. var $filearray = array(); NOTE Notice the use of the var keyword. This syntax will be replaced in PHP 5, but here it simply denotes an instance variable. Any variable declared at this level, namely inside the braces that enclose the class but outside any class function, is an instance variable or, as we might also refer to it, a data member. (In most cases, classes contain more than one data member, but one is sufficient for the moment.) Instance variables are sometimes also referred to as properties. The placement of instance variables outside of any function indicates that they have scope throughout the class. Their visibility is not restricted to any specific function—they can be accessed from anywhere within the class. You could say they are global to the class. The Constructor Next is a function that bears the same name as the class: the constructor. Constructors are commonly used to initialize data members, and as in Listing 4-1, filenames are added to the instance variable $filearray. function DirectoryItems( $directory){ $d = ""; if(is_dir($directory)){ $d = opendir($directory) or die("Couldn't open directory."); while(false !== ($f=readdir($d))){ if(is_file("$directory/$f")){ $this-> filearray[] = $f; } } closedir($d); }else{ //error die("Must pass in a directory."); } } Listing 4-1: The DirectoryItems constructor Constructors are called whenever an object is created. In Listing 4-1, the constructor accepts, as a parameter, a string variable of a directory name. Any files contained within this directory are added to $filearray. Referencing Instance Variables The only remarkable thing about this code is the unusual syntax required to refer to the instance variable. Variables such as $d and $f, which are local to the constructor, are referenced in the same way as any other PHP variable, but when using $filearray, we must precede it with $this->. If you’re familiar with other OO languages such as C++ or Java, you’ll be familiar with $this, a “pseudo-variable” that identifies what follows as an instance variable. However, unlike those other OO languages, use of $this when referring to an instance variable is not optional in PHP. So much for the explanation of the syntax of the constructor. The constructor actually performs a fairly simple and straightforward program- ming task. Wrapper Methods The rest of the class is made up of a series of functions. Some of these func- tions simply enclose or wrap existing array-related functions and are called wrapper functions. These wrapper functions count or sort the list of filenames, but instead of calling them functions, let’s use OO terminology and refer to them as methods. NOTE When declaring the methods of a class you are required to use the keyword function. This can perhaps lead to some confusion. However, throughout we will use the term method to distinguish between a regular function call and the calling a class function. Again, following the studly caps naming convention, if a method name is a compound word, use lowercase for the first word and uppercase for any subsequent words. Listing 4-2 includes three methods that use built-in PHP array functions. function indexOrder(){ sort($this->filearray); } //////////////////////////////////////////////////////////////////// function naturalCaseInsensitiveOrder(){ natcasesort($this->filearray); } //////////////////////////////////////////////////////////////////// function getCount(){ return count($this->filearray); } Listing 4-2: Wrapper methods Finally, add one final method to check that files are all images: function checkAllImages(){ $bln = true; $extension = ""; $types = array( "jpg", "jpeg", "gif", "png"); foreach ($this->filearray as $key => $value){ $extension = substr($value,(strpos($value, ".") + 1)); $extension = strtolower($extension); if(!in_array($extension, $types)){ $bln = false; break; } } return $bln; } Listing 4-3: A method to select only images The checkAllImages method loops through each element in the file array, extracts the extension, and checks that it is one of the acceptable file types. In sum, the DirectoryItems class is made up of one data member, a special function called a constructor, and four other functions or methods. As already noted, you should save this file as DirectoryItems.php. Creating an Instance A class by itself is of absolutely no use whatsoever, and if you preview in a browser the code created so far, nothing will be displayed. That’s because at this point we don’t really have anything—just the idea for something. We need to create an instance of the class. The many Platonists amongst you will immediately know what we’re talking about. Remember Plato and “ideal forms?” Of course you do—it hasn’t been that long since you took Philosophy 101. The explanation of a form usually involved a chair, because there was always one in the classroom. The form of a chair doesn’t exist anywhere, but any specific chair embodies that form. In other words, each particular chair is an instantiation of the chair class. (If you skipped that lecture, we could say that a class acts as a template for a specific occurrence of a class in much the way that a building relates to its blueprint.) Listing 4-4 is a PHP page that creates an instance of the DirectoryItems class. Briefly, this web page opens a directory immediately below the current working directory, checks that all the files in that directory are graphics files, sorts them in a case-insensitive way, and then displays them. <html> <head> <title>Images</title> </head> <body> <?php require 'DirectoryItems.php'; $di =& new DirectoryItems('graphics'); $di->checkAllImages() or die("Not all files are images."); $di-> naturalCaseInsensitiveOrder(); //get array echo "<div style = \"text-align:center;\">"; foreach ($di-> filearray as $key => $value){ echo "<img src=\"graphics/$value\" /><br />\n"; } echo "</div><br />"; ?> </body> </html> Listing 4-4: Creating an instance of the DirectoryItems class Since we are going to create an instance of the DirectoryItems class, we need to include this class by requiring the file that holds the class defini- tion, namely the file saved as DirectoryItems.php. We create the class instance with the code, $di =& new DirectoryItems('graphics');, where $di is the variable or instance of the object, and new both allocates memory and, in association with the class name, invokes the constructor. (When creating an object under PHP 4, it is advisable to return a reference using the assignment by reference operator, =&. The reason for this is discussed in detail in Chapter 13, in the section “__clone” on page 116.) The constructor for the DirectoryItems class expects a directory name to be passed in. In this case, use graphics, which is assumed to be a directory immediately below the current directory. If the constructor cannot locate this directory, construction fails and the program terminates. But if it’s successful, an array of filenames is created. In this particular case we want to ensure that all the files in the graphics directory are images. After all, we’re going to use this class to set the src attribute of an img tag. The checkAllImages method does this work by looking at filename extensions. The arrow operator we saw when using the pseudo- variable $this, reappears here when we want to call an object method: $di->checkAllImages(). Calling an object method is similar to calling a function in procedural programming. However, instead of passing a variable to a function as is commonly done, a class method is called on a variable, or more properly speaking, an object or instance of a class. This is how objects may be said to behave : they do things rather than having things done to them. Next, perform a case-insensitive sort of the filenames. Directly access the data member, $filearray, and iterate through this array to display each image. As you can see, we’ve created a class and used it to accomplish exactly what we set out to do. What Have You Accomplished? Using the syntax of PHP 4, you have created a class to assist in the display of images in a web page. It is a fairly simple class, and it should be readily apparent how the same job could be done procedurally. However, despite the simplicity of the class and of the task it performs, there are some obvious advantages. On the plus side, you could say the HTML page is fairly clean—the somewhat messy task of determining if all files are image files has been hidden away inside the class file. Additionally, if you want to reuse this code, you won’t have to cut and paste the way you so often do with procedural programming; you need only use the require directive with the class filename, and away you go. But would you want to reuse this class? Skeptics might say that we’ve come the long way around and built a class to solve a specific problem, but that we could have achieved the same effect more quickly using procedural programming. Additionally, they might argue that this class is just an ad hoc solution not easily reused elsewhere. There may be some truth to these criticisms; certainly the more a class is tied to the specifics of a situation, the less reusable it is. However, remember that we set out to create a fairly simple class with the intention of elucidating some of the basics of OO programming. At this point we only have a fledg- ling class that requires more nurturing. But Will It Fly? OO enthusiasts are usually eager to point out the big ways in which OOP is superior to procedural programming, through capabilities such as inheri- tance, for instance. True enough, but probably of more importance is the fact that once you have a class, providing that the basic design is sound, you can easily add to its functionality. If it doesn’t do something you want it to do, the simplest and often the best solution is to add a method to create additional behavior. For example, you could easily add a method modeled on the method checkAllImages that would check for other types of files. Or, suppose some of the files in the directory passed to the constructor are not image files, and you don’t want your program to attempt to display them. This could be remedied with a filter method. I’m sure you can think of other ways in which this class can be improved. The next chapter will improve on this class so that it can be used in a variety of ways, but the focus will be on its use with a direc- tory of image files. Furthermore, some of the shortcomings of this class suggest the creation of additional classes rather than additions to the DirectoryItems class. First, images are of varying sizes. This not only affects the aesthetics of a web page, but, if the images are large, this can significantly slow the rate at which a page downloads. Second, if there are a considerable number of files in one directory, a single web page that displays all of them will be unacceptably long. In later chapters we’ll follow up on both of these ideas. At the beginning of this chapter I promised that we wouldn’t create a dog class, and perhaps instead, we’ve created an ugly duckling. In any case, you’ll want to stick around for another chapter not only to see if our fledgling can fly but also to see whether our ugly duckling turns into a swan.