In the previous chapter, we learned about abstract classes, and how they form a partial contract. This is because abstract classes can contain both abstract methods and real methods. For example...
<?php
abstract class Animal
{
public function huggle()
{
echo 'I am huggled!';
}
abstract public function makeNoise();
}
Interfaces are very similar to abstract classes, except that they cannot contain logic. They are purely to shape the classes that implement them. This means that the huggle()
method above, would not be possible within an interface.
Let's define a new interface.
<?php
interface PandaInterface
{
/**
* Eat some food!
*/
public function eat($food);
/**
* Poop! (Normally after eating.)
*/
public function poop();
/**
* Sleepy time!
*/
public function sleep($time);
}
We define interfaces by using the interface
keyword. As you can see, we don't use class
at all this time. Like abstract classes, interfaces cannot be instantiated.
We've called our interface PandaInterface
, and while not required, it's considered good practice to add the 'Interface' suffix to the name.
Our PandaInterface
defines a number of methods that are common to pandas. All pandas eat, sleep and poop. They may do so differently, but they will definitely take part in these activities.
This means that any class which implements this interface must provide these methods, and the logic for them. Let's take a look at how we can implement the panda interface in our new class, the 'RedPanda'.
<?php
class RedPanda implements PandaInterface
{
}
Instead of the extends
keyword that we might use to indicate inheritance, this time we use the implements
keyword to indicate that our class must implement the methods within the interface.
Earlier, we discovered that a PHP class can only extend one class. This same is not true for interfaces. A PHP class may implement as many interfaces as necessary. For example, the following is perfectly legal.
<?php
class RedPanda implements PandaInterface, FurryInterface, CuteInterface
{
}
The class above, implements three different interfaces, and must implement the methods held within each of the interfaces.
A class can even extend another class while implementing interfaces, for example:
class RedPanda extends Animal implements PandaInterface, FurryInterface, CuteInterface
{
}
In the above example, either RedPanda
or its parent class Animal
must implement the methods held within the interfaces. As long as all are present, with each method on either of the classes, then the RedPanda
class can be instantiated.
It took me a while to understand interfaces, and their ideal use case, but once I'd worked it out, I couldn't stop using them! Simply put, interfaces create contracts so that you can trust that a class has the methods you want inside.
Polymorphism
Polymorphism is a horrible word, but a wonderful technique. All of the explanations I found online were overcomplicated and scary. Instead, let's lead with an example. We'll make use of our PandaInterface
. It enforces the implementation of three methods.
<?php
interface PandaInterface
{
/**
* Eat some food!
*/
public function eat();
/**
* Poop! (Normally after eating.)
*/
public function poop();
/**
* Sleepy time!
*/
public function sleep();
}
I've stripped out some of the parameters to simplify the example. Let's create two classes that implement this interface. Here is the first.
<?php
class RedPanda implements PandaInterface
{
/**
* Eat some food!
*/
public function eat()
{
echo "The red panda eats some fruit.\n";
}
/**
* Poop! (Normally after eating.)
*/
public function poop()
{
echo "The red panda takes a poop.\n";
}
/**
* Sleepy time!
*/
public function sleep()
{
echo "The red panda sleeps up a tree.\n";
}
}
Next, we have a second class to represent a Giant Panda.
<?php
class GiantPanda implements PandaInterface
{
/**
* Eat some food!
*/
public function eat()
{
echo "The giant panda eats some bamboo.\n";
}
/**
* Poop! (Normally after eating.)
*/
public function poop()
{
echo "The giant panda takes a giant poop.\n";
}
/**
* Sleepy time!
*/
public function sleep()
{
echo "The giant panda sleeps on the ground.\n";
}
}
It's time to create a new class. We're going to call it the ZooKeeper
. This class doesn't extend any other classes, and doesn't implement any interfaces.
<?php
class ZooKeeper
{
/**
* Care for a panda.
*
* @param PandaInterface $panda
* @return void
*/
public function care(PandaInterface $panda)
{
// Perform panda stuff.
$panda->eat();
$panda->poop();
$panda->sleep();
}
}
The ZooKeeper
class contains a single method to care for a panda. Instead of type-hinting a RedPanda
or GiantPanda
class directly, we type-hint the interface instead.
What this means, is that any class that implements the PandaInterface
interface, can be passed as a parameter to the care()
method. Not only that, but because we're type-hinting the interface directly, we can be sure that the instance passed to the method will have three methods, eat()
, poop()
and sleep()
.
Let's try our ZooKeeper
class.
<?php
// Create panda instances.
$redPanda = new RedPanda;
$giantPanda = new GiantPanda;
// Create the zookeeper.
$keeper = new ZooKeeper;
// Care for both pandas.
$keeper->care($redPanda);
$keeper->care($giantPanda);
First, we instantiate both panda implementations. Next, we create a new ZooKeeper
instance. Finally, we call the care()
method twice, passing both panda implementations.
Let's check the result.
The red panda eats some fruit.
The red panda takes a poop.
The red panda sleeps up a tree.
The giant panda eats some bamboo.
The giant panda takes a giant poop.
The giant panda sleeps on the ground.
You see, it doesn't matter what type of panda we have. As long as it's the right shape for a panda, we can be sure that it can eat, sleep and poop. Type-hinting the interface is our guarantee that these methods are present.
Polymorphism. A complicated word, for a simple concept! It's also one that's incredibly useful. Imagine that you have written a TaxCalculator
class, and that it needs to write some tax information to a long-term data store. We could create a DataStoreInterface
with read()
and write()
methods. This way we could create data stores for writing to the disk, writing to a database, or writing to a remote filesystem, and all of them would be interchangeable. We could let the user decide. That is how you create good applications. That is power.
My books are available online for free to encourage learning. However, if you'd like for me to keep writing, then please consider buying a digital copy over at Leanpub.com.
It's available in PDF, ePub, and Kindle format, and contains a bunch of extras that you won't find on the site. I have a full-time job, and I write my books in my spare time. Please consider buying a copy so that I can continue to write new books from the comfort of my sofa!