In a previous chapter, we learned how we could use inheritance to share common properties and methods between our classes, effectively creating a template. We're going to build on our previous knowledge within this chapter. Excited? Then let's jump right in.
Abstract Classes
For some classes, their sole purpose is to provide common properties and methods. They live for it. When they go to sleep at night, they dream of it.
They'll never be instantiated, because they make no sense on their own. Consider the following classes, that I've left empty for clarity.
<?php
class Animal {}
class Panda extends Animal {}
class Owl extends Animal {}
class Giraffe extends Animal {}
While the Animal class might provide lots of shared features to the Panda, Owl and Giraffe classes, it doesn't make sense to instantiate it directly. Our Animal class is a prime example of an abstract
class. Let's go ahead and make it one.
<?php
abstract class Animal
{
/**
* Is the animal awesome (all are).
*
* @var boolean
*/
private $awesome = true;
/**
* Access the awesome attribute.
*
* @return boolean
*/
public function isAwesome()
{
return $this->awesome;
}
}
In the above example, we've declared our class as abstract, simply by starting the class definition with the keyword abstract
. Everything else should feel familiar. If we were to extend this class, all of our animals would inherit the isAwesome()
method.
Let's try to instantiate the Animal
class directly.
$animal = new Animal;
What happens when we execute the code?
Fatal error: Cannot instantiate abstract class Animal in <file> on line <line>.
Now that our class is marked as abstract, it can no longer be instantiated. This is desirable, since the class has only a single purpose, to provide functionality to other classes.
Abstract Methods
Abstract methods are used within abstract classes. Once again, the keyword abstract
is used to define an abstract method. Let's lead with an example.
<?php
abstract class Animal
{
abstract public function makeNoise();
}
Here, we have defined a new method called makeNoise()
using the abstract
keyword before the scope. You'll notice that the makeNoise()
method has no body, instead it's simply terminated with a semi ;
colon.
What a pointless method!
You're quite right. On it's own, it is quite pointless. In fact, an abstract method can't even be called. So why bother with it? Well you see, the abstract method forms a type of 'contract' with all classes that extend it. It's telling PHP "Any classes which extend this one, MUST define a makeNoise()
method.".
Let's demonstrate this by adding another class.
<?php
abstract class Animal
{
abstract public function makeNoise();
}
class Dog extends Animal
{
}
We've extended the base Animal
class within our Dog
class. Let's try to instantiate a new Dog
, shall we?
$dog = new Dog;
Now we'll simply execute the code and... oh no..
PHP Fatal error: Class Dog contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Animal::makeNoise) in <file> on <line>.
Fortunately, I've always found PHP errors to be quite easy to understand. PHP is telling us that our Dog
class contains an abstract method from the Animal base class called Animal::makeNoise
.
PHP has given us two options. Firstly, we could make the Dog
class an abstract
class too, which would mean that we couldn't instantiate it, but it could be extended. Our second, and more appropriate option is to implement the makeNoise()
method on the Dog
class.
Using the second option, would fulfil the 'contract' that our abstract class defines. Let's do that.
<?php
class Dog extends Animal
{
public function makeNoise()
{
echo 'Woof!';
}
}
As you can see, we don't put the abstract
keyword in this time. This is because we're implementing the method, and we want it to be callable. When implementing an abstract method, it's important to note that the method signature (scope, name and parameters) must be identical to that of the abstract method.
Now that we've implemented our makeNoise()
method, our Dog
class can finally be instantiated.
$dog = new Dog;
What's the point of defining the abstract method, if we're just going to write the full method in all of our classes?
I thought you might ask this question. Let's consider a scenario. We change our Animal
base class and remove the abstract method.
<?php
abstract class Animal
{
}
We can still define makeNoise()
on all of our animal classes. However, what if we don't implement it on one of them. When we attempt to call the makeNoise()
method, our code is going to break.
Using the abstract method to build a contract with the classes that extend our abstract class, means that we have built some trust. We can use the makeNoise()
method without checking that it first exists. We have confidence that all animals will have this method.
In the next chapter, we'll take a look at a technique called polymorphism
, and while it sounds scary, I promise that you'll find it quite useful.
Remember that abstract classes can contain both normal and abstract methods. This is what makes them different from the interfaces that you'll discover in the next chapter.
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!