Abstract classes and interfaces
In the previous lesson, a child class received ready methods from its parent. Sometimes, though, the parent itself does not know how to do part of the work. Take shapes: a square and a circle both have an area, but they compute it differently, and you cannot build a "shape in general".
In the family of blueprints, this is a template blueprint: the walls and the roof are drawn, and where the kitchen should be it says "draw it yourself". Nobody builds a house from a template; it gets completed first.
There is another document too, a job description. It lists what a worker must be able to do, but not how exactly to do it.
In PHP, the template is an abstract class, and the job description is an interface.
Abstract class
An abstract class is a class marked abstract that you cannot create an object of: it exists only as a parent for other classes.
<?php
abstract class Shape
{
public function __construct(protected float $size) {}
abstract public function area(): float;
public function describe(): string
{
return $this::class . ' ' . $this->area();
}
}Let's walk through it step by step:
abstract class Shapedeclares a template: you cannot build aShapeobject.- The constructor and the property
$sizeare the same as in an ordinary class: an abstract class can hold data. abstract public function area(): float;is an abstract method. It has only a signature, that is, the name, the parameters and the return type, and a semicolon instead of a body. Every child class must write it.describe()is a ready method. It calls$this->area()even thougharea()has no body inShape: as you already know,$thisfinds the version from the object's class. And$this::classgives the name of that class as a string.
Here the template picture falls short: in PHP you never fill in the template itself; you write a child class with the missing methods.
Interface
An interface is a list of methods a class promises to have, without their code. You declare it with the word interface, and a class signs up for it with the word implements:
interface HasCorners
{
public function corners(): int;
}
class Square extends Shape implements HasCorners
{
public function area(): float
{
return $this->size ** 2;
}
public function corners(): int
{
return 4;
}
}interface HasCornersdeclares one method,corners(), without a body. Interface methods are alwayspublic.SquareextendsShapeand implementsHasCorners, so it writes both methods:area()for the parent andcorners()for the interface.- A class has one parent but may implement any number of interfaces:
implements A, B.
Besides methods, an interface may declare constants. It never holds properties with values or ready code. Since PHP 8.4, an interface may also require a property, but such a property still has no value in the interface.
If the class lacks one of the interface's methods, PHP stops the script with a fatal error at the class declaration:
Fatal error: Class Square contains 1 abstract method and must therefore be declared abstract or implement the remaining method (HasCorners::corners)Abstract class or interface
This question often comes up in a PHP interview. You can keep the answer in mind as a table:
| Abstract class | Interface | |
|---|---|---|
| Properties with values | allowed | not allowed |
| Methods with a body | allowed | not allowed |
| How many per class | one, through extends | any number, through implements |
An object through new | not allowed | not allowed |
Use an abstract class when a family of classes shares data and code. Use an interface when different classes need the same ability, but each has its own code for it.
instanceof and polymorphism
Let's add a circle and look at the shapes together:
class Circle extends Shape
{
public function area(): float
{
return 3.14 * $this->size ** 2;
}
}
$sq = new Square(3);
foreach ([$sq, new Circle(1)] as $shape) {
echo $shape->describe(), '; '; // Square 9; Circle 3.14;
}
echo "\n";
var_dump($sq instanceof Shape); // bool(true)
var_dump(new Circle(1) instanceof HasCorners); // bool(false)Let's go through what it printed:
- The loop calls the same
describe()on every shape, and each shape computes its area in its own way. This is the polymorphism from the previous lesson: the loop code does not know whether it holds a square or a circle. - The square's
area()returns9.0of typefloat, andechoprints such a number without the.0, as9. instanceofchecks whether an object belongs to a class or an interface.$sq instanceof Shapegivestrueeven though$sqis aSquareobject: the check walks the whole chain of parents and all the interfaces.Circledoes not implementHasCorners, so the second answer isfalse. When the left side ofinstanceofis not an object, the answer isfalsetoo, not an error.
Here the job description analogy falls short. A person may be able to do everything it lists without ever signing it. In PHP, instanceof HasCorners gives true only for a class that wrote implements HasCorners: having a corners() method is not enough.
Common mistake
Trying to create an object of an abstract class:
$shape = new Shape(2); // ✗Fatal error: Uncaught Error: Cannot instantiate abstract class ShapeAn abstract class is a template, and PHP does not let you build an object from it. The same goes for an interface: new HasCorners() gives Cannot instantiate interface HasCorners. The fix is to create an object of a child class, for example new Square(2).
Key points
- An abstract class is a template: you cannot create an object of it, but it may have properties, a constructor, ready methods and abstract ones.
- An interface lists methods without bodies; a class signs up with
implementsand must write every method. - A class has one parent, but it may implement any number of interfaces.
instanceofis true for the object's class, all its parents and all the interfaces it implements.