Chapter
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: PHP treats all objects as references and they can be created with the use of the new operator. With that in mind, 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 , 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 with 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’s namespace feature.
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. It is, however, possible to achieve something that works in a similar way. This is done with the magic methods __set(), __get() and __call() etc. (
http://php.net/manual/en/language.oop5.overloading.php
)
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 and an RFC for its implementation (
https://wiki.php.net/rfc/operator-overloading
), it might someday be implemented.
Multiple inheritance
: PHP does not support multiple inheritance. Implementation of multiple interfaces is supported. And traits as described in the previous chapter provide a way to implement similar functionality.
Only time will tell whether any or all of these features will be supported in future versions of
PHP.
Object Cloning
In PHP the objects are treated as references. Assigning an object to another variable simply creates a second reference to the same object. Manipulating any of the properties will have an effect on the object referenced by both variables. This makes it possible to pass objects to functions and methods. However, because all objects are treated as references rather than as values, it is more difficult to copy an object. If you try to copy a referenced object, to remedy the problems with copying, PHP offers an explicit means for cloning an object.
Let’s first look at an example, Listing
7-1, where an object is assigned to a second variable.
<?php
class Employee {
private $name;
function setName($name) {
$this->name = $name;
}
function getName() {
return $this->name;
}
}
$emp1 = new Employee();
$emp1->setName('John Smith');
$emp2 = $emp1;
$emp2->setName('Jane Smith');
echo "Employee 1 = {$emp1->getName()}
";
echo "Employee 2 = {$emp2->getName()}
";
Listing 7-1Copying an Object
The output
from this example illustrates that even though
$emp1 and
$emp2 look like two different variables, they are both referencing the same object. It will look like this:
Employee 1 = Jane Smith
Employee 2 = Jane Smith
Cloning Example
You
clone an object by prefacing it with the
clone keyword
, like so:
$destinationObject = clone $targetObject;
Listing
7-2 presents an object-cloning example. This example uses a sample class named
Employee
, which contains two properties (
employeeid
and
tiecolor
) and corresponding getters and setters for these properties. The example code instantiates an
Employee object and uses it as the basis for demonstrating the effects of a clone operation.
<?php
class Employee {
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 Employee object
$employee1 = new Employee();
// Set the $employee1 employeeid property
$employee1->setEmployeeID("12345");
// Set the $employee1 tiecolor property
$employee1->setTieColor("red");
// Clone the $employee1 object
$employee2= clone $employee1;
// Set the $employee2 employeeid property
$employee2->setEmployeeID("67890");
// Output the $employee1and $employee2employeeid properties
printf("Employee 1 employeeID: %d <br />", $employee1->getEmployeeID());
printf("Employee 1 tie color: %s <br />", $employee1->getTieColor());
printf("Employee 2 employeeID: %d <br />", $employee2->getEmployeeID());
printf("Employee 2 tie color: %s <br />", $employee2->getTieColor());
Listing 7-2Cloning an Object with the clone Keyword
Executing this code returns the following output:
Employee1 employeeID: 12345
Employee1 tie color: red
Employee2 employeeID: 67890
Employee2 tie color: red
As you can see, $employee2 became an object of type Employee
and inherited the property values of $employee1. To further demonstrate that $Employee2 is indeed of type Employee, 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
Employee class
, adding the following method:
function __clone() {
$this->tiecolor = "blue";
}
With this in place, let’s create a new
Employee 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-3 offers the example.
// Create new Employee object
$employee1 = new Employee();
// Set the $employee1 employeeid property
$employee1->setEmployeeID("12345");
// Clone the $employee1 object
$employee2 = clone $employee1;
// Set the $employee2 employeeid property
$employee2->setEmployeeID("67890");
// Output the $employee1 and $employee2 employeeid properties
printf("Employee1 employeeID: %d <br />", $employee1->getEmployeeID());
printf("Employee1 tie color: %s <br />", $employee1->getTieColor());
printf("Employee2 employeeID: %d <br />", $employee2->getEmployeeID());
printf("Employee2 tie color: %s <br />", $ employee2->getTieColor());
Listing 7-3Extending clone’s Capabilities with the __clone() Method
Executing this code returns the
following
output:
Employee1 employeeID: 12345
Employee1 tie color: red
Employee2 employeeID: 67890
Employee2 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 that 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-4 demonstrates this ability, first creating an Employee class and then creating an Executive class
that inherits from Employee.
<?php
// Define a base Employee class
class Employee {
// 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!";
}
// 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();
?>
Listing 7-4Inheriting from a Base Class
This returns the following:
My name is Richard.
I'm selling company assets to finance my yacht!
Because all employees have a name, the Executive class
inherits from the Employee class
, saving you the hassle of having to re-create the name property and the corresponding getter and setter. You can then focus solely on those characteristics that are specific to an executive, in this case a method named pillageCompany()
. This method is available solely to objects of type Executive, and not to the Employee class or any other class—unless you create a class that inherits from Executive. The following example demonstrates that concept, producing a class titled CEO
, which inherits from Executive:
class Employee {
private $name;
private $salary;
function setName($name) {
$this->name = $name;
}
function setSalary($salary) {
$this->salary = $salary;
}
function getSalary() {
return $this->salary;
}
}
class Executive extends Employee {
function pillageCompany() {
$this->setSalary($this->getSalary() * 10);
}
}
class CEO extends Executive {
function getFacelift() {
echo "nip nip tuck tuck
";
}
}
$ceo = new CEO();
$ceo->setName("Bernie");
$ceo->setSalary(100000);
$ceo->pillageCompany();
$ceo->getFacelift();
echo "Bernie's Salary is: {$ceo->getSalary()}
";
?>
The output will look like this:
nip nip tuck tuck
Bernie's Salary is: 1000000
Because Executive has inherited from Employee, objects of type CEO have all the properties and methods that are available to Executive in addition to the getFacelift() method
, which is reserved solely for objects of type
CEO.
Inheritance and Constructors
A
common question pertinent to class inheritance has to do with the use of constructors. Does a parent class constructor execute when a child is instantiated? If so, what happens if the child class also has its own constructor? Does it execute in addition to the parent constructor, or does it override the parent? Such questions are answered in this section.
If a parent class offers a constructor, it does execute when the child class is instantiated, provided that the child class does not also have a constructor. For example, suppose that the
Employee class
offers this constructor:
function __construct($name) {
$this->setName($name);
}
Then you instantiate the
CEO class
and retrieve the
name property:
$ceo = new CEO("Dennis");
echo $ceo->getName();
It will yield the following:
However, if the child class also has a constructor, that constructor will execute when the child class is instantiated, regardless of whether the parent class also has a constructor. For example, suppose that in addition to the
Employee class
containing the previously described constructor, the
CEO class
contains this
constructor
:
function __construct() {
echo "<p>CEO object created!</p>";
}
Then you instantiate the
CEO class
:
$ceo = new CEO("Dennis");
echo $ceo->getName();
This time it will yield the following output because the
CEO constructor overrides the
Employee constructor:
CEO object created!
My name is
When it comes time to retrieve the
name property, you find that it’s blank because the
setName() method
, which executes in the
Employee constructor, never fires. Of course, you’re probably going to want those parent constructors to also fire. Not to fear because there is a simple solution. Modify the
CEO constructor like so:
function __construct($name) {
parent::__construct($name);
echo "<p>CEO object created!</p>";
}
Again, instantiating the
CEO class
and executing
getName()
in the same fashion as before, this time you’ll see a different outcome:
CEO object created!
My name is Dennis
You should understand that when parent::__construct()
was encountered, PHP began a search upward through the parent classes for an appropriate constructor. Because it did not find one in Executive, it continued the search up to the Employee class, at which point it located an appropriate constructor. If PHP had located a constructor in the Employee class, then it would have fired. If you want both the Employee and Executive constructors to fire, you need to place a call to
parent::__construct() in the Executive constructor.
You also have the option to reference parent constructors in another fashion. For example, suppose that both the
Employee and
Executive constructors should execute when a new
CEO object is created. These constructors can be referenced explicitly within the
CEO constructor
like
so:
function __construct($name) {
Employee::__construct($name);
Executive::__construct();
echo "<p>CEO object created!</p>";
}
Inheritance and Late Static Binding
When
creating class hierarchies, you’ll occasionally run into situations in which a parent method will interact with static class properties that may be overridden in a child class. This has to do with the use of the
self keyword. Let’s consider an example involving a revised
Employee
and
Executive class
:
public static $favSport = "Football";
public static function watchTV()
{
echo "Watching ".self::$favSport;
}
class Executive extends Employee {
public static $favSport = "Polo";
}
echo Executive::watchTV();
Listing 7-6Late Static Binding
Because the
Executive class
inherits the methods found in
Employee, one would presume that the output of this example would be
Watching Polo, right? Actually, this doesn’t happen because the
self keyword
determines its scope at compile time rather than at runtime. Therefore, the output of this example will always be
Watching Football. PHP remedies this issue by repurposing the
static keyword for use when you actually want the scope of static properties to be determined at runtime. To do so, you would rewrite the
watchTV() method
like
this:
public static function watchTV()
{
echo "Watching ".static::$favSport;
}
Interfaces
An
interface
defines a general specification for implementing a particular service, declaring the required functions and constants without specifying exactly how it must be implemented. Implementation details aren’t provided because different entities might need to implement the published method definitions in different ways. The nature of interfaces requires all interface methods to be public.
The point is to establish a general set of guidelines that must be implemented in order for the interface to be considered implemented.
Take, for example, the concept of pillaging a company. This task might be accomplished in a variety of ways, depending on who is doing the dirty work. For example, a typical employee might do his part by using the office credit card to purchase shoes and movie tickets, writing the purchases off as “office expenses,” while an executive might ask his assistant to reallocate funds to a Swiss bank account through the online accounting system. Both employees are intent on pillaging, but each goes about it in a different way. In this case, the goal of the interface is to define a set of guidelines for pillaging the company and then ask the respective classes to implement that interface accordingly. For example, the interface might consist of just two methods:
emptyBankAccount()
burnDocuments()
You can then ask the
Employee
and
Executive
classes to implement these features. In this section, you’ll learn how this is accomplished. First, however, take a moment to understand how PHP 5 implements interfaces. In PHP, an interface is created like so:
interface iMyInterface
{
CONST 1;
...
CONST N;
function methodName1();
...
function methodNameN();
}
An interface is a collection of
method definitions (names and parameter list) that is used as a form of contract when a class implements one or more interfaces. The contract is completed when a class
implements the interface via the implements keyword. All methods must be implemented with the same signature as defined in the interface, or the implementing class must be declared
abstract
(a concept introduced in the next section); otherwise, an error similar to the following will occur:
Fatal error: Class Executive contains 1 abstract methods and must
therefore be declared abstract (pillageCompany::emptyBankAccount) in
/www/htdocs/pmnp/7/executive.php on line 30
The following is the general syntax for implementing the preceding
interface:
class Class_Name implements iMyInterface
{
function methodName1()
{
// methodName1() implementation
}
function methodNameN()
{
// methodNameN() implementation
}
}
Implementing a Single Interface
This
section presents a working example of PHP’s interface implementation by creating and implementing an interface named
iPillage
that is used to pillage the company:
interface iPillage
{
function emptyBankAccount();
function burnDocuments();
}
This interface is then implemented for use by the
Executive class
:
class Executive extends Employee implements iPillage
{
private $totalStockOptions;
function emptyBankAccount()
{
echo "Call CFO and ask to transfer funds to Swiss bank account.";
}
function burnDocuments()
{
echo "Torch the office suite.";
}
}
Because pillaging should be carried out at all levels of the company, you can implement the same interface by the
Assistant class
:
class Assistant extends Employee implements iPillage
{
function takeMemo() {
echo "Taking memo...";
}
function emptyBankAccount()
{
echo "Go on shopping spree with office credit card.";
}
function burnDocuments()
{
echo "Start small fire in the trash can.";
}
}
As you can see, interfaces are particularly useful because, although they define the number and name of the methods and parameters required for some behavior to occur, they acknowledge the fact that different classes might require different ways of carrying out those methods. In this example, the Assistant class
burns documents by setting them on fire in a trash can, while the Executive class
does so through somewhat more aggressive means (setting the executive’s office on
fire).
Implementing Multiple Interfaces
Of
course, it wouldn’t be fair to allow outside contractors to pillage the company; after all, it was upon the backs of the full-time employees that the organization was built. That said, how can you provide employees with the ability to both do their jobs and pillage the company, while limiting contractors solely to the tasks required of them? The solution is to break these tasks down into several tasks and then implement multiple interfaces as necessary. Consider this example:
<?php
interface iEmployee {...}
interface iDeveloper {...}
interface iPillage {...}
class Employee implements IEmployee, IDeveloper, iPillage {
...
}
class Contractor implements iEmployee, iDeveloper {
...
}
?>
As you can see, all three interfaces (iEmployee
, iDeveloper
, and iPillage
) have been made available to the employee, while only iEmployee and iDeveloper have been made available to
the
contractor.
Determining Interface Existence
The
interface_exists() function determines whether an interface exists, returning
TRUE if it does and
FALSE otherwise. Its prototype
follows:
boolean interface_exists(string interface_name [, boolean autoload])
Abstract Classes
An
abstract class is a class that really isn’t supposed to ever be instantiated but instead serves as a base class to be inherited by other classes. For example, consider a class titled Media
, intended to embody the common characteristics of various types of published materials such as newspapers, books, and CDs. Because the Media class
doesn’t represent a real-life entity but is instead a generalized representation of a range of similar entities, you’d never want to instantiate it directly. To ensure that this doesn’t happen, the class is deemed abstract. The various derived Media classes then inherit this abstract class, ensuring conformity among the child classes because all methods defined in that abstract class must be implemented within the subclass.
A class is declared abstract by prefacing the definition with the word abstract, like so:
abstract class Media
{
private $title;
function setTitle($title) {
$this->title = $title;
}
abstract function setDescription($description)
}
class Newspaper extends Media
{
function setDescription($description) {
}
function setSubscribers($subscribers) {
}
}
class CD extends Media
{
function setDescription($description) {
}
function setCopiesSold($subscribers) {
}
}
Attempting to instantiate an abstract class results in the following error message:
Fatal error: Cannot instantiate abstract class Employee in
/www/book/chapter07/class.inc.php.
Abstract classes ensure conformity because any classes derived from them must implement all abstract methods derived within the class. Attempting to forgo implementation of any abstract method defined in the class results in a fatal
error.
Introducing Namespaces
As
you continue to create class libraries as well as use third-party class libraries created by other developers, you’ll inevitably encounter a situation where two libraries use identical class names, producing unexpected application results.
To illustrate the challenge, suppose you’ve created a website that helps you organize your book collection and allows visitors to comment on any books found in your personal library. To manage this data, you create a library named
Library.inc.php
, and within it a class named
Clean
. This class implements a variety of general data filters that you could apply to not only book-related data but also user comments. Here’s a snippet of the class, including a method named
filterTitle()
that can be used to clean up both book titles and user comments:
function filterTitle($text) {
// Trim white space and capitalize first word
return ucfirst(trim($text));
}
Because this is a G-rated website, you also want to pass all user-supplied data through a profanity filter. An online search turned up a PHP class library called
DataCleaner.inc.php
, which unbeknown to you includes a class named
Clean. This class includes a function named
RemoveProfanity()
, which is responsible for substituting bad words with acceptable alternatives. The class looks like this:
function removeProfanity($text) {
$badwords = array("idiotic" => "shortsighted",
"moronic" => "unreasonable",
"insane" => "illogical");
// Replace bad words
return strtr($text, $badwords);
}
Eager to begin using the profanity filter, you include the
DataCleaner.inc.php file
at the top of the relevant script, followed by a reference to the
Library.inc.php library
:
require "DataCleaner.inc.php";
require "Library.inc.php";
You then make some modifications to take advantage of the profanity filter, but upon loading the application into the browser, you’re greeted with the following fatal error message:
Fatal error: Cannot redeclare class Clean
You’re receiving this error because it’s not possible to use two classes of the same name within the same script. This is similar to a file system where you can’t have two files with the same name in a directory, but they can exist in two different directories.
There’s a simple way to resolve this issue by using namespaces. All you need to do is assign a namespace to each class. To do so, you need to make one modification to each file. Open
Library.inc.php
and place this line at the top:
Likewise, open
DataCleaner.inc.php
and place the following line at the top:
The namespace statement must be the first statement in the file.
You can then begin using the respective
Clean classes
without fear of name clashes. To do so, instantiate each class by prefixing it with the namespace, as demonstrated in the following example:
<?php
require "Library.inc.php";
require "Data.inc.php";
use Library;
use DataCleaner;
// Instantiate the Library's Clean class
$filter = new LibraryClean();
// Instantiate the DataCleaner's Clean class
$profanity = new DataCleanerClean();
// Create a book title
$title = "the idiotic sun also rises";
// Output the title before filtering occurs
printf("Title before filters: %s <br />", $title);
// Remove profanity from the title
$title = $profanity->removeProfanity($title);
printf("Title after LibraryClean: %s <br />", $title);
// Remove white space and capitalize title
$title = $filter->filterTitle($title);
printf("Title after DataCleanerClean: %s <br />", $title);
Executing this script produces the following output:
Title before filters: the idiotic sun also rises
Title after DataCleanerClean: the shortsighted sun also rises
Title after LibraryClean: The Shortsighted Sun Also Rises
Namespaces can be defined as a hierarchy of sub-name spaces. This is done by adding more names separated by the namespace separator (backslash). This is useful if the same package or vendor provides multiple versions of a class, function or constant, or multiple classes with functionality that you want to group together.
As an example, here is a list of the namespaces provided by the Amazon Web Services (AWS) SDK:
namespace AwsS3;
namespace AwsS3Command;
namespace AwsS3Enum;
namespace AwsS3Exception;
namespace AwsS3ExceptionParser;
namespace AwsS3Iterator;
namespace AwsS3Model;
namespace AwsS3ModelMultipartUpload;
namespace AwsS3Sync;
The SDK contains many other namespaces for the various services provided. The names in these examples are not too long and only two or three levels are used. In some cases, you might want to specify a shorter name for your namespace. This will require less typing and make the code more readable. This is done by providing an alias to the namespace. This is best illustrated with a short example.
<php
use AwsS3Command;
$cmd = new AwsS3CommandS3Command();
In this case, the namespace was imported or used as is, and all the classes (and functions and constants) would have to be prefixed with the full namespace name.
<php
use AwsS3Command as Cmd;
$cmd = new CmdS3Command();
In the second example, the namespace is renamed to Cmd, and all references to classes and functions after that will be prefixed with the short name.
A special namespace is the global namespace. This is referenced with a backslash (). All the built-in functions and classes are placed in the global namespace. In order to access any of these from within a given namespace, you would have to specify that the function or class belongs to the global namespace. This is only needed if you are using namespaces.
<?php
namespace MyNamespace;
/* This function is MyNamespacegetFile() */
function getFile($path) {
/* ... */
$content = file_get_contents($path);
return $content;
}
?>
In the example above, the new function getFile()
is defined inside a namespace called MyNamespace. In order to call the global function file_get_contents()
, it will have to be designated as global by prefixing it with .
Summary
This and the previous chapter introduced you to the entire gamut of PHP’s OOP features. PHP supports most of the OOP concepts that exist in other programming languages, and many of the libraries and frameworks available today utilize these concepts. If you’re new to OOP, the material should help you to better understand many of the key OOP concepts and inspire you to perform additional experimentation and research.
The next chapter introduces a powerful solution for efficiently detecting and responding to unexpected operational errors that may crop up during your website’s execution, known
as
exceptions.