Lesson 7 - Inheritance in PHP
In the previous exercise, Solved tasks for OOP in PHP lessons 4-6, we've practiced our knowledge from previous lessons.
In the previous tutorial, Solved tasks for OOP in PHP lessons 4-6, we went over the differences between reference and primitive data types. Today, we will go even deeper into the world of object-oriented programming. In the first lesson, I told you all that OOP is built upon 3 pillars: encapsulation, inheritance and polymorphism. We've already gone into encapsulation, i.e. class method/property modifiers. Today is all about inheritance.
Inheritance
Inheritance is one of the main principles of OOP and is used when creating new data structures based on old ones. We'll apply all of these concepts to our Human instances.
In case you forgot, our humans are able to greet, sleep and run. However, let's say we wanted to add a PHP programmer to our application. He/she will be able to do the same as things a regular human can and also be able to program.
Start by adding a new class file named PhpProgrammer.php to the classes folder. Essentially, we could just copy the Human class over and add a program() method to it:
<?php class PhpProgrammer { public $firstName; public $lastName; public $age; private $tiredness = 0; public function __construct($firstName, $lastName, $age) { $this->firstName = $firstName; $this->lastName = $lastName; $this->age = $age; } public function sleep($time) { $this->tiredness -= $time * 10; if ($this->tiredness < 0) $this->tiredness = 0; } public function run($distance) { if ($this->tiredness + $distance <= 20) $this->tiredness += $distance; else echo("I'm too tired."); } public function greet() { echo('Hi, my name is ' . $this->firstName); } public function program() { echo("I'm programming..."); } public function __toString() { return $this->firstName; } }
This code is very redundant. If we wanted to change something in Human, we'd have to change it here as well as in other similar classes. Hopefully, you agree that this is not the best way to extend an application.
Instead, we'll modify our new class using inheritance:
<?php class PhpProgrammer extends Human { public function program() { echo("I'm programming..."); } }
PHP will now inherit the PhpProgrammer from the Human class. To do this, we used the "extends" keyword, which takes all of the methods and properties from an ancestor class and adds them to an ancestor class.
As you may have noticed, I also added a program() method to the class. Let's modify index.php to make $john a PhpProgrammer. I also made him program and greet so you see that the PhpProgrammer class truly is derived from the Human class:
{PHP} require_once('classes/Human.php'); require_once('classes/PhpProgrammer.php'); $carl = new Human('Carl', 'Smith', 30); $john = new PhpProgrammer('John', 'Newman', 24); $carl->run(10); $carl->run(10); $carl->sleep(1); $carl->run(10); $john->greet(); echo('<br />'); $john->program();
{PHP} class Human { public $firstName; public $lastName; public $age; private $tiredness = 0; public function __construct($firstName, $lastName, $age) { $this->firstName = $firstName; $this->lastName = $lastName; $this->age = $age; } public function sleep($time) { $this->tiredness -= $time * 10; if ($this->tiredness < 0) $this->tiredness = 0; } public function run($distance) { if ($this->tiredness + $distance <= 20) $this->tiredness += $distance; else echo("I'm too tired."); } public function greet() { echo('Hi, my name is ' . $this->firstName); } public function __toString() { return $this->firstName; } }
{PHP} class PhpProgrammer extends Human { public function program() { echo("I'm programming..."); } }
Everything works as expected. John has all of the Human class' methods and properties along with the ones we added to the PhpProgrammer class:
Inheritance benefits
Surely, you see the benefits of inheritance. This way, we don't have to write the same exact stuff over and over. We get to focus on new functionality, and the rest gets inherited. The benefits are astronomical, we can extend pre-existing components, i.e. re-use them. We don't have to write loads of redundant (duplicated) code, and most importantly, when we change something in the parent class, the changes are inherited everywhere else automatically. Thanks to that, we won't ever have to go back and change 20 classes, forget something in one, and end up causing an error. We are human beings and we will always make mistakes, so we have to use programming techniques to minimize the odds of us making mistakes.
Sometimes, the parent class is referred to as the "ancestor" (the Human class) and the class that inherits from the ancestor, the "descendant" (the PhpProgrammer class). The descendant can have methods all to itself or modify the methods provided by the parent class (see below). You may also encounter similar terms such as "subclass" and "superclass".
In object modeling, the notation for inheritance is an empty arrow pointing to an ancestor.
Programming languages that support inheritance either support simple inheritance, where the class inherits from a single class - or multiple inheritance, where the class inherits from several classes at a time. Multiple inheritance is not a very common thing to use, you'll see why later on. I will also show you a work-around to this limitation. PHP only supports simple inheritance. Multiple inheritance is found in languages such as C++. As you may have guessed, a descendant can have other descendants and so on.
Inheritance and constructors
Let's add another property to our PhpProgrammer, an IDE (Integrated Development Environment), that he/she will use to program.
Add $ide as a public property to PhpProgrammer class:
public $ide;
Then, modify the program() method so that it prints out which IDE the programmer is using:
public function program() { echo("I'm programming in {$this->ide}..."); }
Calling a parent constructor
Let's make it so that we can set the $ide property in the PhpProgrammer's constructor. PHP inherited the constructor from the ancestor, so now that we need a new constructor, we'll have to "re-declare" it.
It should have all of the parameters needed to create the ancestor + any properties that the descendant requires. Since we have to set the ancestor's properties in the ancestor's constructor, we'll do it in a way that it doesn't re-initialize the object. We'll do this by setting the PhpProgrammer's properties and setting the rest by calling the ancestor's constructor. To do that we use a parent variable. Don't worry about the double colon operator, we'll get to it in future lessons.
public function __construct($firstName, $lastName, $age, $ide) { $this->ide = $ide; parent::__construct($firstName, $lastName, $age); }
Last of all, modify John's declaration in index.php :
{PHP} require_once('classes/Human.php'); require_once('classes/PhpProgrammer.php'); $carl = new Human('Carl', 'Smith', 30); $john = new PhpProgrammer('John', 'New', 24, 'Eclipse'); $carl->run(10); $carl->run(10); $carl->sleep(1); $carl->run(10); $john->greet(); echo('<br />'); $john->program();
{PHP} class Human { public $firstName; public $lastName; public $age; private $tiredness = 0; public function __construct($firstName, $lastName, $age) { $this->firstName = $firstName; $this->lastName = $lastName; $this->age = $age; } public function sleep($time) { $this->tiredness -= $time * 10; if ($this->tiredness < 0) $this->tiredness = 0; } public function run($distance) { if ($this->tiredness + $distance <= 20) $this->tiredness += $distance; else echo("I'm too tired."); } public function greet() { echo('Hi, my name is ' . $this->firstName); } public function __toString() { return $this->firstName; } }
{PHP} class PhpProgrammer extends Human { public $ide; public function __construct($firstName, $lastName, $age, $ide) { $this->ide = $ide; parent::__construct($firstName, $lastName, $age); } public function program() { echo("I'm programming in {$this->ide}..."); } }
Application output:
Protected modifier
In the example above, the parent's private properties won't be accessible to the descendant. All we can access from there is public properties and methods. Private properties and methods are seen as hidden class logic, which is kept from the descendant. In other words, it can use them, but it can't change them. You can check for yourself, a PhpProgrammer can sleep, but we cannot access the $tiredness property directly. This way, every human will be able to sleep in the same way without directly modifying the parent's method.
If we wanted to make a property (or a method) inaccessible from the outside, but accessible for descendants, we would use a protected access modifier. Let's add another method to the Human class, that returns his/her full name ($firstName + $lastName). We will only be able to use this method from inside of the class and from inside classes that inherit from it. To do so, we will mark it as protected:
protected function fullName() { return $this->firstName . ' ' . $this->lastName; }
We won't be able to call the method in index.php, but we will be able to call it from within the Human class as well as from within the PhpProgrammer class. We will continue on this topic in the next lesson, Polymorphism, final attributes and autoloader in PHP, as well as get into polymorphism.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 34x (2.74 kB)
Application includes source codes in language PHP