Inheritance in OOP is the ability to pass the implementation of the class from parents to children. Yes, classes can have parents, and the technical way of referring to this feature is that a class extends from another class. When extending a class, we get all the properties and methods that are not defined as private, and the child class can use them as if they were its own. The limitation is that a class can only extend from one parent.
To show an example, let's consider our
Customer
class. It contains the
properties firstname
, surname
, email
, and
id
. A customer is actually a specific
type of person, one that is registered in our system, so he/she can
get books. But there can be other types of persons in our system,
like librarian or guest. And all of them would have some common
properties to all people, that is, firstname
and surname
. So
it would make sense if we create a Person
class, and make the Customer
class extend from it. The hierarchic tree
would look as follows:

Note how Customer
is
connected to Person
. The methods in
Person
are not defined in Customer
, as they are implicit from the extension.
Now save the new class in src/Domain/Person.php
, following our convention:
<?php namespace Bookstore\Domain; class Person { protected $firstname; protected $surname; public function __construct(string $firstname, string $surname) { $this->firstname = $firstname; $this->surname = $surname; } public function getFirstname(): string { return $this->firstname; } public function getSurname(): string { return $this->surname; } }
The class defined in the preceding code snippet does not look special; we
have just defined two properties, a constructor and two getters.
Note though that we defined the properties as protected, because if
we defined them as private, the children would not be able to
access them. Now we can update our Customer
class by removing the duplicate properties
and its getters:
<?php namespace Bookstore\Domain; class Customer extends Person { private static $lastId = 0; private $id; private $email; public function __construct( int $id, string $name, string $surname, string $email ) { if (empty($id)) { $this->id = ++self::$lastId; } else { $this->id = $id; if ($id > self::$lastId) { self::$lastId = $id; } } $this->name = $name; $this->surname = $surname; $this->email = $email; } public static function getLastId(): int { return self::$lastId; } public function getId(): int { return $this->id; } public function getEmail(): string { return $this->email; } public function setEmail($email): string { $this->email = $email; } }
Note the new keyword extends
; it tells
PHP that this class is a child of the Person
class. As both Person
and Customer
are
in the same namespace, you do not have to add any use
statement, but if they were not, you should let
it know how to find the parent. This code works fine, but we can
see that there is a bit of duplication of code. The constructor of
the Customer
class is doing the same job
as the constructor of the Person
class!
We will try to fix it really soon.
In order to reference a method or property of
the parent class from the child, you can use $this
as if the property or method was in the same
class. In fact, you could say it actually is. But PHP allows you to
redefine a method in the child class that was already present in
the parent. If you want to reference the parent's implementation,
you cannot use $this
, as PHP will invoke
the one in the child. To force PHP to use the parent's method, use
the keyword parent::
instead of
$this
. Update the constructor of the
Customer
class as follows:
public function __construct(
int $id,
string $firstname,
string $surname,
string $email
) {
parent::__construct($firstname, $surname);
if (empty($id)) {
$this->id = ++self::$lastId;
} else {
$this->id = $id;
if ($id > self::$lastId) {
self::$lastId = $id;
}
}
$this->email = $email;
}
This new constructor does not duplicate code.
Instead, it calls the constructor of the parent class Person
, sending $firstname
and $surname
,
and letting the parent do what it already knows how to do. We avoid
code duplication and, on top of that, we make it easier for any
future changes to be made in the constructor of Person
. If we need to change the implementation of
the constructor of Person
, we will
change it in one place only, instead of in all the children.