Tuesday, August 11, 2009

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.

0 comments: