4.2 Inheritance
Inheritance
is available in PHP4 and PHP5.One of the powerful concepts in object-oriented programming is
inheritance. Inheritance allows a new class to
be defined by extending the capabilities of an existing
base class
or parent class.
PHP allows a new class to be created by extending an existing class
with the extends keyword.Example 4-7 shows how the
UnitCounter class from Example 4-4 is extended to create the new class
CaseCounter. The aim of the extended class is to
track the number of cases or boxes that are needed to hold the units
accumulated by the counter. For example, if bottles of wines are the
units, then a case might hold 12 bottles.
Example 4-7. Defining the CaseCounter class by extending UnitCounter
<?phpBefore we discuss the implementation of the
// Access to the UnitCounter class definition
require "example.4-1.php";
class CaseCounter extends UnitCounter
{
var $unitsPerCase;
function addCase( )
{
$this->add($this->unitsPerCase);
}
function caseCount( )
{
return ceil($this->units/$this->unitsPerCase);
}
function CaseCounter($caseCapacity)
{
$this->unitsPerCase = $caseCapacity;
}
}
?>
CaseCounter, we should examine the relationship
with the UnitCounter class. Figure 4-1 illustrates this relationship in a simple
class diagram
. There are several different notations
for representing class diagrams; we show the inheritance relationship
by joining two classes with an annotated line with a solid arrowhead.
Figure 4-1. Class diagram showing UnitCounter and CaseCounter

related to counting cases worth of unitsfor example, bottles
of winewhile the UnitCounter base class
provides the counting and total weight capabilities. To create a
CaseCounter object, the number of units that are
stored in a case needs to be specified. This value is passed to the
constructor when new CaseCounter object is
created,
// Create a CaseCounter that holds 12 bottles in a casethe value is then recorded in the member variable
$order = new CaseCounter(12);
$unitsPerCase.The addCase( ) member function uses the
$unitsPerCase member variable to add a case of
units to the counter:
function addCase( )The units are added by calling the base
{
// The add( ) function is defined in the
// base class UnitCounter
$this->add($this->unitsPerCase);
}
UnitCounter member function add(
). Unless they are declared as private, member variables
and functions defined in the base class can be called in derived
classes using the -> operator and the special
placeholder variable $this.The caseCount( ) member function calculates the
number of cases needed to contain the total number of units. For
example, if there are 50 bottles of wine, and a case can hold 12
bottles, then 5 cases are needed to hold the wine. The number of
cases is therefore calculated by dividing the total number of
unitsstored in the member variable $unit
defined in the UnitCounter classby the
member variable $unitsPerCase. The result of the
division is rounded up to the next whole case with the
ceil( ) function. The ceil(
) function is described in Chapter 3.When a new CaseCounter object is created and
used, all of the publicly accessible member variables and functions
of the base class are also available. This means that you can use a
CaseCounter object as if it were a
UnitCounter but it also has the extra features
of the CaseCounter class. Consider an example:
// Create a CaseCounter that holds 12 bottles in a caseUnlike some other object-oriented languages, PHP only allows a single
$order = new CaseCounter(12);
// Add seven bottles using the UnitCounter defined function
$order->add(7);
// Add a case using the CaseCounter defined function
$order->addCase( );
// Print the total number of Units : 19
print $order->units;
// Print the number of cases: 2
print $order->caseCount( );
base class to
be specified when defining new classes. Allowing inheritance from
multiple base classes can lead to unnecessarily complex code and, in
practice, isn't very useful. In Chapter 14, we explore advanced techniques that
eliminate the need for multiple inheritance.
4.2.1 Calling Parent Constructors
The ability to call parent
constructors is available in PHP5.CaseCounter objects use three member variables:
two are defined in the UnitCounter class, and
the third is defined in CaseCounter. When a
CaseCounter object is created, PHP calls the
_ _construct( ) function defined in
CaseCounter and sets the value of the member
variable $unitsPerCase with the value passed as a
parameter. In the following fragment, the value passed to the
_ _construct( ) function is 12:
// Create a CaseCounter that holds 12 bottles in a casePHP only calls the _ _construct( ) function
$order = new CaseCounter(12);
defined in CaseCounter; the constructor of the
parent class UnitCounter is not automatically
called. Therefore, objects created from the
CaseCounter class defined in Example 4-7 always have the weight defined as 1 kg, the
value that's set in the member variable of the
parent class. The CaseCounter class shown in
Example 4-8 solves this problem by defining a
_ _construct( ) function that calls the
UnitCounter _ _construct( )
function using the parent:: reference.
Example 4-8. Calling parent constructor function
<?phpAs Example 4-8 is written to use features provided
// Access to the UnitCounter class definition
include "example.4-4.php";
class CaseCounter extends UnitCounter
{
private $unitsPerCase;
function addCase( )
{
$this->add($this->unitsPerCase);
}
function caseCount( )
{
return ceil($this->numberOfUnits( )/$this->unitsPerCase);
}
function _ _construct($caseCapacity, $unitWeight)
{
parent::_ _construct($unitWeight);
$this->unitsPerCase = $caseCapacity;
}
}
?>
by PHP5, we extend the more sophisticated
UnitCounter class defined in Example 4-4. Also, the member variable
$unitsPerCase is now defined to be private and we
use the PHP5 _ _construct( ) function. The
constructor function of the improved CaseCounter
shown in Example 4-8 takes a second parameter,
$unitWeight which is passed to the _
_construct( ) function defined in the
UnitCounter class.
4.2.2 Redefined Functions
Both PHP4 and PHP5 allow functions
to be redefined, and the parent:: and class
reference operators are available in PHP5.Functions defined in a base class can be redefined in a
descendant
class. When objects of the descendant
class are created, the redefined functions take precedence over those
defined in the base class. We have already seen the _
_construct( ) function of the base
UnitCounter class redefined in the
CaseCounter class in Example 4-8.Consider the Shape and
Polygon classes defined in the following code
fragment:
class ShapeThe class Shape is the base class to
{
function info( )
{
return "Shape.";
}
}
class Polygon extends Shape
{
function info( )
{
return "Polygon.";
}
}
Polygon, making Polygon a
descendant of Shape. Both classes define the
function info( ). So, following the rule of
redefined functions, when an object of class
Polygon is created, the info(
) function defined in the Polygon
class takes precedence. This is shown in the following example:
$a = new Shape;With PHP 5, we can use the parent:: reference to
$b = new Polygon;
// prints "Shape."
print $a->info( );
// prints "Polygon."
print $b->info( );
access the info( ) function
from the parent class. For example, we can modify the
Polygon class definition of info(
) as follows:
class Polygon extends ShapeThis approach can be used in descendant classes, proving a way of
{
function info( )
{
return parent::info( ) . "Polygon.";
}
}
$b = new Polygon;
// prints "Shape.Polygon."
print $b->info( );
accumulating the result of ancestor functionality. Consider a
Triangle class that extends the
Polygon class:
class Triangle extends PolygonThe parent::
{
function info( )
{
return parent::info( ) . "Triangle.";
}
}
$t = new Triangle;
// prints "Shape.Polygon.Triangle."
print $t->info( );
reference operator only
allows access to the immediate parent class. PHP allows access to any
known ancestor class using a class reference
operatorwe introduced the class reference earlier
in our discussion of static member variables and functions in Section 4.1. We can rewrite the
Triangle class to call the ancestor version of
the info( ) functions directly:
class Triangle extends PolygonUsing the class access operators makes code less portable. For
{
function info( )
{
return Shape::info( ) . Polygon::info( ) . "Triangle.";
}
}
$t = new Triangle;
// prints "Shape.Polygon.Triangle."
print $t->info( );
example, you would need to modify the implementation of the
Triangle class if you decided that
Triangle would extend Shape
directly. Using the parent:: reference operator
allows you to re-arrange class hierarchies more easily.
4.2.3 Protected Member Variables and Functions
Protected members are available in
PHP5.Member variables and functions can be defined using the
protected keyword. This offers a compromise
between being public and private: it allows access to member
variables and functions defined in a class from within descendant
classes, but it prevents access to the member variables and functions
from code outside of the class hierarchy. So, for example, a child
class can access a parent class's protected
functions, but the parent class protected functions
can't be accessed from an unrelated class or from
within a script that uses the class.In Example 4-5, we introduced the
FreightCalculator class to work out freight
costs based on the number of cases and the total weight of a
shipment. The FreightCalculator class defined in
Example 4-5 calculates the per case and per kilogram
costs using the two private functions perCaseTotal(
) and perKgTotal( ).In Example 4-9, we rewrite the
FreightCalculator class to define these
functions as protected. This allows a new class
AirFreightCalculator to extend
FreightCalculator and redefine the functions to
apply different rates per kilogram and case count.
Example 4-9. An air freight calculator
class FreightCalculatorBecause the AirFreightCalculator implementation
{
protected $numberOfCases;
protected $totalWeight;
function totalFreight( )
{
return $this->perCaseTotal( ) + $this->perKgTotal( );
}
protected function perCaseTotal( )
{
return $this->numberOfCases * 1.00;
}
protected function perKgTotal( )
{
return $this->totalWeight * 0.10;
}
function _ _construct($numberOfCases, $totalWeight)
{
$this->numberOfCases = $numberOfCases;
$this->totalWeight = $totalWeight;
}
}
class AirFreightCalculator extends FreightCalculator
{
protected function perCaseTotal( )
{
// $15 + $1 per case
return 15 + $this->numberOfCases * 1.00;
}
protected function perKgTotal( )
{
// $0.40 per kilogram
return $this->totalWeight * 0.40;
}
}
of perCaseTotal( ) and perKgTotal(
) requires access to the
FreightCalculator member variables
$totalWeight and
$numberOfCases, these have also been declared as
protected.
4.2.4 Final Functions
Declaring final functions is
available in PHP5.The AirFreightCalculator class defined in Example 4-9 doesn't redefine the
totalFreight( ) member function because the
definition in FreightCalculator correctly
calculates the total. Descendant classes can be prevented from
redefining member functions in base classes by declaring them as
final. Declaring the totalFreight(
) member function with the final
keyword prevents accidental redefinition in a descendant class:
final function totalFreight( )
{
return $this->perCaseTotal( ) + $this->perKgTotal( );
}