14.1 Working with Class Hierarchies

In Chapter 4, we showed you how inheritance can be used as a powerful tool in object-oriented programming. Using the extends keyword, a class can inherit the capabilities from a parent class. As we explained in Chapter 4, PHP allows an inheritance from only one parent class, but a class can be extended to create any number of child classes.

Inheritance allows functionality to be built up from related classes. Each class provides specialized support that's specific to a class's purpose. It's common that functionality in an object-oriented application is provided by class hierarchies rather than single, unrelated classes.

Example 14-1 shows how a class hierarchy is formed to provide functionality that describes shapes.

Example 14-1. Shape classes
<?php



class Shape

{

    var $color;

    var $sides;



    function color( ) 

    { 

        return $this->color; 

    }



    function sides( )

    {

        return $this->sides;

    }



    function _  _construct($color, $sides)

    {

        $this->color = $color;

        $this->sides = $sides;

    }

}



class Circle extends Shape

{



    function _  _construct($color)

    {

        parent::_  _construct($color, 1);

    }

}



class Polygon extends Shape

{

    var $angles;



    function angles( )

    {

        return $this->angles;

    }



    function _  _construct($color, $sides)

    {

         parent::_  _construct($color, $sides);

         $this->angles = $sides;

    }

}



class Triangle extends Polygon

{

    function _  _construct($color)

    {

        parent::_  _construct($color, 3);

    }

}



class Rectangle extends Polygon

{

    function _  _construct($color)

    {

        parent::_  _construct($color, 4);

    }

}



?>

The class Shape defined in the Example 14-1 supports only two features: the color and number of sides of a shape. The Circle and Polygon classes both extend the base Shape class: the Circle constructor function always sets the number of sides to 1,[1] while the Polygon class allows an arbitrary number of sides and defines the member function angles( ) that returns the number of angles the shape makes.

[1] The number of sides of a circle depends on your definition of side?some definitions lead to no sides, some to an infinite number. By our definition, a circle has one side.

The Polygon class is extended by the Triangle and Rectangle classes, and each defines a constructor function that sets the appropriate number of sides. The relationship between the classes defined in Example 14-1 is shown in the class diagram in Figure 14-1.

Figure 14-1. Class hierarchy for the Shape classes
figs/wda2_1401.gif


The classes defined in Example 14-1 aren't particularly useful?we kept the functionality to a minimum to illustrate how a hierarchy is formed. A real application that deals with shapes would define member variables and functions to support the application requirements. For example, if you were interested in the size of a shape, you could add member variables to record the dimensions, and functions that calculate the area.

14.1.1 Polymorphism

While a class hierarchy can help you to develop modular code, most of the power of object-oriented programming comes from the ability to use an object differently in different circumstances, a capability known as polymorphism . Consider the objects that we can create from the classes defined in Example 14-1. The following fragment creates an array of objects using the Circle, Triangle, and Rectangle classes; and then prints information about each shape using a foreach loop.

// Create an array of objects

$shapes = array(

    new Triangle("blue"),

    new Circle("green"),

    new Rectangle("red"));



foreach ($shapes as $s)

    print "I have a {$s->color( )} shape with {$s->sides( )} sides\n";

We can call the color( ) and sides( ) member functions on each of the objects in the previous example because each object, through inheritance, is a Shape. We can't call the angles( ) function on each object because not all of the objects are Polygons and only instances of Polygons have the angles( ) member function. The previous example prints:

I have a blue shape with 3 sides

I have a green shape with 1 sides

I have a red shape with 4 sides

We give further examples that show the benefits of polymorphic behavior later in this chapter.

14.1.2 Discovering Relationships

The instanceof keyword is available in PHP5, and the is_a( ), get_class( ), and get_parent_class( ) functions are available in PHP4 and PHP5.

When you're dealing with an object in a PHP script, it's not always obvious what type of object it is. For example, while all the objects we created in the previous example are Shapes, only the Triangle and Rectangle objects can be used as Polygon objects. PHP5 supports the instanceof operator that allows you to write scripts that can test the capabilities of an object before using it. The following fragment shows how each shape object is tested before an attempt is made to call the Polygon member function:

// Create an array of objects

$shapes = array(

    new Triangle("blue"),

    new Circle("green"),

    new Rectangle("red"));



foreach ($shapes as $s)

{

    if ($s instanceof Polygon)

        print "I have a {$s->color( )} polygon with {$s->sides( )} 

              sides and {$s->angles( )} internal angles\n";

    else

        print "I have a {$s->color( )} shape with {$s->sides( )} sides\n";

}

The previous example prints the longer Polygon message for the Triangle and Rectangle objects, and the shorter Shape message for the Circle object:.

I have a blue polygon with 3 

              sides and 3 internal angles

I have a green shape with 1 sides

I have a red polygon with 4 

              sides and 4 internal angles

14.1.2.1 Functions

The instanceof keyword performs a similar function to the PHP library function is_a( ). Both evaluate to true if the object is an instance of the test class, or an ancestor class. Here's an example that uses is_a( ) to perform the same function as in the previous example:

    if (is_a($s "Polygon"))

        print "I have a {$s->color( )} polygon with {$s->sides( )} 

              sides and {$s->angles( )} internal angles\n";

    else

        print "I have a {$s->color( )} shape with {$s->sides( )} sides\n";

PHP provides several related functions that return information about the class hierarchy of an object:

boolean is_subclass_of(object obj, string classname)
string get_class(object obj)
string get_parent_class(object obj)

The function is_subclass_of( ) returns true if the class of object obj is a descendant of classname. The get_class( ) function returns the name of the class for the object obj, and get_parent_class( ) returns the parent class name. Both get_class( ) and get_parent_class( ) normalize the name of the class to lower case as demonstrated in the following fragment:

// Create a new Triangle object

$shape = new Triangle("orange");



// prints "triangle"

print get_class($shape);



// prints "polygon"

print get_parent_class($shape);