100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
PHP

Abstract Classes and Polymorphism

Abstract classes define a partial implementation and a contract that subclasses must complete, enabling polymorphic code that treats different concrete types uniformly.

Object-Oriented PHPIntermediate10 min readJul 9, 2026
Analogies

Abstract Classes and Polymorphism

An abstract class in PHP is a class declared with the abstract keyword that cannot be instantiated directly — it exists solely to be extended. Abstract classes typically mix concrete methods (shared, working logic) with abstract methods (signatures with no body) that force every concrete subclass to supply its own implementation. This combination is powerful: the parent class owns the common workflow and state, while subclasses plug in the parts that genuinely vary. Polymorphism is the payoff — code written against the abstract type can call a method like render() or calculateTotal() on any subclass instance without knowing or caring which concrete class it actually is, because PHP dispatches the call to the correct overriding method at runtime.

🏏

Cricket analogy: Think of a base 'Bowler' blueprint in a coaching manual that fixes the run-up routine (shared) but leaves 'deliverBall()' blank, forcing a pacer like Bumrah and a spinner like Ashwin to each fill in their own action, yet the umpire just calls 'bowl' without caring which.

Declaring an abstract class

Both the class and each abstract method must carry the abstract keyword. An abstract method has no body — just a signature ending in a semicolon — and any class extending the abstract class must implement every abstract method or itself be declared abstract. Abstract classes can have constructors, typed properties, constants, and fully implemented (non-abstract) methods just like ordinary classes.

🏏

Cricket analogy: Like a fielding side that must have every position covered — keeper, slips, gully — before play starts, a PHP subclass must implement every abstract method or itself be declared abstract; meanwhile the team retains its fixed toss rules and kit (constructor, properties, constants).

php
<?php
declare(strict_types=1);

abstract class Shape
{
    public function __construct(protected readonly string $name) {}

    abstract public function area(): float;

    public function describe(): string
    {
        return sprintf('%s has an area of %.2f', $this->name, $this->area());
    }
}

final class Circle extends Shape
{
    public function __construct(private readonly float $radius)
    {
        parent::__construct('Circle');
    }

    public function area(): float
    {
        return M_PI * $this->radius ** 2;
    }
}

final class Rectangle extends Shape
{
    public function __construct(private readonly float $width, private readonly float $height)
    {
        parent::__construct('Rectangle');
    }

    public function area(): float
    {
        return $this->width * $this->height;
    }
}

/** @var Shape[] $shapes */
$shapes = [new Circle(3.0), new Rectangle(4.0, 5.0)];
foreach ($shapes as $shape) {
    echo $shape->describe() . PHP_EOL;
}

Polymorphism in practice

In the example above, describe() is written once on Shape yet automatically works correctly for every subtype, because it calls $this->area(), and PHP resolves that call to whichever area() override belongs to the actual runtime object — this is dynamic dispatch. The calling code in the foreach loop is fully decoupled from the concrete shape types; it only knows it has a Shape. Adding a new shape, say Triangle, requires zero changes to describe() or the loop — this is the open/closed principle in action, and it is the central practical benefit of designing around abstract classes and polymorphism rather than long if/match chains that branch on a type property.

🏏

Cricket analogy: A commentator says 'well bowled' without knowing if it's Bumrah's yorker or Ashwin's carrom ball — the call resolves to whichever bowler is actually bowling, just as describe() calls area() and PHP dispatches to whichever subclass's area() applies; adding a new bowler needs no script changes.

A useful mental model: an abstract class is a template with blanks. The template (concrete methods) is filled in once and reused by everyone; the blanks (abstract methods) are filled in differently by each subclass. Polymorphism is what lets callers use the finished template without knowing which set of blanks was used.

Attempting new Shape('generic') produces a fatal error — 'Cannot instantiate abstract class Shape.' A common mistake is forgetting to implement every abstract method in a subclass, which forces that subclass to also become abstract, silently propagating the restriction further than intended.

Abstract classes versus interfaces

Interfaces (covered in a separate topic) define a pure contract with no shared implementation and support multiple inheritance of type. Abstract classes allow shared state and partially implemented behaviour but, like any PHP class, support only single inheritance. A common and effective design combines both: implement an interface to declare the public contract, and provide an abstract base class implementing that interface with the boilerplate shared logic, letting concrete subclasses extend the abstract class and thereby automatically satisfy the interface.

🏏

Cricket analogy: An ICC playing-contract is a pure list of obligations (like an interface) any board can sign regardless of nationality, while a national academy (like an abstract class) shares training infrastructure but only one academy can raise a player; the best setups have a player sign the contract and train at an academy that already satisfies it.

  • An abstract class cannot be instantiated; it exists only to be extended.
  • Abstract methods declare a signature with no body and force subclasses to implement them.
  • Polymorphism lets calling code invoke a method on a base type reference while PHP dispatches to the correct subclass override at runtime.
  • Abstract classes combine shared concrete logic with subclass-specific abstract methods, avoiding duplicated boilerplate.
  • New subclasses can be added without modifying code that already depends on the abstract type — the open/closed principle.
  • Unlike interfaces, abstract classes can hold state and concrete methods but still only support single inheritance.

Practice what you learned

Was this page helpful?

Topics covered

#PHP#PHPProgrammingStudyNotes#Programming#AbstractClassesAndPolymorphism#Abstract#Classes#Polymorphism#Declaring#OOP#StudyNotes#SkillVeris