PHP Abstract Classes

La Lakshmi Priya Updated 19 Oct 2026
6 min read ·Lesson 135 of 143

What Is an Abstract Class?

In simple terms: An abstract class is an incomplete class — a template that defines what its child classes have in common and leaves some parts for them to fill in. You cannot create an object from it directly, just as you cannot buy a generic "vehicle"; you buy a car or a motorcycle. The abstract class describes what all vehicles share; the concrete classes complete the details.

Technically: A class declared with abstract cannot be instantiated. It may contain normal (concrete) methods and properties, plus abstract methods — method signatures without a body. Every non-abstract child class must implement all inherited abstract methods with compatible signatures; otherwise PHP raises a fatal error.

Why Abstract Classes Exist

When several classes share code and structure but differ in specific steps, an abstract class:

  • holds the shared code once (properties, constructor, common methods),
  • forces each child to implement the parts that differ,
  • prevents creating "half-finished" objects of the base class.

Syntax

abstract class BaseClass
{
    // normal properties and methods

    abstract protected function step(string $input): string;   // no body
}

class ConcreteClass extends BaseClass
{
    protected function step(string $input): string
    {
        // implementation
    }
}

A First Example: Shapes

<?php
abstract class Shape
{
    public function __construct(protected string $name) {}

    abstract public function area(): float;

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

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

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

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

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

foreach ([new Circle(1.5), new Rectangle(4, 2.5)] as $shape) {
    echo $shape->describe() . "\n";
}

try {
    $shape = new Shape('Generic');
} catch (Error $e) {
    echo $e->getMessage() . "\n";
}

Output

Circle with area 7.07
Rectangle with area 10.00
Cannot instantiate abstract class Shape

How It Works

  1. Shape declares area() as abstract: every shape has an area, but there is no general formula.
  2. describe() is a concrete method that already uses area(), even though Shape does not implement it. At runtime, the child's implementation is called.
  3. Circle and Rectangle provide the formulas and reuse the shared constructor logic and describe().
  4. new Shape(...) fails: an abstract class is not a complete type and cannot be instantiated.

Rules for Abstract Classes and Methods

RuleError If Violated
Abstract classes cannot be instantiatedCannot instantiate abstract class Shape
Abstract methods have no bodyAbstract function Shape::area() cannot contain body
A class with an abstract method must itself be abstractClass Shape contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Shape::area)
Concrete child classes must implement every abstract methodSame error, naming the missing methods
Implementations must have compatible signatures and equal or wider visibilityDeclaration of ... must be compatible with ...
Abstract methods cannot be privateChild classes could not implement them

Forgetting an Implementation

class Square extends Shape
{
    // area() is missing
}
PHP Fatal error:  Class Square contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Shape::area)

The error appears when the class is declared — not when area() is first called — so missing implementations are caught early.

An abstract class does not need to have abstract methods. Declaring a class abstract without them simply prevents direct instantiation of a base class that is only meant to be extended.


The Template Method Pattern

The most common use of abstract classes is the Template Method pattern: the abstract class implements an algorithm with a fixed sequence of steps, and child classes implement individual steps.

<?php
abstract class Exporter
{
    final public function export(array $rows): string
    {
        if ($rows === []) {
            throw new InvalidArgumentException('Nothing to export.');
        }

        $output = $this->header(array_keys($rows[0]));

        foreach ($rows as $row) {
            $output .= $this->row(array_values($row));
        }

        return $output . $this->footer(count($rows));
    }

    abstract protected function header(array $columns): string;

    abstract protected function row(array $values): string;

    protected function footer(int $rowCount): string
    {
        return '';   // optional step: no footer by default
    }
}

final class CsvExporter extends Exporter
{
    protected function header(array $columns): string
    {
        return implode(',', $columns) . "\n";
    }

    protected function row(array $values): string
    {
        return implode(',', $values) . "\n";
    }
}

final class MarkdownExporter extends Exporter
{
    protected function header(array $columns): string
    {
        return '| ' . implode(' | ', $columns) . " |\n"
            . '|' . str_repeat(' --- |', count($columns)) . "\n";
    }

    protected function row(array $values): string
    {
        return '| ' . implode(' | ', $values) . " |\n";
    }

    protected function footer(int $rowCount): string
    {
        return "\n_$rowCount rows_\n";
    }
}

$rows = [
    ['sku' => 'PEN-01', 'price' => 20],
    ['sku' => 'NB-A5', 'price' => 60],
];

echo (new CsvExporter())->export($rows);
echo "\n";
echo (new MarkdownExporter())->export($rows);

Output

sku,price
PEN-01,20
NB-A5,60

| sku | price |
| --- | --- |
| PEN-01 | 20 |
| NB-A5 | 60 |

_2 rows_

Step-by-Step Explanation

  1. export() is the template method: it validates the input and defines the order header → rows → footer. It is final, so no exporter can skip the validation or change the sequence.
  2. header() and row() are abstract: every format must define them.
  3. footer() is a concrete method with an empty default — an optional hook that child classes may override, as MarkdownExporter does.
  4. Adding a new format (HTML, TSV) requires only a new subclass with two or three small methods.

Abstract Classes vs Interfaces

Abstract ClassInterface
Contains implemented methodsYesNo (only signatures, plus constants and — in PHP 8.4 — property declarations)
Properties and constructorYesNo regular properties; no constructor logic
A class can extend / implementOnly one abstract classMany interfaces
PurposeShare code and structure among closely related classesDefine a contract that unrelated classes can fulfill

They are often combined: an interface defines the contract (Exporter), and an optional abstract base class implementing it provides shared code (AbstractExporter). Code that uses exporters depends on the interface, so it also accepts implementations that do not extend the base class (see PHP Interfaces).

When to Choose an Abstract Class

  • Several classes share substantial implementation (not just method names).
  • The shared code needs state (properties) or a common constructor.
  • You want to enforce a workflow (template method) while letting subclasses fill in steps.

Common Mistakes

Trying to Instantiate the Abstract Class

Effect: Cannot instantiate abstract class.

Fix: Instantiate a concrete subclass.

Leaving Abstract Methods Unimplemented

Effect: Fatal error when the child class is declared.

Fix: Implement all abstract methods, or declare the child abstract too.

Using Abstract Classes Only to Define a Contract

Effect: Implementations are forced into a single inheritance chain.

Fix: Use an interface when no shared code exists.

Large "God" Base Classes

Effect: Child classes inherit many unrelated methods and become tightly coupled to the base.

Fix: Keep abstract classes focused; move unrelated helpers into separate collaborating classes.


Best Practices

  • Use abstract classes to share code among closely related classes and to enforce required methods.
  • Make workflow methods final and abstract steps protected (Template Method pattern).
  • Provide optional hooks as concrete methods with sensible defaults.
  • Pair abstract base classes with interfaces so code depends on the contract, not the base class.
  • Keep inheritance hierarchies shallow.

Official reference: https://www.php.net/manual/en/language.oop5.abstract.php

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Home PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else Slicing Strings User-defined Functions Slicing Strings PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators AJAX PHP AJAX Database AJAX XML AJAX Live Search AJAX Poll AJAX with JSON Security Introduction Password Hashing SQL Injection Prevention XSS Prevention Garbage Collection Project: Simple Blog PHP What is OOP PHP Classes/Objects The $this Keyword Access Array Items Update Array Items Add Array Items Remove Array Items Sorting Arrays Multidimensional Arrays Array Destructuring Array Spread Operator Search in Arrays Merge Arrays PHP Abstract Classes PHP Interfaces Polymorphism PHP Traits PHP Static Methods PHP Static Properties Late Static Binding Magic Methods Object Cloning
Ask about this post
AI Ask about this post

Ask questions about PHP Abstract Classes and get answers drawn from it.

Signed-in readers only.