Abstract Classes vs Interfaces in PHP: Core Distinctions and Practical Rules
Abstract Classes
An abstract class cannot be instantiated. Once any method inside a class is marked abstract, the whole class must be declared abstract. The method itself only defines the signature—no body is allowed.
<?php
abstract class Repository
{
// force subclasses to supply the body
abstract protected function fetchKey(): string;
abstract protected function decorate(string $prefix): string;
// concrete method available to every subclass
public function dump(): void
{
echo $this->fetchKey() . PHP_EOL;
}
}
Rules for extending:
- A non-abstract child must implement every abstract method.
- Visibility must stay the same or be widened (
protected→publicis fine,privateis not). - Signature must match exactly-for-argument; optional parameters added by the child are allowed.
Extra notes:
- Properties are allowed.
- An abstract method cannot be
private; PHP raises a fatal error. - An abstract class may implement an interface without providing the bodies of its methods.
- Abstract classes can extend other abstract classes. You must not redeclare an inherited abstract method; doing so triggers a fatal error.
abstract class Animal
{
abstract public function feed(): void;
}
abstract class Mammal extends Animal
{
abstract public function walk(): void;
// redeclaring feed() here would fail
}
Interfaces
Interfaces describe a contract: they list method names and signatures but contain no implementation. All interface methods are implicitly public; using protected or private causes a fatal error.
interface Drawable
{
const MAX_WIDTH = 1920;
public function render(): string;
public function resize(int $w, int $h): void;
}
Implementing an interface:
- A concrete class must supply every method exactly as declared.
- A class may implement multiple interfaces, separated by commas.
- When both inheritance and interface implementation are used,
extendsmust precedeimplements. - Abstract classes may implement interfaces without fulfilling the methods.
- Method names must be unique across all implemented interfaces.
interface Storable
{
public function save(): void;
}
interface Loggable
{
public function log(string $msg): void;
}
abstract class BaseModel implements Storable, Loggable
{
// bodies omitted—permitted because class is abstract
}
class User extends BaseModel
{
public function save(): void { /* ... */ }
public function log(string $msg): void { /* ... */ }
}
Interfaces can extend one or more other interfaces via extends, but they cannot implement another interface or extend a class.
interface Resizable
{
public function scale(float $factor): void;
}
interface DrawableAndResizable extends Drawable, Resizable
{
public function rotate(float $degrees): void;
}
Key Differences
| Aspect | Abstract Class | Interface |
|---|---|---|
| Inheritance keyword | extends |
implements |
| Multiple inheritance | No—only one abstract class | Yes—multiple interfaces |
| Properties | Allowed (instance & static) | Only constants (const) |
| Constructor | Allowed | Not allowed |
| Method visibility | public, protected, private |
Always public |
| Method implementation | Can mix abstract and concrete | All methods are abstract (PHP 8+ allows default via traits) |
| Instantiation | Never | Never |