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":

▶ サンプル: Shape Abstract Class Hierarchy

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
?>
論理コード 45 行(40 行制限超過、参照専用)

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:

▶ サンプル: Polymorphic Payment System

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),
]);
?>
▶ 試してみよう
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).

❓ よくある質問

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.

📖 まとめ

📝 練習問題

  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 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%