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
?>
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.📖 まとめ
extendsinherits all public/protected properties and methods from the parentparent::calls a parent method that has been overriddenfinalprevents a class from being inherited or a method from being overriddeninstanceofchecks if an object is an instance of a given class- Abstract classes: define a "family template," forcing subclasses to implement abstract methods
- Interfaces: define a "capability contract"—a class can implement multiple interfaces
- Polymorphism: operate on different subclass objects through a unified parent/interface type
📝 練習問題
- Create a
Vehicleparent class (brand/year/getInfo), then createCarandMotorcyclechild classes. OverridegetInfo()while usingparent::getInfo()to preserve the parent's output. - Create a
Loggerinterface (log(string $message): void), then implement two classes:FileLogger(writes to a file) andDatabaseLogger(outputs to screen). Use polymorphism to handle logging uniformly. - Create an abstract
Employeeclass (name/salary + abstract methodcalculateBonus()), then implementManagerandDeveloperchild classes, each with their own bonus calculation logic.