Chapter 08 ยท Going Deeper
Abstract Classes & Interfaces
Sometimes a parent class is only an idea, never a real thing. You can meet a Student and a Lecturer โ but have you ever met a plain "Person-in-general" walking down the street? This chapter teaches PHP's two tools for describing ideas and contracts: abstract classes and interfaces.
Abstract classes โ templates that can't be built
"Shape" is an idea. You can draw a circle, a square, a triangle โ but you cannot draw "a shape in general". Yet all shapes share things: they all have an area, they can all be described. An abstract class captures exactly this: shared code + a promise of what every child must provide โ while forbidding anyone from building the raw idea itself.
<?php
abstract class Shape {
public function __construct(public string $color) {}
// Abstract method: NO body. Every child MUST write its own version.
abstract public function area(): float;
// Normal method: shared by all children, written once.
public function describe(): string {
return "A $this->color shape with area " . $this->area();
}
}
class Circle extends Shape {
public function __construct(string $color, private float $radius) {
parent::__construct($color);
}
public function area(): float {
return round(3.14159 * $this->radius ** 2, 2);
}
}
class Rectangle extends Shape {
public function __construct(string $color, private float $w, private float $h) {
parent::__construct($color);
}
public function area(): float {
return $this->w * $this->h;
}
}
$shapes = [new Circle("red", 5), new Rectangle("blue", 4, 6)];
foreach ($shapes as $shape) {
echo $shape->describe() . "<br>";
}
$s = new Shape("green"); // ๐ฅ Error: Cannot instantiate abstract class
Three rules to memorise:
abstract classโ cannot be created withnew. It exists only to be extended.- An
abstractmethod has no body โ just a signature ending in;. It is a promise: "every child will implement this." - If a child forgets to implement an abstract method, PHP refuses to run. The contract is enforced.
Look at describe() โ it calls $this->area() even though
Shape itself has no idea how to compute an area! It trusts that whichever child is
running will supply one. Shared logic on top, specific logic below: that's the abstract-class
superpower.
Interfaces โ pure contracts
Think of a wall socket. It doesn't care whether you plug in a phone charger, a kettle or a TV. It only demands one thing: your plug must have the right pins. An interface is that socket shape โ a list of methods a class promises to have, with zero code inside. Any class, from any family, can promise to "fit the socket".
interface Payable {
public function pay(float $amount): string; // no body โ only the promise
}
interface Printable {
public function printOut(): string;
}
// A class "implements" an interface โ and may implement several at once
class TuitionInvoice implements Payable, Printable {
public function __construct(public string $student, public float $total) {}
public function pay(float $amount): string {
$this->total -= $amount;
return "Paid K$amount. Remaining: K$this->total";
}
public function printOut(): string {
return "INVOICE โ $this->student โ K$this->total";
}
}
class LibraryFine implements Payable {
public function __construct(public float $total) {}
public function pay(float $amount): string {
$this->total -= $amount;
return "Fine reduced by K$amount.";
}
}
// The magic: this function accepts ANYTHING that fits the Payable socket
function processPayment(Payable $bill, float $amount): void {
echo $bill->pay($amount) . "<br>";
}
processPayment(new TuitionInvoice("Chanda", 5000), 1500);
processPayment(new LibraryFine(80), 50);
The function processPayment() doesn't know or care what kind of bill it received.
It only knows: "this thing promised it can pay()". Invoices and fines aren't even
related by inheritance โ they just fit the same socket. That is polymorphism at its purest.
Abstract class vs interface โ which one when?
| Abstract class | Interface | |
|---|---|---|
| Can contain real, shared code? | โ Yes (normal methods, properties) | โ No โ method signatures and constants only |
| How many can a class take? | Only one (extends one parent) | Many (implements A, B, C) |
| The relationship in words | "is a" โ Circle is a Shape | "can do" โ Invoice can be paid |
| Typical names | Nouns: Shape, Person, Model | Abilities: Payable, Printable, Countable |
Sharing actual code between related classes โ abstract class. Guaranteeing that unrelated classes offer the same ability โ interface. Still unsure? Start with an interface โ it's the lighter promise.
Create an interface Notifiable with one method
send(string $message): string. Implement it in two classes:
EmailNotifier (returns "EMAIL: your message") and
SmsNotifier (returns "SMS: your message"). Write a function
alertStudent(Notifiable $channel) that sends "Results are out!" through whichever
channel it receives. Test with both.
Show solution
<?php
interface Notifiable {
public function send(string $message): string;
}
class EmailNotifier implements Notifiable {
public function send(string $message): string {
return "EMAIL: $message";
}
}
class SmsNotifier implements Notifiable {
public function send(string $message): string {
return "SMS: $message";
}
}
function alertStudent(Notifiable $channel): void {
echo $channel->send("Results are out!") . "<br>";
}
alertStudent(new EmailNotifier());
alertStudent(new SmsNotifier());
What happens if you write new Shape() when Shape is abstract?
How many interfaces can one class implement?
- An abstract class is an idea: it can hold shared code but can never be built with
new. - An abstract method has no body โ every child is forced to implement it.
- An interface is a pure contract: method signatures only, no code.
- A class
extendsone parent but canimplementsmany interfaces. - Type-hinting an interface (
function f(Payable $x)) accepts any class that fits the contract โ powerful polymorphism. - Rule of thumb: shared code โ abstract class; shared ability โ interface.