PHP has evolved into a feature-rich, widely deployed web development solution. With each new version released, new features appear, while existing features are improved. PHP's object support is one such feature that has been improved. Object oriented support first appeared in PHP3. PHP4 made additional improvements, such as the way constructors are handled. In fact, the up and coming Zend Engine 2.0 introduces a new object model, more similar to Java. With PHP's object support maturing, many of the reasons developers might not take an object oriented approach are diminishing.
This article is the first in a series of articles that will focus on various object-oriented design topics. In this article we are going to talk about the concepts surrounding abstract classes, situations where they are useful in design, how we can implement abstract classes in PHP, and hopefully some helpful examples to demonstrate the concepts covered.

Prerequisites

In order to get the most out of this article, you need to have a solid understanding of several OO concepts, such as:  what is an object, defining an object, instantiation, inheritance, and object composition. For an in depth look at the before mentioned topics, check out some of the other great articles in the Application Architecture / Object Oriented section on this site. With that being said, let's move on to our discussion of abstract classes and how you can use them in design.

What Is An Abstract Class?

An abstract class is a base class that is not instantiated. It provides common functionality to be inherited by subclasses. Another way to think of an abstract class is like a template. An abstract class contains no code, but instead, defines what code must be defined in subclasses.
Why create a class without any code and one that cannot be instantiated? Well, there are many situations where abstract classes are quite useful. Let's take a look...

Designing With Abstract Classes

One common use for an abstract class is at the top of an object hierarchy. Perhaps you are designing an application in which you would prefer all of your objects to share a certain set of common behavior, such as toString(). Regardless of what each object models within your business domain, you can reliably call upon this expected behavior, as long as implementation has been provided. This is an excellent situation where an abstract class will provide us with a definition for common behavior.
An interface definition is another great example of using abstract classes within your application design. In languages like Java, interfaces are defined with the type interface, and contain public method signatures exclusively. If you try to put implementation into an interface in Java, you will get a compile error. PHP does not have an interface type as such. However, you can still get the same feel, by using an abstract class and keeping out any implementation. With an interface, a certain behavior is defined that any class can implement in any way it sees fit. Furthermore, these interfaces can be composed within classes to extend their functionality even further. Later on, we'll take a look at how these interfaces can be dynamically pluggable at runtime.

Implementing Abstract Classes In PHP

In order to implement an abstract class in PHP, let's review the characteristics of an abstract class.
Currently PHP does not support any of the above mentioned characteristics for classes. So how in the world are we going to implement an abstract class in PHP? Simple. All we need is a little creativity and well documented code.
First, let's take a look at how we can prevent the instantiation of a class. For our example, we'll declare a class named Base, and setup an empty constructor...well, sort of.

<?php
class Base
{
  function 
Base()
  {
    
/* Do not allow instantiation of base class. */
    
if ( strcasecmpget_class($this), "Base") == ) {
      
trigger_error("Instantiation of Base prohibited."E_USER_ERROR);
      return 
NULL;
    }
  }
}
?>
Ok, so we added a few extra lines of code to the constructor. Yes, somewhat cheesy and not near as elegant as adding an abstract modifier to the class declaration (currently not possible in PHP), but it works. When the constructor is called, we grab the name of the class from the current instance, and compare it to the name of class Base. If the two names match, we trigger an error halting the script.
Finished? Not quite. Although an attempt to instantiate our base class would trigger an error, nothing prevents our constructor from being called statically, Base::Base(). Any class, regardless of inheritance, could call our constructor through static method syntax. In fact, any code practically anywhere could still call our constructor statically. Since PHP currently doesn't make a distinction between instance vs. static methods (except for $this at runtime), we just code around it.

<?php
class Base
{
  function 
Base()
  {
    
/* Do not allow instantiation of base class. */
    
if ( strcasecmpget_class($this), "Base") == ) {
      
trigger_error("Instantiation of Base prohibited."E_USER_ERROR);
      return 
NULL;
    }
    
    
/* Prevent non subclass from calling constructor. */
    
if ( !is_subclass_of($this"Base") ) {
      
trigger_error("Base constructor call not allowed."E_USER_ERROR);
      return 
NULL;
    }
  }
}
?>
We now have an abstract base class that cannot be instantiated or have the constructor called directly, unless from a subclass. Attempting either of the following calls would result in a script error.

<?php
$base 
= new Base();
Base::Base();
?>
To finalize this example, let's clean up our code a little. The second if() statement can actually serve two purposes. It solves our need to prevent the constructor from being called statically. It indirectly solves the instantiation of the class as well. To see for your self, comment out the first if() statement, and once more attempt to instantiate the class or call the constructor statically. You should still get a script error. All that's left is to modify our error message a bit. The final code provides a simple way to prevent class instantiation and simulate an abstract class.

<?php
class Base
{
  function 
Base()
  {
    
/*
     * Do not allow instantiation of base class.
     * Prevent non subclass from calling constructor.
     */
    
if ( !is_subclass_of($this"Base") ) {
      
trigger_error("Base instantiation from non subclass."E_USER_ERROR);
      return 
NULL;
    }
  }
}
?>
For the remaining characteristics of an abstract class, we will provide well-written comments in our code. Most of the remaining characteristics (i.e. inherited classes must implement inherited abstract methods) are forced at compile time by languages such as Java. You can use the method_exists() function to ensure a subclass has the methods of the base. Of course, this will always return true, since the methods do exist through inheritance. A distinction between abstract and non-abstract methods doesn't exist in PHP. There is no sensible way to check for implementation (if anyone knows otherwise, please share your knowledge). Therefore you must provide appropriate comments informing the implementer what is expected.

Example: Object Hierarchy

We want to provide an abstract class that will live at the top of our object hierarchy. This class will define core behavior that all classes in our hierarchy should implement. We will call this top-level class Object. I realize the creativity is somewhat lacking. We have identified 2 core behaviors that we would like to define in our abstract class: (1) returning a string representation of an object, and (2) comparing an instance of one object to that of another object. Here is a look at our abstract class.

<?php
class Object
{
  function 
Object()
  {
    
// Enforce abstract behavior.
    
if ( !is_subclass_of($this"Object") ) {
      
trigger_error("Object instantiation from non subclass."E_USER_ERROR);
      return 
NULL;
    }
  }
  
  function 
toString() {}
  
  function 
equals($object)
  {
    return (
$this === $object);
  }
}
?>
Here we have defined an abstract class Object on which we can build our object hierarchy. Let's take a look at this example in action. Imagine we are designing a human resources application. One obvious class in our model would be an Employee class. For the OO purists, you could model a Person and let each person wear an Employee hat through composition, but we'll save that for another discussion. Let's take a look at our Employee class.

<?php
class Employee extends Object
{
  
// Member variables.
  
var $_id;
  var 
$_ssn;
  var 
$_firstName;
  var 
$_lastName;
  
  function 
Employee($id$ssn$firstName$lastName)
  {
    
// Assign member variables.
    
$this->_id        $id;
    
$this->_ssn       $ssn;
    
$this->_firstName $firstName;
    
$this->_lastName  $lastName;
  }
  
  function 
toString()
  {
    
$info "ID: ".$this->_id."\n";
    
$info .= "SSN: ".$this->_ssn."\n";
    
$info .= "FirstName: ".$this->_firstName."\n";
    
$info .= "LastName: ".$this->_lastName."\n";
    return 
$info;
  }
}
?>
Notice our Employee class has provided implementation for the toString() method. No implementation for the equals() method was provided, since the Object class provides a sufficient one. However, if the implementation did not suffice, we could have provided our own version of equality checking. As we further design our object hierarchy based on Object, we know that all our classes will have 2 common behaviors: equals() and toString().

Example: Interfaces

For our second example, we will see how an abstract class can be used as an interface. We will start out by designing a few objects that will be a part of a fictitious web application for home-automation (one of my favorite hobbies). We want to build a web interface to control our various home devices (i.e. lights, toaster, stereo). However, not all of our devices speak the same language, or protocol. While the X-10 protocol still dominates the market, we find several other home-automation protocols creeping in. The four major contenders among home-automation protocols are:
The implementation for each of these protocols varies significantly. Yet, amongst these protocols the same common control functionality exists: (1) turning on a device, (2) turning off a device, and (3) getting the status of a device. Our interface will provide a template for this common functionality. We'll name our interface Control. As you can see, the methods do not contain any code.

<?php
class Control
{
  function 
on() {}
  function 
off() {}
  function 
getStatus() {}
}
?>
Earlier I mentioned providing well-written comments in our code. This helps the implementer have a clear understanding of what behavior is expected. Therefore, we'll update our example with appropriate comments.

<?php
/*
 * Class:   Control
 *  Type:   Interface
 *
 * Purpose: A template for common control functionality
 *          of home automated devices.
 *
 * Methods: on()
 *          off()
 *          getStatus()
 *
 * Error Codes
 * -----------
 * 0 - No errors occurred during transmission.
 * 1 - An error occurred during transmission.
 *
 *
 * API Developers
 * --------------
 * Be sure to provide appropriate functionality for each
 * method. The application will dynamically instantiate
 * the appropriate protocol for a selected device, and will
 * expect consistent control functionality.
 */
class Control
{
    function 
Control()
  {
    
/*
     * Do not allow instantiation of Control class.
     * Prevent non subclass from calling constructor.
     */
    
if ( !is_subclass_of($this"Control") ) {
      
trigger_error("Control instantiation from non subclass."E_USER_ERROR);
      return 
NULL;
    }
  }
  
  function 
on() {}         // Turn on a device.
  
function off() {}        // Turn off a device.
  
function getStatus() {}  // Get the status of a device.
}
?>
Now that we have our interface defined, let's implement a protocol. We will work with the X-10 protocol, since it is the most popular one. We will inherit from Control, and provide the appropriate implementation in X10_Control.

<?php
class X10_Control extends Control
{
  function 
on()
  {
    
// ...implementation code.
  
}
  function 
off()
  {
    
// ...implementation code.
  
}
  function 
getStatus()
  {
    
// ...implementation code.
  
}
  function 
dim()
  {
    
// ...implementation code.
  
}
  function 
bright()
  {
    
// ...implementation code.
  
}
}
?>
Notice the addition of methods dim() and bright(). This functionality is available in the X-10 protocol, but may not be common in other protocols. Therefore, we add it here exclusively. Now that our X-10 protocol class is complete, we want to see how this protocol and others will apply to a home device. We'll start out by creating a Device class. Let's take a look.

<?php
class Device
{
  
// Member variables.
  
var $_type;
  var 
$_protocol;
  
  function 
Device($deviceID)
  {
    
// Grab device settings from storage.
    
$deviceRS DataProvider::getDevice($deviceID);
    
    
// Assign settings to members.
    
$this->_type     $deviceRS->type;      // LIGHT, APPLIANCE, AUDIO, etc.
    
$this->_protocol $deviceRS->protocol;  // "X10"
    
    // Compose pluggable control protocol through dynamic aggregation.
    
aggregate$this$this->_protocol."_Control" );
  }
  
  function 
setProtocol($protocol)
  {
    
// Clear any existing aggregation.
    
deaggregate$this$this->_protocol."_Control" );
    
    
// Update protocol.
    
$this->_protocol $protocol;
    
    
// Assign new aggregation.
    
aggregate$this$this->_protocol."_Control" );
  }
}
?>
We have declared two member variables to hold the type of device and protocol used (i.e. X10, CEBus). In the constructor, we grab the device settings from storage, assign these settings to our instance, and then apply the appropriate control functionality, based on the protocol specified.
On a side note, PHP supports two types of composition:  association and aggregation. The protocol functionality is composed through aggregation. Now we can swap in and out different protocols by simply assigning a new protocol type; even at runtime. Today your garage light is X-10 compatible; tomorrow Smart House.
We'll conclude our example by showing how this sort of design can be implemented into our web application. Imagine we have a screen that lists the various automated devices throughout our house. Each device listing has a checkbox associated with it. This allows the user to select which device(s) to control. Upon selecting the device(s), the user can select on one of three commands: ON, OFF, STATUS.
In the processing script, the command is processed for all devices selected. The processing script is unaware which protocol each devices has implemented. It just knows that the devices have implemented the Control interface, so it can reliably call on(), off(), or getStatus(). The following code shows the processing script that is called once the user makes the selection(s) and sends a control command.

<?php
// Loop through each selected device, processing command.
foreach($_POST["devices"] as $deviceID => $selected) {
  
$device Device::getDevice($deviceID);

  switch ( 
$_POST["command"] ) {
    case 
"on":
      
$device->on();
      break;
    case 
"off":
      
$device->off();
      break;
    case 
"getStatus":
      
$device->getStatus();
  }
}
?>

Conclusion

Well that concludes our discussion of abstract classes. By now you should have a good understanding of what exactly abstract classes are, as well as the knowledge to put them to use. Be sure to study the design of various PHP applications that are available on the 'net. You'd be surprised how many developers are already putting them to use in PHP today.
The code presented in this article is available for download. Take a look at each example to see how these concepts come together. If this article helped, let me know. Until next time... [editor's note: there was a little confusion regarding the sample code. I've asked Jonathan to resend it and will post it here ASAP. 20030207 - JS]