Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

I see this a lot when developers start building larger systems. You want to create a "blueprint" for a set of related classes, so you use an abstract class. But then, you run into a fatal error that stops the whole site from loading. Let's look at a payment system example where this usually happens.

<?php
abstract class PaymentGateway {
    protected $apiKey;

    public function __construct($apiKey) {
        $this->apiKey = $apiKey;
    }

    // Every gateway MUST have a way to process the payment
    abstract public function processPayment($amount);
}

class StripeGateway extends PaymentGateway {
    public function execute($amount) {
        return "Processing \${$amount} via Stripe...";
    }
}

$stripe = new StripeGateway('sk_test_123');
echo $stripe->processPayment(100);
?>

The "Missing Implementation" Crash

If you try to run the code above, PHP is going to throw a Fatal error. It'll tell you that StripeGateway contains one abstract method and must therefore be declared abstract or implement the remaining methods.

Here is the problem: in the PaymentGateway class, I defined abstract public function processPayment($amount). By doing this, I'm making a pinky-promise to PHP that every single class that extends PaymentGateway will have a method called processPayment. However, in StripeGateway, I named the method execute() instead. PHP doesn't care that the logic is there; it only cares that the specific name promised in the abstract class is missing.

Fixing the Contract

To fix this, we have to align the child class with the parent's requirements. We don't change the abstract class—that's our "contract"—we change the implementation.

<?php
abstract class PaymentGateway {
    protected $apiKey;

    public function __construct($apiKey) {
        $this->apiKey = $apiKey;
    }

    abstract public function processPayment($amount);
}

class StripeGateway extends PaymentGateway {
    // Fixed: The method name now matches the abstract definition exactly
    public function processPayment($amount) {
        return "Processing \${$amount} via Stripe using key {$this->apiKey}...";
    }
}

$stripe = new StripeGateway('sk_test_123');
echo $stripe->processPayment(100); // Now it works!
?>

Why we can't just use a regular class

You might be wondering, "Why not just make PaymentGateway a normal class and leave the method empty?"

I've tried that in the past, and it's a recipe for bugs. If you use a regular class with an empty method, and you (or a teammate) create a new PayPalGateway but forget to write the payment logic, the code will run silently and do absolutely nothing. You won't know it's broken until a customer complains that their payment didn't go through.

By using an abstract class, you are forcing the error to happen during development. PHP refuses to even compile the code if the child class is missing a required method. It's a safety net that ensures your application remains consistent as it grows.

Preventing Direct Instantiation

Another huge benefit is that you can't do this: $gateway = new PaymentGateway('key');.

Since PaymentGateway is abstract, PHP prevents you from creating an object of that type. This makes sense—what would a "generic" payment gateway even do? It doesn't have a specific API to talk to. Abstract classes are meant to be inherited, not instantiated. I use them whenever I want to share some common logic (like the apiKey property and constructor) while still forcing child classes to handle the specific "how-to" of a process.




📋 Practical Task

Building a Multi-Format File Exporter

You are building a system that needs to export data in different formats (JSON and XML). To ensure every exporter follows the same rules, you need to use an abstract class.

Your Requirements:

  • Create an abstract class called FileExporter.
  • The FileExporter should have a protected property $filename and a constructor to set it.
  • The FileExporter must define an abstract method called export(array $data).
  • Create a concrete class JsonExporter that extends FileExporter and implements the export method to return the data as a JSON string (use json_encode) prefixed with "JSON Export to [filename]: ".
  • Create a concrete class XmlExporter that extends FileExporter and implements the export method to return a simple string simulating XML (e.g., "<xml>...</xml>") prefixed with "XML Export to [filename]: ".

Testing your code: Instantiate both exporters with different filenames and a sample array of data, then echo the results of their export() methods.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.