Inheritance and Interfaces in PHP
PHP is a widely used and popular web development language. If you need to brush up on your PHP skills, read on for a great tutorial.
Join the DZone community and get the full member experience.
Join For FreeIntroduction
Once upon a time, there was a web developer who used to spend weeks and weeks updating the same lines of code. This gave him very bad headaches. One day, while browsing blog posts, he came across the words Object-Oriented Programming and what happened next is astonishing…
In the previous post, you discovered the basics of the OOP paradigm in PHP, from its simple syntax to the advantages of reusing the same code in a straightforward but still powerful way.
Today you will learn how to best manage these objects and create long and safe relationships among them using inheritance and interfaces.
Inheritance
What Is Inheritance in OOP?
We are now entering in one of the four basic pillars of Object-Oriented Programming in PHP.
Inheritance, alongside encapsulation, abstraction, and polymorphism, needs to be mastered to become proficient in programming and really improve your skills.
Inheritance relates classes to each other in a parent-child model.
This is very useful when extracting characteristics that the related classes must have in common and allow you to implement further methods and properties without rewriting the same code again and again.
Like the last post, let’s use the Building
class as an example.
In this example, we are presuming that when you want to instantiate a new building the work to build it has been completed and it is open to the public.
class Building
{
$inConstruction = false;
$isOpen = true;
function closeBuilding()
{
return $this->isOpen = false;
}
function openBuilding()
{
return $this->isOpen = true;
}
}
class School extends Building
{
}
class Office extends Building
{
$isOpen = false;
}
We have three classes here, the parent class which is the Building
class, and its children that inherit the building features, both properties and methods.
We can now instantiate specific Schools and Offices rather than an abstract class, Building
.
$mySchool = new School();
$myOffice = new Office();
echo $myShool->closeBuilding(); // The output will be “false” because of the parent Building
echo $myOffice->isOpen; // The output will be “false” because of the child Office
As you can see, I haven’t actually written anything inside the School
class but it still inherits all the properties and methods of its parent class, Building
.
This principle is called DRY (Don’t repeat yourself) and its goal is to reduce the amount of code we write time and time again.
I am sure you have also noticed that the Office
class has a different value on the $isOpen
variable than its parent. In fact, by writing the same property or method in the of the parent into the child we overwrite its functionality.
Basically, when instantiated, $myOffice
is closed; instead $mySchool
follows the behavior of Building
and $isOpen
remains false ($mySchool
is open to visit).
Why Use Inheritance?
There are several current schools of thought about this topic. Some say inheritance is good, some senior developers will kill you if you just think about using it in a live project, but I think that as most of the time the truth is somewhere in the middle.
Inheritance is an amazing way to save time, as it allows you to avoid having to write the same things over and over again; it is very simple to understand and for a novice in OOP is the right path to follow.
It is also true that inheritance has several limitations and, for an experienced web developer, there are better choices.
My advice is to use inheritance as long as you are 100% comfortable with this technique, you understand how to use it, when to use it to share functionalities and when to, instead, use other technique like composition, design patterns (the factory pattern is a really close case), or even just add a trait to the class.
The 'extends' Keyword
Nobody knows why the keyword to create inheritance is not inherits or something more consistent and easy to remember, but here we are.
To allow a class to inherit from another class we need to specify the keyword extends
and the parent class just after the name of the child class and before the opening braces.
A situation to be careful about is that a child class must always depend only on a single parent class, which mean multiple inheritances is not supported.
Think about it in this way: the Office
class we used in the previous example cannot inherit features from the Building
class and the Animal
class at the same time. They just have no characteristics in common.
The 'final' Keyword
The final
keyword is a really easy concept introduced for the first time in PHP 5.
Simply put, it prevents methods or classes from being extended.
Let’s look at a quick example.
Presume that the mayor of our imaginary city decided that all buildings must have at least one emergency exit. Take a look at the following sample of our code.
class Building
{
...
function setFireExtinguisher()
{
$this->fireExtinguisherCount = 1;
}
}
class Office extends Building
{
function setFireExtinguisher()
{
$this->fireExtinguisheCount = 0;
}
}
In this case, the setFireExtinguisher()
method of the Office
class has been overwritten by the Building
class and set the fireExtinguisheCount
to 0, which, as the mayor decided, is illegal.
A way to prevent this and keep the architects from going to jail is it to add the final
keyword to the method.
class Building
{
...
final function setFireExtinguisher()
{
$this->fireExtinguisherCount = 1;
}
}
class Office extends Building
{
function setFireExtinguisher()
{
$this->fireExtinguisheCount = 0;
}
}
Now the code for the setFireExtinguisher()
method of the Office
class cannot be used and it will actually result in a Fatal error “Cannot override final method Building->setFireExtinguisher()”.
A very simple concept with very easy implementation.
There is only a rule in this section of the article: you cannot use final
on attributes. Attributes are variables and, as the name suggests, they need to be able to vary.
You can use final
on classes that you do not want to be extended or methods that must not be overwritten.
Interfaces
What are the mandatory steps that architects and constructors need to follow in order to create a building?
There are several regulations that need to be followed, especially in public areas, such as adding several first aid stating, setting evacuation plans up, etc.
An interface lets you arrange these steps in advance and oblige the classes to implement them accordingly.
In a very simplistic way, if a class is a blueprint of an object then you can think of an interface like a blueprint for classes. Think of an interface as a pure template.
Let me explain.
Why do you need object interfaces in the first place? In PHP, interfaces define which methods a class must implement. At the same time, interfaces do not specify how these methods need to be implemented. You will see soon that this gives you a lot of freedom when managing your classes.
The 'interface' Keyword
The syntax to create interfaces is almost the same as the one to create classes. Naming an interface usually follows some conventions that will make it easier for others to understand your code, the most used convention are:
- ThingInterface;
- IThing;
- I_Thing;
In the following example, I am going to create interfaces that I will use in the Building
class.
The mayor of our city is very concerned about security and he decided that every building in our city must have a fire alarm and an assembly point in case of calamity.
Let’s code those interfaces.
interface FireAlarmInterface
{
function setFireAlarm();
}
interface AssemblyPointInterface
{
function setAssemblyPoint();
}
There are two things that you should notice in the previous snippet:
The first one is that interfaces look very similar to classes.
The second is that there are no braces that open the block of code in the methods. The reason is that interfaces do not determinate how methods need to be implemented, they just determinate what methods need to be implemented.
The 'implements' Keyword
Now that the interfaces have been created we need to implement them in our Building
class. To implement an interface, you need to use the implement's keyword.
A fundamental particularity that you have to take note of is that all methods in the interfaces must be included in the classes that implement the interface.
This is a mandatory rule, and there is no exception.
PHP will let you know if you forgot methods by throwing a fatal error on the screen.
class Building implements FireAlarmInterface
{
function setFireAlarm()
{
$this->fireAlarm = true;
}
}
As said, all methods in the interface must be implemented within a class; failure to do so will result in a fatal error.
Unlike inheritance, a class may implement more than one interface.
In fact, if desired, you can list as many interfaces you like by listing them and separating each interface with a comma.
class Building implements FireAlarmInterface, AssemblyPointInterface
{
…
}
Although this is a very impressive feature of PHP, let me highlight once again that each method of each implemented interface must be included within the class and this can lead to a lot of disorder.
So be economic with your use of interfaces and use them only after you have studied all the incoming problems that can take place.
List of Built-It Interfaces Included in PHP
PHP has a handful of interfaces integrated into the language.
Although the majority of them will be unlikely to be used on a day-to-day basis, some could be very useful and will definitely save you some time during the development process of your project.
Traversable
As the name suggests, the Traversable
interface analyzes a class and returns a boolean value depending on whether the class that is implementing the traversable is using a foreach
.
This interface has no methods, its intent is to be a foundation for traversable classes.
Traversable {
}
You can use this interface in your conditional statement to check if something is usable with a foreach
.
Here is a quick example
if( $buildings instanceof Traversable ){
foreach ($buildings as $building){
…
}
}
Notice that the interface does not work on arrays and objects, which means you cannot user Traversable
to check if a variable of these types can be used in an array.
Why do you even need to verify them in the first place?
Iterator
This interface works on objects or iterators that can repeat themselves. This simply means that you can extend the Iterator
interface to whichever element is traversable and include the following methods on the class.
Iterator extends Traversable {
function current ()
{
}
function key ()
{
}
function next ()
{
}
function rewind ()
{
}
function valid ()
{
}
}
Here is a sample of how this interface might really be used:
public function rewind()
{
$this->position = 0;
}
public function current() {
return $this->array[$this->position];
}
public function key() {
return $this->position;
}
public function next() {
++$this->position;
}
public function valid() {
return isset($this->array[$this->position]);
}
IteratorAggregate
The IteratorAggregate
interface is simply another method to implement an Iterator in your code.
IteratorAggregate extends Traversable {
function getIterator ()
{
}
}
The benefit of using this interface is the speed. In fact, IteratorAggregate
is much faster than other options.
It's important to note that that, despite the name, this is not an Iterator but it is a Traversable
. This means there are no next
, key
, current
, valid
, or rewind
methods available.
Throwable
The Throwable
interface is used for all the objects that can be thrown via the throw
statement,
Theses include errors and exceptions.
Here is the interface and its complete list of methods:
interface Throwable {
function getMessage ()
{
}
function getCode ()
{
}
function getFile ()
{
}
function getLine ()
{
}
function getTrace ()
{
}
function getTraceAsString ()
{
}
function getPrevious ()
{
}
function __toString ()
{
}
}
PHP cannot implement this interface directly, it can only be extended via an exception.
There's more to come in this series, so stay tuned!
Published at DZone with permission of Nico Anastasio. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments