Tuesday, August 11, 2009

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.

0 comments: