Article
The PHP Anthology Volume 1, Chapter 2 - Object Oriented PHP
Inheritance in Action
Now that you have a rough idea of how inheritance is used in PHP, it’s time to look at an example that should give you a better idea of how inheritance can be applied.
The following example implements a simple navigation system for a Web page, generating the HTML that appears at the top of the page. By having one class inherit from another, it becomes possible to add “crumb trail” navigation to the page when it’s needed.
First up, the StandardHeader class deals with generating the HTML for the top of the page, as well as supplying the setHeader and getHeader methods to access the variable where the HTML is stored.
Example 2.20. 12.php (excerpt)
<?php
/**
* A standard header for a Web page
*/
class StandardHeader {
/**
* The header HTML is stored here
*/
var $header = '';
/**
* The constructor, taking the name of the page
*/
function StandardHeader($title)
{
$html = <<<EOD
<html>
<head>
<title> $title </title>
</head>
<body>
<h1>$title</h1>
EOD;
$this->setHeader($html);
}
/**
* General method for adding to the header
*/
function setHeader($string)
{
if (!empty($this->header)) {
$this->header .= $string;
} else {
$this->header = $string;
}
}
/**
* Fetch the header
*/
function getHeader()
{
return $this->header;
}
}
Now, the subclass CategoryHeader brings extra functionality to its parent, adding the “bread crumb” links to the HTML that was generated. We don’t need to recreate the setHeader and getHeader methods, as these are inherited from StandardHeader when CategoryHeader is instantiated.
Example 2.21. 12.php (excerpt)
/**
* Subclass for dealing with Categories, building a breadcrumb
* menu
*/
class CategoryHeader extends StandardHeader {
/**
* Constructor, taking the category name and the pages base URL
*/
function CategoryHeader($category, $baseUrl)
{
// Call the parent constructor
parent::StandardHeader($category);
// Build the breadcrumbs
$html = <<<EOD
<p><a href="$baseUrl">Home</a> >
<a href="$baseUrl?category=$category">$category</a></p>
EOD;
// Call the parent setHeader() method
$this->setHeader($html);
}
}
Let’s now put these two classes to use:
Example 2.22. 12.php (excerpt)
// Set the base URL
$baseUrl = '12.php';
// An array of valid categories
$categories = array('PHP', 'MySQL', 'CSS');
// Check to see if we're viewing a valid category
if (isset($_GET['category']) &&
in_array($_GET['category'], $categories)) {
// Instantiate the subclass
$header = new CategoryHeader($_GET['category'], $baseUrl);
} else {
// Otherwise it's the home page. Instantiate the Parent class
$header = new StandardHeader('Home');
}
// Display the header
echo $header->getHeader();
?>
<h2>Categories</h2>
<p><a href="<?php echo $baseUrl; ?>?category=PHP">PHP</a></p>
<p><a href="<?php echo $baseUrl; ?>?category=MySQL">MySQL</a></p>
<p><a href="<?php echo $baseUrl; ?>?category=CSS">CSS</a></p>
</body>
</html>
As you can see, the controlling logic above looks for a $_GET['category'] variable. If it exists, it creates an instance of CategoryHeader, displaying the navigation to allow users to find their way back to the home page. But if it doesn’t exist, it creates an instance of the parent StandardHeader instead, which applies when users view the home page (and therefore does not require bread crumbs to find their way back).
In other words, inheritance allows us to add the extra functionality we need without having to reproduce the logic that already resides within the parent class; the existing methods and logic can be reused via the child subclass.
Inheritance provides a powerful mechanism to make classes that are modular, addressing a specific problem, while still making available shared methods and variables that can be used irrespective of the specific object we’re dealing with.
Avoid Deep Inheritance Structures
As a general rule of thumb, when using inheritance to build class hierarchies, avoid going deeper than two generations.
Doing so is often a sign of a bad design, in which opportunities for classes to interact in different ways (see the next solution) were missed. In practice, having more than two generations of classes often leads to all sorts of debugging problems and makes the code difficult to maintain. For example, it can become hard to keep track of variable names you’ve used higher up in the hierarchy.