Object-Oriented Programming (OOP) in PHP is a robust paradigm that allows developers to model real-world scenarios and relationships through classes, inheritance, encapsulation, polymorphism, and abstraction. One of the most fundamental features of OOP is the concept of abstract classes.
In this blog, we’ll dive into the concept of abstract classes in PHP, focusing on the most recent PHP syntax and features available in PHP 8.3. We’ll cover what abstract classes are, why and how they are used, and when to prefer them over interfaces or regular classes.
What is an Abstract Class?
An abstract class in PHP is a special type of class that cannot be instantiated on its own. It is designed to be extended by other classes, and it serves as a blueprint for its subclasses. An abstract class can define methods (both abstract and non-abstract) that must or can be implemented in child classes.
Key Characteristics of Abstract Classes:
- Abstract classes cannot be instantiated directly.
- They can have both abstract methods (which must be implemented by child classes) and concrete methods (which have implementations).
- Child classes that inherit from an abstract class must implement all abstract methods unless they are also declared abstract.
Why Use Abstract Classes?
Abstract classes provide a way to create a base class that defines common behavior for its subclasses while enforcing the implementation of certain methods. This ensures consistency across related classes while allowing some flexibility in how subclasses implement the details.
How to Define and Use Abstract Classes in PHP
Basic Syntax
In PHP, abstract classes are defined using the abstract
keyword. Here's a basic example to demonstrate the concept
<?php
abstract class Animal
{
// Abstract method - must be implemented in child classes
abstract public function makeSound(): string;
// Concrete method - can be inherited as-is or overridden
public function move(): string
{
return "The animal moves.";
}
}
class Dog extends Animal
{
// Implement the abstract method
public function makeSound(): string
{
return "Bark!";
}
}
class Cat extends Animal
{
// Implement the abstract method
public function makeSound(): string
{
return "Meow!";
}
}
$dog = new Dog();
echo $dog->makeSound(); // Output: Bark!
echo $dog->move(); // Output: The animal moves.
$cat = new Cat();
echo $cat->makeSound(); // Output: Meow!
echo $cat->move(); // Output: The animal moves.
Explanation:
- Abstract Class
Animal
: Defines an abstract methodmakeSound()
and a concrete methodmove()
. - Concrete Classes
Dog
andCat
: Both extendAnimal
and must implement the abstract methodmakeSound()
. They automatically inherit themove()
method fromAnimal
, though they could override it if needed.
Advanced Usage of Abstract Classes
1. Abstract Classes with Properties and Constructors
Abstract classes in PHP can also have properties and constructors, which can be used by child classes. This allows you to define common properties and initialization logic in the base abstract class.
<?php
abstract class Vehicle
{
protected readonly string $brand;
public function __construct(string $brand)
{
$this->brand = $brand;
}
abstract public function startEngine(): string;
public function getBrand(): string
{
return $this->brand;
}
}
class Car extends Vehicle
{
public function startEngine(): string
{
return "The engine of the {$this->brand} car is starting.";
}
}
class Motorcycle extends Vehicle
{
public function startEngine(): string
{
return "The engine of the {$this->brand} motorcycle is starting.";
}
}
$car = new Car("Toyota");
echo $car->startEngine(); // Output: The engine of the Toyota car is starting.
$motorcycle = new Motorcycle("Harley-Davidson");
echo $motorcycle->startEngine(); // Output: The engine of the Harley-Davidson motorcycle is starting.
Explanation:
- Abstract Class
Vehicle
: Declares a protected property$brand
and a constructor to initialize it. The abstract methodstartEngine()
must be implemented by child classes. - Concrete Classes
Car
andMotorcycle
: Both extendVehicle
and provide their own implementations for thestartEngine()
method.
2. Abstract Methods Example
PHP, especially in its latest versions, encourages the use of strict typing and specifying return types. Abstract methods can (and should) define return types to ensure consistency across implementations in child classes.
<?php
abstract class Shape
{
// Abstract method with return type
abstract public function calculateArea(): float;
}
class Circle extends Shape
{
private float $radius;
public function __construct(float $radius)
{
$this->radius = $radius;
}
public function calculateArea(): float
{
return pi() * ($this->radius ** 2);
}
}
class Rectangle extends Shape
{
private float $width;
private float $height;
public function __construct(float $width, float $height)
{
$this->width = $width;
$this->height = $height;
}
public function calculateArea(): float
{
return $this->width * $this->height;
}
}
$circle = new Circle(5);
echo $circle->calculateArea(); // Output: 78.539816339745
$rectangle = new Rectangle(4, 5);
echo $rectangle->calculateArea(); // Output: 20.0
Explanation:
- Abstract Class
Shape
: Defines an abstract methodcalculateArea()
with a return type offloat
. - Concrete Classes
Circle
andRectangle
: Both implement thecalculateArea()
method and provide their own logic for calculating the area.
3. Final Methods in Abstract Classes
In some cases, you may want to provide a method in an abstract class that cannot be overridden by subclasses. You can achieve this by using the final
keyword.
<?php
abstract class PaymentGateway
{
final public function processPayment(float $amount): string
{
return "Processing payment of $" . number_format($amount, 2);
}
abstract public function connect(): void;
}
class PayPalGateway extends PaymentGateway
{
public function connect(): void
{
echo "Connecting to PayPal API...";
}
}
$gateway = new PayPalGateway();
$gateway->connect(); // Output: Connecting to PayPal API...
echo $gateway->processPayment(99.99); // Output: Processing payment of $99.99
Explanation:
- The
processPayment()
method in thePaymentGateway
class is marked asfinal
, meaning subclasses cannot override it. - This ensures that the payment processing logic remains consistent, while allowing flexibility in other methods like
connect()
.
Abstract Classes vs. Interfaces
While abstract classes and interfaces may seem similar at first glance, there are key differences:
- Abstract Classes can have properties, constructors, and methods with implementations. They are useful when you want to provide a default implementation or state management to subclasses.
- Interfaces cannot contain properties or method implementations. They only define method signatures, enforcing contracts that must be followed by implementing classes.
When to use abstract classes:
- You want to share behavior (methods) and properties between related classes.
- You need to provide default functionality but allow subclasses to override certain methods.
When to use interfaces:
- You want to define a contract that any class can implement, regardless of its inheritance hierarchy.
- You want to enforce method signatures without sharing any logic between classes.
Conclusion
Abstract classes in PHP are a vital tool for enforcing consistency and sharing functionality between related classes. By using the latest PHP syntax, including return types and readonly
properties, you can write more predictable and maintainable object-oriented code. While abstract classes and interfaces often complement each other, knowing when to use one over the other is key to designing clean and efficient applications.
As you grow in your understanding of PHP's OOP capabilities, mastering abstract classes will give you greater control and flexibility when building robust, scalable applications.