Chapter 6 introduced the fundamentals of object-oriented programming (OOP). This chapter builds on that foundation by introducing several of PHP’s more advanced OOP features. Specifically, this chapter introduces the following five features:
Object cloning: One of the major improvements to PHP’s object-oriented model in version 5 is the treatment of all objects as references rather than values. However, how do you go about creating a copy of an object if all objects are treated as references? By cloning the object.
Inheritance: As discussed in Chapter 6, the ability to build class hierarchies through inheritance is a fundamental OOP concept. This chapter introduces PHP’s inheritance features and syntax, and it includes several examples that demonstrate this key OOP feature.
Interfaces: An interface is a collection of unimplemented method definitions and constants that serves as a class blueprint. Interfaces define exactly what can be done with the class, without getting bogged down in implementation-specific details. This chapter introduces PHP’s interface support and offers several examples demonstrating this powerful OOP feature.
Abstract classes: An abstract class is a class that cannot be instantiated. Abstract classes are intended to be inherited by a class that can be instantiated, better known as a concrete class. Abstract classes can be fully implemented, partially implemented, or not implemented at all. This chapter presents general concepts surrounding abstract classes, coupled with an introduction to PHP’s class abstraction capabilities.
Namespaces: Namespaces help you to more effectively manage your code base by compartmentalizing various libraries and classes according to context. In this chapter I’ll introduce you to PHP 5.3’s new namespace feature.
■ Note All the features described in this chapter are available only for PHP 5 and newer.
Advanced OOP Features Not Supported by PHP
If you have experience in other object-oriented languages, you might be scratching your head over why the previous list of features doesn’t include certain OOP features supported by other programming languages. The reason might well be that PHP doesn’t support those features. To save you from further wonderment, the following list enumerates the advanced OOP features that are not supported by PHP and thus are not covered in this chapter:
Method overloading: The ability to implement polymorphism through method overloading is not supported by PHP and probably never will be.
Operator overloading: The ability to assign additional meanings to operators based upon the type of data you’re attempting to modify is currently not supported by PHP. Based on discussions found in the PHP developer’s mailing list, it is unlikely that this feature will ever be implemented.
Multiple inheritance: PHP does not support multiple inheritance. Implementation of multiple interfaces is supported, however.
Only time will tell whether any or all of these features will be supported in future versions of PHP.
Object Cloning
One of the biggest drawbacks to PHP 4’s object-oriented capabilities was its treatment of objects as just another datatype, which impeded the use of many common OOP methodologies, such as design patterns. Such methodologies depend on the ability to pass objects to other class methods as references, rather than as values. Thankfully, this matter has been resolved with PHP 5, and now all objects are treated by default as references. However, because all objects are treated as references rather than as values, it is now more difficult to copy an object. If you try to copy a referenced object, it will simply point back to the addressing location of the original object. To remedy the problems with copying, PHP offers an explicit means for cloning an object.
Cloning Example
You clone an object by prefacing it with the clone keyword, like so:
destinationObject = clone targetObject;
Listing 7-1 presents an object-cloning example. This example uses a sample class named
Corporate_Drone, which contains two properties (employeeid and tiecolor) and corresponding getters and setters for these properties. The example code instantiates a Corporate_Drone object and uses it as the basis for demonstrating the effects of a clone operation.
Listing 7-1. Cloning an Object with the clone Keyword
<?php
class Corporate_Drone { private $employeeid;
private $tiecolor;
// Define a setter and getter for $employeeid function setEmployeeID($employeeid) {
$this->employeeid = $employeeid;
}
function getEmployeeID() { return $this->employeeid;
}
// Define a setter and getter for $tiecolor function setTieColor($tiecolor) {
$this->tiecolor = $tiecolor;
}
function getTieColor() { return $this->tiecolor;
} }
// Create new Corporate_Drone object $drone1 = new Corporate_Drone();
// Set the $drone1 employeeid property $drone1->setEmployeeID("12345");
// Set the $drone1 tiecolor property $drone1->setTieColor("red");
// Clone the $drone1 object $drone2 = clone $drone1;
// Set the $drone2 employeeid property $drone2->setEmployeeID("67890");
// Output the $drone1 and $drone2 employeeid properties
printf("Drone1 employeeID: %d <br />", $drone1->getEmployeeID());
printf("Drone1 tie color: %s <br />", $drone1->getTieColor());
printf("Drone2 employeeID: %d <br />", $drone2->getEmployeeID());
printf("Drone2 tie color: %s <br />", $drone2->getTieColor());
?>
Executing this code returns the following output:
Drone1 employeeID: 12345 Drone1 tie color: red Drone2 employeeID: 67890 Drone2 tie color: red
As you can see, $drone2 became an object of type Corporate_Drone and inherited the property values of $drone1. To further demonstrate that $drone2 is indeed of type Corporate_Drone, its employeeid property was also reassigned.
The __clone() Method
You can tweak an object’s cloning behavior by defining a __clone() method within the object class. Any code in this method will execute directly following PHP’s native cloning behavior. Let’s revise the Corporate_Drone class, adding the following method:
function __clone() {
$this->tiecolor = "blue";
}
With this in place, let’s create a new Corporate_Drone object, add the employeeid property value, clone it, and then output some data to show that the cloned object’s tiecolor was indeed set through the __clone() method. Listing 7-2 offers the example.
Listing 7-2. Extending clone’s Capabilities with the __clone() Method // Create new Corporate_Drone object
$drone1 = new Corporate_Drone();
// Set the $drone1 employeeid property
$drone1->setEmployeeID("12345");
// Clone the $drone1 object
$drone2 = clone $drone1;
// Set the $drone2 employeeid property
$drone2->setEmployeeID("67890");
// Output the $drone1 and $drone2 employeeid properties
printf("Drone1 employeeID: %d <br />", $drone1->getEmployeeID());
printf("Drone2 employeeID: %d <br />", $drone2->getEmployeeID());
printf("Drone2 tie color: %s <br />", $drone2->getTieColor());
Executing this code returns the following output:
Drone1 employeeID: 12345 Drone2 employeeID: 67890 Drone2 tie color: blue
Inheritance
People are adept at thinking in terms of organizational hierarchies; we make widespread use of this conceptual view to manage many aspects of our everyday lives. Corporate management structures, the Dewey Decimal system, and our view of the plant and animal kingdoms are just a few examples of systems that rely heavily on hierarchical concepts. Because OOP is based on the premise of allowing humans to closely model the properties and behaviors of the real-world environment we’re trying to implement in code, it makes sense to also be able to represent these hierarchical relationships.
For example, suppose that your application calls for a class titled Employee, which is intended to represent the characteristics and behaviors that one might expect from a company employee. Some class properties that represent characteristics might include the following:
• name: The employee’s name
• age: The employee’s age
• salary: The employee’s salary
• yearsEmployed: The number of years the employee has been with the company Some Employee class methods might include the following:
• doWork: Perform some work-related task
• eatLunch: Take a lunch break
• takeVacation: Make the most of those valuable two weeks
These characteristics and behaviors would be relevant to all types of employees, regardless of the employee’s purpose or stature within the organization. Obviously, though, there are also differences among employees; for example, the executive might hold stock options and be able to pillage the company while other employees are not afforded such luxuries. An assistant must be able to take a memo, and an office manager needs to take supply inventories. Despite these differences, it would be quite inefficient if you had to create and maintain redundant class structures for those attributes that all classes share. The OOP development paradigm takes this into account, allowing you to inherit from and build upon existing classes.
Class Inheritance
Class inheritance in PHP is accomplished by using the extends keyword. Listing 7-3 demonstrates this ability, first creating an Employee class and then creating an Executive class that inherits from Employee.
■
Note A class that inherits from another class is known as a child class, or a subclass. The class from which the child class inherits is known as the parent, or base class.
Listing 7-3. Inheriting from a Base Class
<?php
// Define a base Employee class class Employee {
private $name;
// Define a setter for the private $name property.
function setName($name) {
if ($name == "") echo "Name cannot be blank!";
else $this->name = $name;
}
// Define a getter for the private $name property function getName() {
return "My name is ".$this->name."<br />";
}
} // end Employee class
// Define an Executive class that inherits from Employee class Executive extends Employee {
// Define a method unique to Employee function pillageCompany() {
echo "I'm selling company assets to finance my yacht!";
}
} // end Executive class
// Create a new Executive object $exec = new Executive();
// Call the setName() method, defined in the Employee class $exec->setName("Richard");
// Call the getName() method echo $exec->getName();
// Call the pillageCompany() method $exec->pillageCompany();
?>
This returns the following: