Skip to content

PHP Tutorial Advanced OOP

Polymorphism

Lesson 218 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

Polymorphism means “many forms”. Different classes respond to the same method call in their own way, and the caller does not need to know which class it has.

A payment example

index.php
PHP
<?php
interface PaymentMethod
{
    public function pay(float $amount): string;
}

class CreditCard implements PaymentMethod
{
    public function pay(float $amount): string
    {
        return "Charged $amount to the credit card";
    }
}

class Upi implements PaymentMethod
{
    public function pay(float $amount): string
    {
        return "Requested $amount through UPI";
    }
}

class Wallet implements PaymentMethod
{
    public function pay(float $amount): string
    {
        return "Deducted $amount from the wallet";
    }
}

function checkout(PaymentMethod $method, float $amount): void
{
    echo $method->pay($amount) . "\n";
}

checkout(new CreditCard(), 499);
checkout(new Upi(), 499);
checkout(new Wallet(), 499);
Output
OUTPUT
Charged 499 to the credit card
Requested 499 through UPI
Deducted 499 from the wallet

checkout() calls pay() on whatever object it receives. Each class does the right thing.

Without polymorphism

The same logic with if or switch on a type:

index.php
PHP
<?php
function checkoutOld(string $type, float $amount): string
{
    if ($type === "card") {
        return "Charged $amount to the credit card";
    } elseif ($type === "upi") {
        return "Requested $amount through UPI";
    } elseif ($type === "wallet") {
        return "Deducted $amount from the wallet";
    }

    throw new InvalidArgumentException("Unknown type");
}

echo checkoutOld("upi", 100);
Output
OUTPUT
Requested 100 through UPI

Every new payment method means editing checkoutOld(), and every function that branches on the type. With polymorphism, you add a new class, and existing code does not change.

With an abstract parent class

It works with inheritance too.

index.php
PHP
<?php
abstract class Employee
{
    public function __construct(protected string $name) {}

    abstract public function pay(): float;

    public function slip(): string
    {
        return $this->name . ": " . number_format($this->pay(), 2);
    }
}

class Salaried extends Employee
{
    public function __construct(string $name, private float $monthly)
    {
        parent::__construct($name);
    }

    public function pay(): float
    {
        return $this->monthly;
    }
}

class Hourly extends Employee
{
    public function __construct(string $name, private float $rate, private int $hours)
    {
        parent::__construct($name);
    }

    public function pay(): float
    {
        return $this->rate * $this->hours;
    }
}

$staff = [new Salaried("Ana", 50000), new Hourly("Ben", 400, 120)];

foreach ($staff as $employee) {
    echo $employee->slip() . "\n";
}
Output
OUTPUT
Ana: 50,000.00
Ben: 48,000.00

The benefit in one line

You can add new kinds of objects without changing the code that uses them.

instanceof is a warning sign

If you find yourself writing if ($x instanceof A) ... elseif ($x instanceof B), the behaviour probably belongs inside the classes, as a method.

lightbulb
Program to an interface
Write functions that ask for the interface (PaymentMethod), not a class (CreditCard). That is what makes the code flexible and easy to test.