Object-Oriented PHP: Working with Inheritance

Learn how inheritance works in PHP. This tutorial explores the concept of parent and child classes; overriding methods; final classes; abstract classes, and interfaces. Example code included.

Object-Oriented PHP: Working with Inheritance

Welcome to my third object-oriented PHP tutorial! If you haven't already checked out the first two then you may want to take a look at those first, since this tutorial builds on the previous two:

In this article we're going to explore the idea of inheritance in object-oriented programming, and how inheritance works in PHP. With inheritance, your objects and classes can become much more powerful and flexible, and you can save a lot of time and effort with your coding.

We'll look at the following topics in this article:

  • The concept of inheritance, and why it's so useful
  • How one PHP class can inherit from another
  • How a child class can override some of the functionality of its parent
  • Working with final classes and methods
  • Using abstract classes, and
  • Working with interfaces.

Ready? Let's get started!

How inheritance works

Inheritance is based around the concept of parent classes and child classes. By using a special syntax, you can create a class that is a child of another class (which becomes, naturally enough, its parent class).

Parent classes are also known as base classes or superclasses. Similarly, child classes are sometimes called derived classes or subclasses.

When you create a child class, it inherits all the properties and methods of the parent. The child class can then include additional properties and methods, thereby extending the functionality of the parent class.

For example, say a web forum application has a Member class for forum members, containing methods such as createPost(), editProfile(), showProfile(), and so on. Since forum administrators are also members, you can also create a class called Administrator that is a child of the Member class. The Administrator class then inherits all of the properties and methods of the Member class, so an Administrator object behaves just like a Member object.

Then, you can add administrator-specific functionality to the Administrator class by adding extra methods such as createForum(), deleteForm() and banMember(). Similarly, if you want different privilege levels for administrators then you can add an $adminLevel property to the Administrator class.

In this way, you don't clutter up your Member class with administrator-specific methods that aren't appropriate for regular member. You also avoid having to copy and paste the Member class's properties and methods into the Administrator class. So inheritance gives you the best of both worlds.

You can create as many child classes as you like from a single parent, and each child class can add its own properties and methods.

Creating child classes in PHP

Ducklings

So how do you create a class that is a child of another class in PHP? You use the extends keyword, as follows:


class ParentClass {
  // properties and methods here
}

class ChildClass extends ParentClass {
  // additional properties and methods here
}

Here we've created a class, ParentClass, then created another class, ChildClass, that inherits from ParentClass. ChildClass inherits all the properties and methods of ParentClass, and it can also add its own properties and methods.

Let's try an example. We'll create a Member class for an imaginary web forum, then create an Administrator class that is a child of the Member class:

class Member {

  public $username = "";
  private $loggedIn = false;

  public function login() {
    $this->loggedIn = true;
  }

  public function logout() {
    $this->loggedIn = false;
  }

  public function isLoggedIn() {
    return $this->loggedIn;
  }
}

class Administrator extends Member {

  public function createForum( $forumName ) {
    echo "$this->username created a new forum: $forumName<br>";
  }

  public function banMember( $member ) {
    echo "$this->username banned the member: $member->username<br>";
  }

}

As you can see, our Member class contains a public $username property, a private $loggedIn property, methods to log the member in and out, and a method to determine whether the member is logged in or not.

We then add an Administrator class as a child of the Member class. Administrator inherits all the properties and methods of the Member class. We also add a couple of extra admin-specific methods to the Administrator class:

createForum( $forumName )
Creates a new forum called $forumName
banMember( $member )
Bans the member $member from the forum

Of course, these methods don't really do anything since this is just an imaginary forum. Instead, each method simply displays a message using echo.

Now let's try out our Member and Administrator classes. We'll create a new member and administrator, then call some of their methods to do stuff:

// Create a new member and log them in
$member = new Member();
$member->username = "Fred";
$member->login();
echo $member->username . " is " . ( $member->isLoggedIn() ? "logged in" : "logged out" ) . "<br>";

// Create a new administrator and log them in
$admin = new Administrator();
$admin->username = "Mary";
$admin->login();
echo $admin->username . " is " . ( $member->isLoggedIn() ? "logged in" : "logged out" ) . "<br>";

// Displays "Mary created a new forum: Teddy Bears"
$admin->createForum( "Teddy Bears" );

// Displays "Mary banned the member: Fred"
$admin->banMember( $member );

This code outputs the following:


Fred is logged in
Mary is logged in
Mary created a new forum: Teddy Bears
Mary banned the member: Fred

Here's how the code works:

  1. First we create a new Member object, give it a username of "Fred", log the member in, and display his logged-in status.
  2. Then we create a new Administrator object. Since Administrator inherits from Member, we can use all the same properties and methods that we used for the Member object. We give the admin a username of "Mary", log her in, and display her logged-in status.
  3. Now we call the admin's createForum() method, passing in the name of the forum to create ("Teddy Bears").
  4. Finally we call the admin's banMember() method, passing in the member to ban (Fred).

That's really the essence of inheritance in PHP. In the rest of this tutorial you'll explore various ways that you can tweak inheritance for specific purposes, including overriding, final classes and methods, abstract classes, and interfaces.

Overriding parent class methods

As you've seen, when you create a child class, that class inherits all of the properties and methods of its parent. However, sometimes you might want an inherited method in a child class to behave differently to its parent's method.

Using our earlier forum example: When an administrator logs into the forum, you log them in just like a regular member, but you might also want to record the login event in a log file for security purposes.

By overriding the login() method in the Administrator class, you can redefine the method so that it also records the login event.

To override a parent class's method in a child class, you simply create a method in the child class with the same name as the parent class's method. Then, whenever the method is called for objects of the child class, PHP runs the child class's method instead of the parent class's method:


class ParentClass {
  public function myMethod() {
    // (method code here)
  }
}

class ChildClass extends ParentClass {
  public function myMethod() {
    // For ChildClass objects, this method is called
    // instead of the parent class's MyMethod()
  }
}

Let's override the login() function in our Administrator class so that it also records admin login events:

class Member {

  public $username = "";
  private $loggedIn = false;

  public function login() {
    $this->loggedIn = true;
  }
  
  public function logout() {
    $this->loggedIn = false;
  }
}

class Administrator extends Member {

  public function login() {
    $this->loggedIn = true;
    echo "Log entry: $this->username logged in<br>";
  }

}

// Create a new member and log them in
$member = new Member();
$member->username = "Fred";
$member->login();
$member->logout();

// Create a new administrator and log them in
$admin = new Administrator();
$admin->username = "Mary";
$admin->login();           // Displays "Log entry: Mary logged in"
$admin->logout();

As you can see, we've redefined login() inside the Administrator class to display a mocked-up log entry, indicating that the admin has logged in.

We then create a regular Member object ("Fred"), and an Administrator object ("Mary"). When we call Fred's login() method, PHP calls Member::login() as usual. However, when we call Mary's login() method, PHP notices that we've overridden login() in the Administrator class, so it calls Administrator::login() instead. This displays "Log entry: Mary logged in" in the page.

On the other hand, since we haven't overridden the logout() method in the Administrator class, Member:logout() is called for both the Member object and the Administrator object.

Calling a parent method from a child method

When you override a parent class's method in a child class, you don't always want to redefine the method entirely. Often, you still want to use the functionality of the parent method, and merely add additional functionality in the child method.

For instance, in the code example in the previous section, we overrode the Member class's login() method in the Administrator class to display a log entry. However, we also duplicated the functionality of Member::login() inside Administrator::login() when we set $this->loggedIn to true:

class Administrator extends Member {

  public function login() {
    $this->loggedIn = true;
    echo "Log entry: $this->username logged in<br>";
  }
}

Rather than duplicating code like this, it would be better if we could simply call Member::login() from inside Administrator::login().

To access a parent class's method from within a child class's method, you use the parent keyword, like this:


parent::myMethod();

So now we can rewrite our Administrator class's login() method so that it calls the Member class's login() method before it adds its own functionality:

class Administrator extends Member {

  public function login() {
    parent::login();
    echo "Log entry: $this->username logged in<br>";
  }
}

Not only is this neater, but it's more future-proof. If, at a later date, you want to change the way members are logged in, you only have to change the code in Member::login(), and Administrator::login() will automatically run the new code.

Preventing inheritance with final methods and classes

Stop sign

Most of the time, allowing your classes to be extended using inheritance is a good thing. It's part of what makes object-oriented programming so powerful.

Occasionally, though, overriding certain methods of a class can cause things to break easily, create security issues, or make the resulting code overly complex. In these situations, you might want to prevent methods within the class — or even the entire class — from being extended.

To prevent a parent class's method from being overridden by any of its child classes, you add the final keyword before the method definition. For example, you might decide to prevent your Member class's login() method from being overridden for security reasons:

class Member {

  public $username = "";
  private $loggedIn = false;

  public final function login() {
    $this->loggedIn = true;
  }

  public function logout() {
    $this->loggedIn = false;
  }

  public function isLoggedIn() {
    return $this->loggedIn;
  }
}

If some other code tries to extend this class and override the login() method:

class NaughtyMember extends Member {

  public function login() {
    $this->loggedIn = true;
    // Do something bad
  }
}

...then PHP generates the following error:


Fatal error: Cannot override final method Member::login()

You can also prevent an entire class from being extended by adding final to the class definition:

final class Member {
  // This class can't be extended at all
}

Any attempts to create a child class from the Member class will now result in this error:


Fatal error: Class NaughtyMember may not inherit from final class (Member)

Although it covers Java rather than PHP, this is a great article that discusses the benefits of final classes and methods.

Working with abstract classes

An abstract class is a special type of class that can't be instantiated — in other words, you can't create objects from it. Instead, you create child classes from the abstract class, and create objects from those child classes instead. An abstract class is designed to be used as a template for creating classes.

An abstract class contains one or more abstract methods. When you add an abstract method to an abstract class, you don't include any code inside the method. Instead, you leave the implementation of the method to any child classes that inherit from the abstract class.

The moment you add one or more abstract methods to a class, you must declare that class to be abstract.

When a child class extends an abstract class, the child class must implement all of the abstract methods in the abstract class. (If it doesn't then PHP generates an error.) In this way, the abstract class lays down the rules as to how its children should behave. Any code that uses a child class of the abstract class knows that the child class will implement a given set of methods.

You can also add regular, non-abstract methods to your abstract class. These methods are then inherited by child classes as normal.

Let's look at an example. Say we're building a website that has both members of a forum on the site, and shoppers in an online store section of the site. Since members and shoppers are both people, we can create an abstract class, Person, that contains some properties and methods common to all people using our website:

abstract class Person {

  private $firstName = ""; 
  private $lastName = "";

  public function setName( $firstName, $lastName ) {
    $this->firstName = $firstName;
    $this->lastName = $lastName;
  }

  public function getName() {
   return "$this->firstName $this->lastName";
  }
  
  abstract public function showWelcomeMessage();
}

As you can see, we've declared our Person class to be abstract by adding the abstract keyword before the class definition in line 1. The class contains a couple of properties common to all people — $firstName and $lastName — as well as methods to set and retrieve these properties.

The class also contains an abstract method called showWelcomeMessage() at line 15. This method should display a welcome message to the person when they join the site. Again, we add the abstract keyword at the start of the method declaration to declare it as abstract. Since the method is abstract, it doesn't contain any implementation code; it's merely a declaration. However, any child classes created from this abstract class must include — and implement — showWelcomeMessage().

Now let's create a couple of classes based on the Person abstract class:

  1. A Member class for forum members
  2. A Shopper class for shoppers at the online store
class Member extends Person {

  public function showWelcomeMessage() {
    echo "Hi " . $this->getName() . ", welcome to the forums!<br>";
  }

  public function newTopic( $subject ) {
    echo "Creating new topic: $subject<br>";
  }
}

class Shopper extends Person {

  public function showWelcomeMessage() {
    echo "Hi " . $this->getName() . ", welcome to our online store!<br>";
  }

  public function addToCart( $item ) {
    echo "Adding $item to cart<br>";
  }
}

As you can see, each class implements the abstract showWelcomeMessage() method from the Person class. They implement the method differently — Member displays a "welcome to the forums" message, while Shopper displays "welcome to our online store" — but that's OK. The point is that they've fulfilled the obligation laid down by the abstract class to implement the showWelcomeMessage() method.

If one class — say, Shopper — fails to implement showWelcomeMessage() then PHP raises an error:


Class Shopper contains 1 abstract method and must therefore be declared abstract
or implement the remaining methods (Person::showWelcomeMessage)

As well as implementing the abstract method, each class also contains a class-specific method. Member contains a newTopic() method to create new forum topics, while Shopper contains addToCart() for adding items to a shopping cart.

We can now create Member and Shopper objects in our website. As well as calling newTopic() and addToCart() on these objects, we can also call getName() and setName(), since these methods are inherited from the abstract Person class.

More importantly, since we know that our Member and Shopper classes are derived from Person, we can happily call the showWelcomeMessage() method on Member and Shopper objects, safe in the knowledge that both classes implement this method. We know that Member and Shopper must implement it because it was declared abstract inside Person.

Here's an example:

$aMember = new Member();
$aMember->setName( "John", "Smith" );
$aMember->showWelcomeMessage();
$aMember->newTopic( "Teddy bears are great" );

$aShopper = new Shopper();
$aShopper->setName( "Mary", "Jones" );
$aShopper->showWelcomeMessage();
$aShopper->addToCart( "Ornate Table Lamp" );

This code displays:


Hi John Smith, welcome to the forums!
Creating new topic: Teddy bears are great
Hi Mary Jones, welcome to our online store!
Adding Ornate Table Lamp to cart

Creating and using interfaces

Ribbon cable

Interfaces are, in many ways, similar to abstract classes. An interface is a template that defines how one or more classes should behave.

There are several key differences between an abstract class and an interface:

  • No methods in an interface can be implemented within the interface. They are all "abstract". (In an abstract class, you can mix abstract and non-abstract methods.)
  • An interface can't contain properties, only methods.
  • A class implements an interface, whereas a class extends or inherits from an abstract class.
  • A class can implement more than one interface at the same time. (That same class can also extend a parent class.) In contrast, a class can only be derived from one parent class (abstract or otherwise).

As with an abstract class, an interface declares one or more methods that must be implemented by any class that implements the interface. The syntax looks like this:


interface MyInterface {
  public function aMethod();
  public function anotherMethod();
}

To create a class that implements an interface, you write:


class MyClass implements MyInterface {

  public function aMethod() {
    // (code to implement the method)
  }

  public function anotherMethod() {
    // (code to implement the method)
  }

}

Interfaces are useful when you want to create several otherwise unrelated classes that need to implement a common set of features.

For example, a web forum might well contain a Member class for forum members, and a Topic class to store topics that members create in the forum. Inheritance-wise, these classes will likely be unrelated, since they perform quite different functions.

However, let's say that we want to be able to save and retrieve both Member and Topic objects to and from a MySQL database. To achieve this, we can create an interface called Persistable that specifies the methods required for objects to persist in a database:

interface Persistable {

  public function save();
  public function load();
  public function delete();

}

Now let's create our Member class, and make it implement the Persistable interface. This means that the class must provide implementations for save(), load() and delete():

class Member implements Persistable {
 
  private $username;
  private $location;
  private $homepage;
 
  public function __construct( $username, $location, $homepage ) {
    $this->username = $username;
    $this->location = $location;
    $this->homepage = $homepage;
  }
   
  public function getUsername() {
    return $this->username;
  }

  public function getLocation() {
    return $this->location;
  }

  public function getHomepage() {
    return $this->homepage;
  }

  public function save() {
    echo "Saving member to database<br>";
  }

  public function load() {
    echo "Loading member from database<br>";
  }

  public function delete () {
    echo "Deleting member from database<br>";
  }

}

Similarly, our Topic class also implements Persistable, so it needs to include save(), load() and delete() methods too:

class Topic implements Persistable {
 
  private $subject;
  private $author;
  private $createdTime;
 
  public function __construct( $subject, $author ) {
    $this->subject = $subject;
    $this->author = $author;
    $this->createdTime = time();
  }
   
  public function showHeader() {
    $createdTimeString = date( 'l jS M Y h:i:s A', $this->createdTime );
    $authorName = $this->author->getUsername();
    echo "$this->subject (created on $createdTimeString by $authorName)<br>";
  }

  public function save() {
    echo "Saving topic to database<br>";
  }

  public function load() {
    echo "Loading topic from database<br>";
  }

  public function delete () {
    echo "Deleting topic from database<br>";
  }

}

Naturally, since this is a fictional forum, the save(), load() and delete() methods don't actually interact with a database. Instead, they just display messages.

We've also included some class-specific methods, such as Member::getUsername() to return the member's username, and Topic::showHeader() to display the topic's subject, author and creation time.

Now we can create Member and Topic objects and call their getUsername() and showHeader() methods respectively. What's more, since we know that both classes implement the Persistable interface, we can also call methods such as save(), load() and delete() on the objects, knowing that the objects must implement those methods:

$aMember = new Member( "fred", "Chicago", "http://example.com/" );
echo $aMember->getUsername() . " lives in " . $aMember->getLocation() ."<br>";
$aMember->save();

$aTopic = new Topic( "Teddy Bears are Great", $aMember );
$aTopic->showHeader();
$aTopic->save();

This code displays the following:


fred lives in Chicago
Saving member to database
Teddy Bears are Great (created on Wednesday 25th May 2011 02:19:14 AM by fred)
Saving topic to database

As you can see, an interface lets you bring together otherwise unrelated classes in order to use them for a specific purpose, such as storing the objects in a database. Remember also that one class can implement more than one interface:


class MyClass implements anInterface, anotherInterface {
  ...
}

Interfaces are a powerful feature of OOP, and there's more to them than we've covered here. Find out more about them in the PHP manual.

Summary

In this tutorial you explored one of the most powerful features of object-oriented programming: the concept of inheritance. You learned:

  • How inheritance works, and how it lets you reuse and extend classes easily.
  • How to create child classes in PHP using the extends keyword.
  • Why you might need to override methods in your child classes.
  • How to use the parent keyword to access a parent class's methods.
  • All about final methods and classes, and when you might need to use them.
  • The concept of abstract classes for creating templates for child classes.
  • How to use interfaces to define a set of behaviours for unrelated classes to implement.

If you've followed all the tutorials in this series so far, you now have all the knowledge you need to write complex object-oriented PHP applications. Congratulations!

In the next tutorial in the series, we wrap up the topic of PHP object-oriented programming by looking at some extra useful OOP features that PHP offers: autoloading, serializing, and querying objects.

Happy coding!

Learn PHP With Ease!

Written by Matt Doyle — ELATED's resident Web programming expert — Beginning PHP 5.3 is a complete introduction to PHP, covering everything in these tutorials and lots more besides. Find out how to:

  • Set up PHP on your computer
  • Use strings, arrays, functions and objects
  • Create interactive Web forms
  • Handle cookies and sessions
  • Work with files on the server
  • Build database-driven sites with MySQL
  • Send emails from your scripts
  • Create images on the fly with PHP
  • Work with regular expressions
  • Write robust, secure PHP applications

...and lots more!

“What a pleasure it's been spending hours and hours studying PHP with this magical book.” — Lulio, Florida
“The book is not only great for learning, but I find myself using it constantly as a reference as well!” — David A. Stoltz

Buy Beginning PHP 5.3 now from Amazon.comBeginning PHP 5.3 or Amazon.co.ukBeginning PHP 5.3.

Follow Elated

Related articles

Responses to this article

2 responses (oldest first):

29-Jun-11 19:23
Thank you for these tutorials Matt. They have been such a help that I just ordered your book as a thank you. Can't wait for it to arrive!
23-Aug-11 22:57
@TunaMaxx: I only just saw your comment 2 months later - no idea why!! Anyway, thank you - I really appreciate it. Hope you enjoyed the book.

Post a response

Want to add a comment, or ask a question about this article? Post a response.

To post responses you need to be a member. Not a member yet? Signing up is free, easy and only takes a minute. Sign up now.

Top of Page