Classes
In the real world, objects have characteristics and behaviors. A car has a color, a weight, a manufacturer, and a gas tank of a certain volume. Those are its characteristics. A car can accelerate, stop, signal for a turn, and sound the horn. Those are its behaviors. Those characteristics and behaviors are common to all cars. Although different cars may have different colors, all cars have a color. OOP enables you to establish the idea of a car as being something with all of those characteristics through the use of a construct known as a class. A class is a unit of code, composed of variables and functions, which describes the characteristics and behaviors of all the members of a set. A class called Car, for example, would describe the properties and methods common to all cars.
In OO terminology, the characteristics of a class are known as its properties. Properties have a name and a value. Some allow that value to be changed while others do not. For example, the Car class would probably have such properties as color and weight. Although the color of the car can be changed by giving it a new paint job, the weight of the car (without cargo or passengers) is a fixed value.
Some properties represent the state of the object. State refers to those characteristics that change due to certain events but are not necessarily directly modifiable on their own. In an application that simulates vehicle performance, the Car class might have a property called velocity. The velocity of the car is not a value that can be changed on its own, but rather is a byproduct of the amount of fuel being sent to the engine, the performance characteristics of that engine, and the terrain over which the car is traveling.
The behaviors of a class—that is, the actions associated with the class—are known as its methods. Methods are implemented as functions. Like functions, methods can accept parameters of any valid data type. Some methods act on external data passed to them as parameters but they can also act on the properties of their object either using those properties to inform actions made by the method, such as when the accelerate method examines the remaining amount of fuel to determine whether the car is capable of accelerating, or to change the state of the object by modifying values such as the velocity of a car.
Objects
To begin with, you can think of a class as a blueprint for constructing an object. In much the same way that many houses can be built from the same blueprint, you can build multiple instances of an object from its class. But the blueprint doesn't specify things like the color of the walls, or type of flooring. It merely specifies that those things will exist. Classes work much the same way. The class specifies the behaviors and characteristics the object will have, but not necessarily the values of those characteristics. An object is a concrete entity constructed using the blueprint provided by a class. So although the idea of a house is analogous to a class, your house (a specific instance of the idea of a house) is analogous to an object.
With a blueprint in hand and some building materials, you can construct a house. In OOP, you use the class to build an object, a process known as instantiation. Instantiating an object requires two things:
-
A memory location into which to load the object. This is automatically handled for you by PHP.
-
The data that will populate the values of the properties. This data could come from a database, a flat text file, another object, or some other source.
A class never has property values or state. Only objects can. You have to use the blueprint to build the house before you can give it wallpaper or vinyl siding. Similarly, you have to instantiate an object from the class before you can interact with its properties or invoke its methods. When to use the word class and when to use the word object is often confusing to those new to OOP. Just remember that classes are manipulated at design-time when you make changes to the methods or properties, and objects are manipulated at runtime when values are assigned to their properties and their methods are invoked.
Once an object is instantiated, it can be put to work implementing the business requirements of the application. Let's look at exactly how to do that in PHP.
Creating a Class
Let's start with a simple example. Save the following in a file called class.Demo.php:
And there you have it—the Demo class. Not terribly exciting just yet, but this is the basic syntax for declaring a new class in PHP. Use the keyword class to let PHP know you're about to define a new class. Follow that with the name of the class and braces to indicate the start and end of the code for that class.
You can instantiate an object of type Demo like this:
To instantiate an object, first make sure PHP knows where to find the class declaration by including the file containing your class (class.Demo.php in this example). Then invoke the new operator, and supply the name of the class and opening and closing parentheses. The return value of this statement is assigned to a new variable, $objDemo in this example. Now you can invoke the $objDemo object's methods and examine or set the value of its properties—if it actually has any.
It's important to have a clearly defined convention for organizing your source code files. A good rule to follow is to put each class into its own file and to name that file class.[ClassName].php.
Even though the class you've created doesn't do much of anything yet, it's still a valid class definition.
Adding a Method
The Demo class isn't particularly useful if it isn't able to do anything, so let's look at how you can create a method. Remember, a method of a class is basically just a function. If you read Chapter 6, "Writing High-Quality Code," (and you did read Chapter 6, right?) you already know how to create a function. By adding a function inside the braces of your class, you're adding a method to that class. Here's an example:
An object derived from your class is now capable of printing out a greeting to anyone who invokes the sayHello method. To invoke the method on your $objDemo object, you need to use the operator -> to access the newly created function. Save the following code in a file called testdemo.php:
The object is now capable of printing a friendly greeting! The -> operator is used to access all methods and properties of your objects.
For those who have had exposure to OOP in other programming languages, please note that the -> operator is always used to access the methods and properties of an object. PHP does not use the dot operator (.) in its OO syntax at all.
Adding a Property
Adding a property to your class is as easy as adding a method. You just declare a variable inside the class to hold the value of the property. In procedural code, if you want to store a value, you assign that value to a variable. In OOP, if you want to store the value of a property, you also use a variable. This variable is declared at the top of the class declaration, inside the braces that bracket the class's code. The name of the variable is the name of the property. If the variable is called $color, you have a property called color.
Open the class.Demo.php file and add the highlighted code:
Adding this new variable, called $name, is all you have to do to create a name property of the Demo class. To use this property, the same -> operator that you saw earlier is used, along with the name of the property. The rewritten sayHello method shows how to access the value of this property.
Create a new file called testdemol.php and add the following:
Save the file and then open it in your Web browser. The strings "Hello Steve!" and "Hello Ed!" print to the screen.
The keyword public is used to let the class know that you want to have access to the following variable from outside the class. Some member variables of the class exist only for use by the class itself and should not be accessible to external code. In this example, you want to be able to set and retrieve the value of the property name. Note that the way the sayHello method works has changed. It now fetches the name value from the property instead of taking a parameter.
You use the variable $this so that an object can get information about itself. You might have multiple objects of a class, for instance, and because you don't know in advance what the name of an object variable will be, the $this variable enables you to refer to the current instance.
In the example, the first call to sayHello prints Steve and the second call prints Ed because the $this variable enables each object to talk to itself without having to know the name of the object variable currently referencing it. Remember that some properties influence the action of certain methods, such as when the accelerate method of the Car class, for example, needs to examine the amount of fuel remaining. The code inside accelerate would use code such as $this->amountOfFuel to access that property.
When accessing properties, you only need one $. The syntax is $obj->property, not $obj->$property.
This often causes confusion for those new to PHP. The property variable is declared as public $property; and accessed using $obj->property.
In addition to the variables that store the values for the properties of the class, there may be other variables declared for use by the internal operations of the class. Both kinds of data are collectively referred to as the class's internal member variables. Some of these are accessible to code outside the class in the form of properties. Others are not accessible and are strictly for internal housekeeping. For example, if the Car class needed to get information from a database for whatever reason, it might keep a database connection handle in an internal member variable. This database connection handle is obviously not a property of the car, but it is something the class needs to carry out certain operations.
Protecting Access to Member Variables
There are three different levels of visibility that a member variable or method can have: public, private, and protected. Public members are accessible to any and all code. Private members are only accessible to the class itself. These are typically items used for internal housekeeping, such as the database connection handle in the Car class that I mentioned earlier. Protected members are available to the class itself, and to classes that inherit from it. Inheritance is defined and discussed in detail later in this chapter.
Public is the default visibility level for any member variables or functions that do not explicitly set one, but it is good practice to always explicitly state the visibility of all the members of the class.
By creating get and set functions for all your properties it becomes much easier to add data validation, new business logic or other changes to your objects in the future. Even if the current business requirements for your application involve no data validation of a given property, you should still implement that property with get and set functions so you can add validation or business logic functionality in the future.
As the preceding example shows, you can set the value of the name property to just about anything you want—including an object, an array of integers, a file handle, or any other nonsensical value. However, you don't get an opportunity to do any sort of data validation or update any other values when the name property is set.
To work around this problem, always implement your properties in the form of functions called get[property name] and set[property name] as you'll see in the following Try It Out section.
Try it Out: Access Properties with get and set Methods Make the changes highlighted in the following to class.Demo.php:
Edit testdemo.php as shown here:
How it Works
As you can see, the member access level of name has changed from public to private and been prefixed it with an underscore. The underscore is a recommended naming convention to indicate private member variables and functions, however it is just a convention and is not required by PHP. The keyword private protects code outside the object from modifying this value. Private internal member variables are not accessible from outside the class. Because you can't access these variables directly, you're forced to use the getName() and setName() methods to access this information, ensuring that your class can examine the value before allowing it to be set. In this example, an exception is thrown if an invalid value is supplied for the name property. Additionally, the public access specifier for the functions has been added. Always use get and set functions for your properties. Changes to business logic and data validation requirements in the future will be much easier to implement.
Using _Get and _Set
The imperative to use get and set methods for all of your properties is advice you should take. However large objects with dozens of properties are going to take a fairly large amount of code to implement. This effort is especially unwelcome if there are no immediate business logic requirements for most of the properties. This is where methods called __get and __set come in handy.
If you try to access a property of an object, but there is no public member variable with that name, PHP looks to see if a function called __get has been defined for the object. If it has, PHP automatically calls __get to try to determine the value for the property. Similarly, when setting a property that does not exist, PHP invokes a function called __set, if it's has been defined.
Try it Out: Use _get and _set This example shows how to use the __get and __set functions to make handling properties easier. Create a file called class.PropertyObject.php and enter the following code into it:
To test this new class, create a file called testpropertyobject.php and enter the following:
Run testpropertyobject.php in your Web browser. You should see the sayHello message, "Hi! My name is Bob. I was born on March 5, 1977," followed by a rather ugly error message letting you know that the value 'blue' is invalid for the dateofbirth property.
How it Works
This enables you to define all the valid properties that this object can have in an array, whose values you initialize to some sort of reasonable default value (in this case null). Any attempts to access properties for which there is no public member variable are automatically passed by PHP to the __get function.
The implementation of __get in this example first validates that the property has been defined in the private $_properties array. If it hasn't been defined, an exception is thrown. If the property has been defined, you examine the object itself to see if a function called get[property name] has been defined. If that function exists, you call it by using the call_user_func method. The method calls a procedural function if the first parameter is a string (where the string contains the name of the function to be called). If the first parameter is an array, in the form array ($object, $methodName), then __get invokes the method $methodName on the object $object. In this case, you tell it to call the function get[property name] on $this. If there's no function called get[property name], then you just return the value of the item in the $_properties array at the array index corresponding the property name.
You do the same with the __set method. First, validate that the property has been defined, and then look for a function called set[property name], invoking it if it exists, and passing to it the value to be set. If that method doesn't exist, simply set the value of the item in the properties array having the key equal to the name of the property.
In this example, the setDateOfBirth method validates the date supplied to this property. The attempt to set the date of birth to the string ' blue' throws an exception.
The power of this technique is that it lets you use simple variables to store property values, but by simply creating a new function, you can easily add business logic and data validation without having to rewrite any of the code that might be using your class. It's also worth mentioning that the syntax of setting properties with $obj->myProperty = 'foo' is more intuitive than calling a function to set a simple property. You'll see the PropertyObject class a few more times in this chapter and the next (Chapter 13) to look at ways of improving it. By the end of the next chapter, you'll have a powerful utility class that you can use in all your projects. Initializing Objects
For many of the classes you create, you'll need to do some special set up when an object of that class is first instantiated. You might need to fetch some information from a database, or initialize some property values, for example. By creating a special function called __construct(), you can perform any activities required to instantiate the object. PHP automatically calls this special function when instantiating the object.
For example, you could rewrite PropertyObject class in the following way:
The __construct function is invoked automatically when you instantiate a new object of class PropertyObject.
For PHP4 users: In PHP4, object constructors were functions with the same name as the class. PHP5 changed this to use a unified constructor scheme. For backward compatibility, PHP first looks for a function called __construct, but if none is found, it still looks for a function with the same name as the class.
You can also pass parameters to the constructor. These parameters could be used to initialize the values of the properties with data, or to fetch some information from a database. The following example shows the use of the PostgreSQL functions to get some information about a user from a database. This class reuses the same __get and __set methods from the PropertyObject class.
Try it Out: Populate An Object in its Constructor Save the following in a file called class.User.php. The unhighlighted code can be copied from class.PropertyObject.php. You will need a running MySQL database to use this example. If you have a different database available to you, feel free to substitute the mysql_ functions for those appropriate to your database platform. Be sure to change the connection string to match that required by your environment.
Create a file called testuser.php and enter the following:
If you have a working MySQL database to test against, run the following SQL statement to create the users table.
You can also create a dummy value to test the example against by using the following INSERT statement:
If the user ID supplied to the constructor does not exist in the database and you don't wrap the instantiation of $obj in a try...catch...block, you'll see something like the following printed to the screen:
How it Works
You created a private member variable $_hDB to store the resource handle to your database connection. You then added the constructor function __construct and gave it a parameter $userID. When creating new objects of type User, you pass in the ID of the user you want to fetch. This value represents the primary key of a table in the database where you store information about the user.
The constructor opens a connection to the database and stores the resource handle in $this->_hDB. Call mysql_select_db to specify the name of the database to which you want to connect. You then issue a SELECT statement to the database to fetch all the information about the user. The values you're interested in are written to the $_properties array where they will be used by __get, __set, and any get[property name] or set[property name] functions you might create.
In a real-world application, you'd store the database connection information in a globally included file (see Chapters 9, 10, and 11) to make it easier to change the connection parameters or database name, should that become necessary. That way there's only one place where that information is kept, making it easier to change rather than having to revisit many different files to update the information.
By using a constructor with a parameter, you can initialize all the property values by fetching them from the database. In this case you retrieve information about this user. Of course in a real-world application, you'd make sure line 4 of testuser.php is wrapped in a try...catch...block to catch any errors that might occur when connecting to the database or fetching this user.
Also note that the set ID function throws an exception if you attempt to change the ID property. Because the ID is the primary identifier of this user, you should not allow modifications to that value.
If you have a class that does not require any special initialization code to be run, there is no need to create a constructor. As we saw in the very simple preceding Demo class, PHP automatically does what it needs to do to create that object. Create a constructor function only when you need one.
Destroying Objects
The object variables that you create are removed from system memory when the requested page has completed running, when the variable falls out of scope, or when it is explicitly set to null. In PHP5 you can trap the destruction of the object and take actions when that happens. To do so, create a function called __destruct with no parameters. Before the object is destroyed, this function, if it exists, is called automatically.
This gives you the opportunity to perform any last minute clean up, such as closing file handles or database connections that might have been opened by the class, or any other last-minutes housekeeping that might need to be done before the object is destroyed.
Try it out: Create a Destructor The following example uses the code from the earlier User class with a destructor added to demonstrate a really powerful technique. The following code changes allow User objects to automatically save their own changes to the database. When one of the object's properties is changed, you make a note of that change. When the object is destroyed, you look to see which properties, if any, were altered, and then update the database with these new values.
Make the following changes to class.User.php:
Alter testuser.php as shown here:
How it Works
You first create a new private member variable called $_changedProperties. The __set method is changed to keep track of any properties that might have been modified in this variable. When __set iscalled, you look at both the name of the property being changed and its value. If the new value is different from the old value, you make a note of the change and set the new value. There's no need to make note of the change more than once, so you also check to see that this property has not already been added to the $_changedProperties array.
Then add the destructor by creating the __destruct method. In this example, __destruct has two responsibilities:
-
To see if anything was changed in this object. If any changes were made, it constructs an UPDATE statement to be issued to the database. The UPDATE statement updates only those values that were actually modified.
-
It closes your connection to the database server. After writing any changes back to the database, you still have an open database connection resource that needs to be closed. This was the connection initially opened in the constructor. By keeping that connection open throughout the lifetime of the object, you can use that connection to perform other database queries without having to incur the overhead of opening a new connection each time you need it. By closing the connection when you're done with the object, you save system resources.
After $obj2 has said hello, the script is done executing and PHP begins the process of cleaning up any unused variables, including $obj and $obj2. Part of that clean-up process checks to see if a __destruct method has been defined. If so, that method is invoked on the object prior to removing it from system memory. In this example, that happens at the end of testuser.php.
Look at how powerful this technique can be. You can fetch a user from the database, change a property of that user and automatically write the changed information back to the database with only two lines of code in testuser.php. If nothing is changed, as was the case for $obj2, you don't need to go back to the database, saving load on the database server and improving the performance of the application.
Users of the object do not necessarily need to understand its internals. If a senior member of the software development team wrote the User class, she would be able to give this object to a junior member, who perhaps doesn't understand SQL as well, and the junior member of the team would be able to put this object to use without any knowledge whatsoever of where the data comes from or how to save changes to it. In fact, you could change the data source from a MySQL database to a PostgreSQL database or even to an XML file without the junior team member ever knowing, and without ever having to touch any of the code that uses this class.
Inheritance
If you were creating an application to handle inventory at a car dealership, you'd probably need classes such as Sedan, PickupTruck, and MiniVan that would correspond to the same types of automobiles in the dealer's inventory. Your application would need to show not only how many of these items were in stock, but also report on the characteristics of these vehicles so the salespeople could ably give the information to customers.
A sedan is a four-door car and you'd want to record the back seat space and the trunk capacity. A pickup truck doesn't have a trunk, but it has a cargo bed with a certain capacity, and a towing capacity. A minivan has a number of sliding doors (either one or two) and a number of seats inside.
But each of these vehicles is really just a different type of automobile and as such would share a number of characteristics in your application, such as color, manufacturer, model, year, vehicle identification number, and so on. To ensure that each of the classes has these same properties, you could copy the code that creates those properties into each of the files containing your class definitions. As mentioned earlier, though, one of the benefits of an OOP approach is code reuse, so of course you don't need to copy code, but instead can reuse the properties and methods of these classes through a process called inheritance. Inheritance is the capability of a class to assume the methods and properties of a parent class.
Inheritance enables you to define a base class—in this case, Automobile—and say that other classes are a type of Automobile and as such have all the same properties and methods that all Automobiles have. You can say that a Sedan is an Automobile, and the Sedan would automatically inherit everything defined by the Automobile class without your having to copy any code. Then you only need to write the additional properties and methods of the Sedan class that are not shared by all automobiles. The only work left for you to do is define the differences; the similarities between the classes are inherited from the base class.
The capability to reuse code is one benefit, but there's a second major advantage to using inheritance. Let's say you have a class called Customer with a method buyAutomobile. This method would take one parameter, an object of class Automobile, and its internal operations would print the paperwork needed to document the sale and would decrement the car in question from the inventory system. Because all Sedans, PickupTrucks, and MiniVans are Automobiles, you can pass objects of these classes to a function expecting an Automobile. Because the three specific types inherit from the more generic parent class, you know that they will all have the same base set of properties and methods. As long as you only need the methods and properties common to all Automobiles you can accept objects of any class that inherits from Automobile.
Here's another example: cats. All cats share some properties. They eat, sleep, purr, and hunt. They also have shared properties—weight, fur color, whisker length, and running speed. However, lions have a mane of a certain length (at least the male lions do) and they growl. Cheetahs have spots. Common house cats have neither of these things, yet all of these animals are cats.
In PHP you specify that a class is a subset of another by using the keyword extends, which tells PHP that the class you are declaring should inherit all the properties and methods from its parent class, and that you are adding functionality or providing some additional specialization to that class.
If you had to design an application to handle zoo animals, you'd probably need to have classes Cat, Lion, and Cheetah. Before writing any code, plan your class hierarchy in UML diagrams so you have something to work from when you write the code and the documentation of those classes. (We'll take a closer look at UML in Chapter 13, so don't worry if you can't completely understand what's shown here.) Your class diagram should indicate a parent class Cat with subclasses Lion and Cheetah inheriting from it.
Both the Lion and Cheetah classes inherit from Cat, but the Lion class also implements the property maneLength and the method roar(), whereas Cheetah adds the property numberOfSpots.
The Cat class can be implemented as follows:
This simple class sets up all the properties and methods common to all cats. To create the Lion and Cheetah classes, you could copy all the code from the Cat class to classes called Lion and Cheetah, but that generates two problems. First, if you find a bug in the Cat class, you have to know to fix it in the Lion and Cheetah classes, as well. This creates more work for you, not less (and creating less work is supposed to be one of the primary advantages of an OO approach).
Second, imagine you had a method of some other object that looked like this:
Although it may not be a terribly safe idea to pet a lion or cheetah, they will purr if they let you get close enough to do so. You should be able to pass an object of class Lion or Cheetah to the petTheKitty() function.
So you need to take the other route to creating the Lion and Cheetah classes, and that's to use inheritance. By using the keyword extends and specifying the name of the class that is extended, you can easily create two new classes that have all the same properties as a regular cat but provide some additional features. For example:
And that's it! With the Lion class extending Cat you can now do something like the following testlion.php file:
So you can invoke the properties and methods of the child class Cat without having to rewrite all that code. Remember that the extends keyword tells PHP to automatically include all of the functionality of a Cat along with any Lion-specific properties or methods. It also tells PHP that a Lion object is also a Cat object and you can now call the petTheKitty() function with an object of class Lion, even though the function declaration uses Cat as the parameter hint:
In this way, any changes you make to the Cat class are automatically inherited by the Lion class. Bug fixes, changes to function internals, or new methods and properties are all passed along to the subclasses of a parent class. In a large, well-designed object hierarchy, this can make bug fixing and the addition of enhancements very easy. A small change to one parent class can have a large effect on the entire application.
Try it Out: Create the Cheetah Class In this example, you'll see how a custom constructor can be used to extend and specialize a class. Create a new file called class.Cheetah.php and enter the following:
Enter the following code into testcheetah.php:
How it Works
The Cheetah class adds a new public member variable called numberOfSpots and a constructor that did not exist in the parent Cat class. Now when you create a new Cheetah, the maxSpeed property (inherited from Cat) is initialized to 100 kilometers per hour (roughly 60 miles per hour), which is the approximate maximum speed of a cheetah over short distances. Because a default value for the Cat class isn't specified, the maxSpeed evaluates as 0 (actually, null) in the petTheKitty() function. Given how much house cats sleep, their maximum speed probably is approaching 0!
By adding new functions, properties, or even constructors and destructors, the functionality of subclasses of a parent class can easily be extended. In other words, you can add new features and capabilities to your application with a minimum amount of code. You'll see a much more powerful example of this toward the end of the chapter when you extend the PropertyObject class to the classes of the contact manager application.
When you can say that a class is a special type of another class, use inheritance to maximize the potential for code reuse and increase the flexibility of your application.
Overriding Methods
Just because a child class inherits from a parent doesn't mean that the child class necessarily needs to use the parent class's implementation of a function. For example, if you were designing an application that needed to calculate the area of different geometric shapes, you might have classes called Rectangle and Triangle. Both of these shapes are polygons, and as such these classes will inherit from a parent class called Polygon.
The Polygon class will have a property called numberOfSides and a method called getArea. All polygons have a calculable area, although the methods for calculating that area can be different for different types of polygons. The formula for the area of a rectangle, for example, is w*h where w is the width of the rectangle and h is the height. The area of a triangle is calculated as (h/2)*b where h is the height of a triangle with base b.
For each subclass of Polygon that you create, you probably need an area method different from the default implementation. By simply redefining that method for the specific class you can provide your own implementation.
In the case of the Rectangle class, for example, you create two new properties, height and width, and override the Polygon class's implementation of the getArea method (the general formula for the area of a noncomplex polygon—which would be the Polygon class's getArea method—is rather complicated, and because you're going to override it for all of your subclasses, there's really no need to go into that formula here). For the Triangle class, you would probably add properties to store information about the three angles, the height, and length of the base segment, and use the height and base length to override the getArea method. By using inheritance and overriding methods of the parent class, you can allow the subclasses to specialize their implementations of those methods.
A function that takes Polygon as a parameter and needs to print the area of that polygon automatically calls the getArea method of the subclass of Polygon that was passed to it (that is, Rectangle or Triangle). This capability for an OO programming language to automatically determine at runtime which getArea method to call is known as polymorphism. Polymorphism is the capability of an application to do different things based on the particular object it is acting on. In this case, that means invoking a different getArea method.
Override a method in a subclass when the parent class's implementation is different from that required by the subclass. This enables you to specialize the activities of that subclass.
Sometimes you want to retain the implementation provided by the parent class but also perform some additional activities in the method of the subclass. Let's say, for example, that you have an application that manages a nonprofit organization, and that you have a class called Volunteer that has a method called signUp. This method enables volunteers to sign up for a community service project, and then adds the users to the list of volunteers for that activity.
However, you might have some users with restrictions, such as a criminal background, that should prevent them from signing up for certain projects. In this case, polymorphism enables you to create a class called RestrictedUser with an overridden signUp method that first checks the restrictions on the user account against the properties of the project and prevents users from signing up if their restrictions do not permit them to volunteer for a particular activity. If their restrictions do not prohibit them from participating, then you invoke the actions of the parent class to complete their registration.
When you override methods of the parent class, you do not necessarily need to completely rewrite the method. You can continue to use the implementation provided by the parent but add additional specialization for your subclass. In this way, you can reuse code and also provide customizations as required by the business rules.
The capability for one class to inherit the methods and properties of another class is one of the most compelling features of an object-oriented system and enables you to gain an incredible level of efficiency and flexibility in your applications.
Try it Out: Override Inherited Methods In this example, you create two classes—Rectangle and Square. A square is a special kind of rectangle. Anything you can do with a rectangle you can do with a square, but a rectangle has two different side lengths and a square has only one, you need to do some things differently.
Create a file called class.Rectangle.php and add the following code:
This is a fairly straightforward implementation of a class to model a rectangle. The constructor takes parameters for the width and height and the area function calculates the area of the rectangle by multiplying them together.
Here's class.Square.php:
This code overrides both the constructor and the getArea() method. In order for a square to be a square, both sides must be of the same length. As a result, you need only one parameter for the constructor. If more than one parameter is passed to the function, any values after the first are ignored.
Test it using the following script (testsquare.php):
This code echoes out the expected value for the area of a square with sides of length 7.
PHP does not raise an error if the number of parameters passed to a user-defined function is greater than the number of parameters established in the function declaration. There are a few cases where this is actually desired behavior. If you'd like to learn more, see the documentation of the built-in func_get_args() function.
It also overrides the getArea() function. The implementation in the Rectangle class would have returned a perfectly correct result for Square objects, but the method was overridden to improve application performance (although in this case the performance benefit is miniscule). It is faster for PHP to fetch one property and compute its square than to fetch two properties and multiply them together.
By overriding constructors, destructors, and methods you can alter aspects of how subclasses operate.
Preserving the Functionality of the Parent
Sometimes, you want to preserve the functionality provided by the parent. You don't need to completely override the function, you just need to add something to it. You could copy all the code from the parent method into the subclass' method, but as you've already seen, OOP offers you better ways of doing this than just copying lines of code.
To call the functionality provided by the parent, use the syntax parent::[function name]. To just add additional behavior to a method, first call parent::[function name] and then add your additional code. When extending a function in this way, always call the method on the parent before doing anything else. This ensures that any changes to the operation of the parent won't break your code.
Because the parent class may be expecting the object to be in a certain state, or may alter the state of the object, overwrite property values, or manipulate the objects internal data, always invoke the parent method before adding your own code when extending an inherited method.
Let's take a look at how this works.
Try it Out: Preserve the Functionality of a Parent Method This example has two classes—Customer and SweepstakesCustomer. A supermarket has an application that from time to time switches which class is being used in the cash register application when certain promotions are run. Each customer who comes in has his own ID value (which comes from a database) as well as a customer number, which indicates how many customers have come to the supermarket before him. For this sweepstakes, the one millionth customer wins a prize.
Create a file called class.Customer.php and add the following:
Create a file called class.SweepstakesCustomer.php and type in this code:
To see how to use this class, create a file called testCustomer.php and enter the following code:
Run testCustomer.php in your browser with the $promotionCurrentlyRunning variable set first to false, then to true. When the value is true, the prize message is displayed.
How it Works
The Customer class initializes values from the database based on the customer ID. You would most likely retrieve the customer ID from a loyalty program swipe card like the type available at most larger grocery store chains. With the customer ID, you can fetch his personal data from the database (just hard coded in this example) along with an integer value representing how many customers have entered the store before him. Store all of this information in public member variables.
The SweepstakesCustomer class adds a bit of extra functionality to the constructor. You first invoke the parent class's constructor functionality by calling parent::_construct and pass to it the parameters it expects. You then look at the customerNumber property. If this customer is the one-millionth customer, you inform him that he's won a prize.
Interfaces
There are times when you have a group of classes that are not necessarily related through an inheritance-type relationship. You may have totally different classes that just happen to share some behaviors in common. For example, both a jar and a door can be opened and closed, and are in no other way related. No matter the kind of jar or the kind of door, they both can carry out these activities, but there is no other common thread between them.
In OOP, an interface enables you to specify that an object is capable of performing a certain function but it does not necessarily tell you how the object does so. An interface is a contract between unrelated objects to perform a common function. An object that implements the interface is guaranteeing to its users that it is capable of performing all of the functions defined by the interface specification. Bicycles and footballs are totally different things, yet objects representing those items in a sporting goods store inventory system must be capable of interacting with that system.
By declaring an interface and then implementing it in your objects, it's possible to hand completely different classes to common functions. The following example shows the door-and-jar analogy.
Try it Out: Use Interfaces Create a file called interface. Openable.php:
Just as you name your class files class.[class name].php, you should use the same convention with interfaces and call them interface.[interface name].php.
You declare the interface Openable using syntax similar to that of a class, except you substitute the word interface for the word class. Generally, an interface does not have member variables or specify an implementation of its member functions.
Because no implementation is specified, you declare these functions to be abstract. This tells PHP that any class implementing this interface is responsible for providing an implementation of the functions. If you fail to provide an implementation of all of the abstract methods of an interface, PHP raises a runtime error. You can't selectively choose some of the abstract methods to implement—you must provide implementations of all of them.
An abstract method is one for which no implementation is provided. Abstract methods can appear in interfaces and classes. If a method in a class is declared abstract, then the class itself must be declared abstract by placing the keyword abstract before the word class in the class declaration. An abstract class is one that cannot be instantiated on its own, but must have subclasses that provide a concrete implementation of the abstract methods. Note that abstract method declarations do not involve braces and end with a semicolon.
How it Works
The Openable interface is a contract with other parts of the application that says any class implementing this interface will provide two methods—open() and close()—that take no parameters. With this agreed-upon set of methods you can allow very different objects to pass into the same function without their needing to be an inherited relationship between them.
Create the file class.Door.php:
And the file class.Jar.php:
To use them, create a new file called testOpenable.php in the same directory:
Because both the Door class and the Jar class implement the Openable interface, you can pass both to the openSomthing() function. Because that function accepts only something that implements the Openable interface, you know that you can call the functions open() and close() within it. However, you should not attempt to access the contents property of the Jar class or utilize the lock() or unlock() functions of the Door class within the openSomething() function because that property and those methods are not part of the interface. The interface contract guarantees that you have open() and close() methods and nothing else.
By using interfaces in your application, you can allow completely different and unrelated objects to talk to each other with a guarantee that they will be able to interact on the terms specified in the interface.
Encapsulation
As mentioned earlier in this chapter, objects enable you to hide the details of their implementation from users of the object. For example, a user doesn't need to know whether the Volunteer class stores information in a database, a flat text file, an XML file, or other data storage mechanism to be able to invoke the signUp method. Similarly, he doesn't need to know whether the information about the volunteer contained within the object is implemented as single variables, an array, or even another object. This ability to hide the details of implementation is known as encapsulation. Generally speaking, encapsulation refers to these two concepts: the protection of a class's internal data from code outside that class, and the hiding of the details of implementation.
The word encapsulate literally means to place in a capsule, or outer container. A well-designed class provides a complete outer shell around its internals and presents an interface to code outside the class that is wholly separated from the particulars of those internals. By doing this, you gain two advantages: you can change the implementation details at any time without affecting code that uses your class and, because you know that nothing outside your class can inadvertently modify the state or property values of an object built from your class without your knowledge, you can trust the state of the object and the value of its properties to be valid and to make sense.
You'll recall from earlier in the chapter that the member variables of a class and its functions have a visibility, which refers to what can be seen by code outside the class. Private member variables and functions are not accessible to code outside the class and are used for the class's internal implementation. Protected member variables and functions are only visible to the subclasses of the class. Public member variables and functions are usable by any code, inside or outside of the class.
Generally speaking, all internal member variables of a class should be declared private. Any access needed to those variables by code outside the class should be done through a public method. When someone wants you to try a new food, you don't allow him to insert it directly into your stomach for a good reason—you need to be able to examine the food and determine if it's something you want to allow into your body. The method for giving you food involves handing it to you and allowing you to decide to eat it or not. Similarly, when an object wants to allow code outside of it to change properties or in some other way affect its internal data, by encapsulating access to that data in a public function (and by keeping the internal data private) you have the opportunity to validate the changes and accept or reject them.
For example, if you're building a banking application that handles details of customer accounts, you might have an Account object with a property called totalBalance and methods called makeDeposit and makeWithdrawal. The totalBalance property should be read-only. The only way to affect the balance is to make a withdrawal or a deposit. If the totalBalance property is implemented as a public member variable, you can write code that would increase the value of that variable without having to actually make a deposit. Obviously, this would be bad for the bank. Instead, you implement this property as a private member variable and provide a public method called getTotalBalance, which returns the value of that private member variable. With the variable storing the value of the account balance made private, it can't be manipulated directly. Because the only public methods that affect the account balance are makewithdrawal and makeDeposit, a user has to actually make a deposit if he wants to increase the value of his account.
Encapsulation of internal data and method implementations allows an object-oriented software system to protect and control access to data and to hide the details of implementation, giving you flexible, stable applications.