PHP: Inheritance and Polymorphism

Inheritance is the heart of OOP—"a student is a type of user," "a cat is a type of animal." Parent classes define common behavior, and child classes extend and specialize it. Polymorphism lets you handle different types of objects through a unified interface.

1. extends — Inheritance

PHP
<?php
// Parent class (base class)
class Animal {
    public function __construct(
        public string $name
    ) {}
    
    public function speak(): string {
        return "{$this->name} makes a sound";
    }
    
    public function sleep(): string {
        return "{$this->name} is sleeping";
    }
}

// Child class inherits from parent
class Dog extends Animal {
    // Override the parent method
    public function speak(): string {
        return "{$this->name}: Woof!";
    }
    
    // Add a new method
    public function fetch(): string {
        return "{$this->name} went to fetch the ball";
    }
}

class Cat extends Animal {
    public function speak(): string {
        return "{$this->name}: Meow~";
    }
}

$dog = new Dog("Buddy");
echo $dog->speak();   // Buddy: Woof! (overridden method)
echo $dog->sleep();   // Buddy is sleeping (inherited method)
echo $dog->fetch();   // Buddy went to fetch the ball (new method)

$cat = new Cat("Luna");
echo $cat->speak();   // Luna: Meow~ (overridden method)
echo $cat->sleep();   // Luna is sleeping (inherited method)
?>
💡 Tip: PHP uses single inheritance—a class can only extends one parent class. Multi-inheritance capabilities are achieved through traits and interfaces (covered in upcoming lessons).


2. parent — Calling Parent Methods

When a child class overrides a method but you still want the parent's original logic, use parent:::

PHP
<?php
class Vehicle {
    public function __construct(
        public string $brand,
        public int $year
    ) {}
    
    public function getInfo(): string {
        return "{$this->year} {$this->brand}";
    }
}

class ElectricCar extends Vehicle {
    public function __construct(
        string $brand,
        int $year,
        public int $batteryRange  // Extra property for electric cars
    ) {
        // Call the parent constructor
        parent::__construct($brand, $year);
    }
    
    // Override and extend the parent method
    public function getInfo(): string {
        return parent::getInfo() . " Electric (range: {$this->batteryRange}km)";
    }
}

$car = new ElectricCar("Tesla", 2026, 600);
echo $car->getInfo();  // 2026 Tesla Electric (range: 600km)
?>

3. final — Prevent Inheritance/Overriding

PHP
<?php
class Template {
    // Algorithm skeleton—child classes must not modify it
    final public function render(): string {
        $html = $this->header();
        $html .= $this->body();
        $html .= $this->footer();
        return $html;
    }
    
    protected function header(): string {
        return "<header>Default Header</header>";
    }
    
    protected function body(): string {
        return "<main>Default Content</main>";
    }
    
    protected function footer(): string {
        return "<footer>Default Footer</footer>";
    }
}

// ✅ Child classes can override header/body/footer
class BlogTemplate extends Template {
    protected function header(): string {
        return "<header>Blog Title</header>";
    }
    
    // ❌ But they can't override render()—it's protected by final
    // public function render(): string { ... }
}
?>

4. instanceof — Type Checking

PHP
<?php
function makeSound(Animal $animal): string {
    if ($animal instanceof Dog) {
        return $animal->speak() . " (Good dog!)";
    } elseif ($animal instanceof Cat) {
        return $animal->speak() . " (So cute!)";
    }
    return $animal->speak();
}

echo makeSound(new Dog("Buddy"));  // Buddy: Woof! (Good dog!)
echo makeSound(new Cat("Luna"));   // Luna: Meow~ (So cute!)
?>

5. Abstract Classes

Abstract classes cannot be instantiated directly—their purpose is to define "what a subclass should look like":

▶ Example: Shape Abstract Class Hierarchy

Output:

TEXT 📖 Display only
value
value
PHP
<?php
abstract class Shape {
    public function __construct(
        protected string $color = "Black"
    ) {}
    
    // Abstract methods: subclasses must implement these
    abstract public function area(): float;
    abstract public function perimeter(): float;
    
    // Regular method: subclasses can inherit or override
    public function describe(): string {
        return "A {$this->color} shape, area: " . round($this->area(), 2);
    }
}

class Circle extends Shape {
    public function __construct(
        private float $radius,
        string $color = "Black"
    ) {
        parent::__construct($color);
    }
    
    public function area(): float {
        return pi() * pow($this->radius, 2);
    }
    
    public function perimeter(): float {
        return 2 * pi() * $this->radius;
    }
}

class Rectangle extends Shape {
    public function __construct(
        private float $width,
        private float $height,
        string $color = "Black"
    ) {
        parent::__construct($color);
    }
    
    public function area(): float {
        return $this->width * $this->height;
    }
    
    public function perimeter(): float {
        return 2 * ($this->width + $this->height);
    }
}

// $s = new Shape("Red");  // ❌ Abstract classes can't be instantiated
$c = new Circle(5, "Red");
echo $c->describe();  // A Red shape, area: 78.54

$r = new Rectangle(4, 6, "Blue");
echo $r->describe();  // A Blue shape, area: 24
?>

Output:

TEXT 📖 Display only
A Red shape, area: 78.54
A Blue shape, area: 24

6. Interfaces

An interface defines a contract of "what you can do," without defining "how you do it":

PHP
<?php
interface Payable {
    public function getAmount(): float;
    public function pay(): string;
}

interface Refundable {
    public function refund(): string;
}

// A class can implement multiple interfaces
class CreditCard implements Payable, Refundable {
    public function __construct(
        private string $cardNumber,
        private float $amount
    ) {}
    
    public function getAmount(): float {
        return $this->amount;
    }
    
    public function pay(): string {
        return "Credit card {$this->cardNumber} paid {$this->amount}";
    }
    
    public function refund(): string {
        return "Refunded {$this->amount} to card {$this->cardNumber}";
    }
}

class Crypto implements Payable {
    public function __construct(
        private float $amount
    ) {}
    
    public function getAmount(): float {
        return $this->amount;
    }
    
    public function pay(): string {
        return "Crypto payment of {$this->amount}";
    }
    // Crypto doesn't support refunds—so it doesn't implement Refundable
}
?>

7. Polymorphism in Practice

The core idea of polymorphism: handle different subclass objects through a unified parent/interface type:

▶ Example: Polymorphic Payment System

Output:

TEXT 📖 Display only
[Payment] {value->pay()}<br>
[Refund Support] {value->refund()}<br>
value<br>
Total: {99.99}<br>
PHP
<?php
class PaymentProcessor {
    /**
     * Process payment with polymorphism—works regardless of payment method
     */
    public function process(Payable $payment): void {
        echo "[Payment] {$payment->pay()}<br>";
        
        // Some payment methods support refunds
        if ($payment instanceof Refundable) {
            echo "[Refund Support] {$payment->refund()}<br>";
        }
    }
    
    /**
     * Batch settlement—since all implement Payable, we can handle them uniformly
     */
    public function settle(array $payments): float {
        $total = 0;
        foreach ($payments as $payment) {
            $total += $payment->getAmount();
            echo $payment->pay() . "<br>";
        }
        echo "Total: {$total}<br>";
        return $total;
    }
}

$processor = new PaymentProcessor();

// Polymorphism—the caller doesn't know the specific payment method
$processor->settle([
    new CreditCard("1234-5678", 199.00),
    new Crypto(0.05),
    new CreditCard("8765-4321", 59.99),
]);
?>

Output:

TEXT 📖 Display only
Credit card 1234-5678 paid 199
Crypto payment of 0.05
Credit card 8765-4321 paid 59.99
Total: 259.04
Feature abstract class interface
Can have properties
Can have implemented methods ❌ (PHP 8+ can have default methods)
Can have constants
Multiple inheritance ❌ (single inheritance) ✅ (multiple interfaces)
Used for "is-a" relationships "can-do" capabilities
💡 Tip: Selection guideline: use abstract classes when subclasses have a strong logical connection (Shape→Circle). Use interfaces when unrelated classes need a shared capability (CreditCard/Crypto→Payable).

▶ Example: Method Overriding with parent:: Constructor Chaining

Output:

TEXT 📖 Display only
value
PHP
<?php
class Person {
    public function __construct(
        public string $name,
        public int $age
    ) {}

    public function introduce(): string {
        return "I'm {$this->name}, age {$this->age}";
    }
}

class Student extends Person {
    public function __construct(
        string $name,
        int $age,
        public string $school
    ) {
        parent::__construct($name, $age);
    }

    public function introduce(): string {
        return parent::introduce() . ", studying at {$this->school}";
    }
}

$student = new Student("Alice", 20, "MIT");
echo $student->introduce();  // I'm Alice, age 20, studying at MIT
?>

Output:

TEXT 📖 Display only
I'm Alice, age 20, studying at MIT

❓ FAQ

Q When should I use abstract class vs. interface?
A Use an abstract class when subclasses share properties or logic (e.g., Shape has a color property). Use an interface when completely unrelated classes need a common capability (payment, logging, serialization).
Q Why doesn't PHP support multiple inheritance?
A Multiple inheritance causes the "diamond problem"—when two parent classes have methods with the same name, the child class doesn't know which one to use. PHP replaces multiple inheritance with interfaces + traits.
Q What's the point of final classes and methods?
A To prevent critical logic from being modified. Use final on core code like payment calculations or authentication to ensure subclasses can't override it, guaranteeing consistent behavior.

📖 Summary

📝 Exercises

  1. Create a Vehicle parent class (brand/year/getInfo), then create Car and Motorcycle child classes. Override getInfo() while using parent::getInfo() to preserve the parent's output.
  2. Create a Logger interface (log(string $message): void), then implement two classes: FileLogger (writes to a file) and DatabaseLogger (outputs to screen). Use polymorphism to handle logging uniformly.
  3. Create an abstract Employee class (name/salary + abstract method calculateBonus()), then implement Manager and Developer child classes, each with their own bonus calculation logic.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏