0

Inheritance — extends, method overriding, parent::

Beginner5 min read·php-07-006
interviewcompare

Concept

Inheritance lets a class extend another, inheriting all public and protected methods and properties. PHP supports single inheritance only — a class can only extend one parent. (Multiple interface implementation is available, and traits provide horizontal code reuse.)

extends: The child class gains all public/protected members of the parent. Private members are inherited in memory but inaccessible from the child. The child can add new methods/properties and override inherited ones.

Method overriding: Redefining a parent method in the child replaces it. The override must be compatible — in PHP 8, the signature must be a subtype (covariant return type, contravariant parameter types). Calling parent::methodName() invokes the parent implementation from within the override.

Constructor inheritance: Constructors are NOT automatically called — if the child defines __construct, it must explicitly call parent::__construct() if needed. If the child doesn't define __construct, the parent's is inherited.

When inheritance is appropriate: When there is a genuine "is-a" relationship (a Dog IS an Animal). When you want to provide a base implementation with hooks for subclasses to customize (Template Method pattern). When you need to add behavior to existing classes you own.

When inheritance is abused: Using it purely for code reuse without an "is-a" relationship. Prefer composition — a UserService that HAS a Logger is cleaner than a UserService that IS a Logger.

Code Example

php
<?php
declare(strict_types=1);

class Animal
{
    public function __construct(
        protected string $name,
        protected int $age
    ) {}

    public function speak(): string
    {
        return "..."; // base implementation
    }

    public function describe(): string
    {
        return "{$this->name} says: " . $this->speak();
    }

    protected function makeSound(string $sound): string
    {
        return $sound;
    }
}

class Dog extends Animal
{
    public function __construct(string $name, int $age, private string $breed)
    {
        parent::__construct($name, $age); // MUST call parent constructor
    }

    // Override speak() — polymorphism
    public function speak(): string
    {
        return $this->makeSound("Woof!"); // can call parent's protected method
    }

    public function getBreed(): string { return $this->breed; }
}

class Cat extends Animal
{
    public function speak(): string { return "Meow!"; }
}

$animals = [new Dog('Rex', 3, 'German Shepherd'), new Cat('Whiskers', 5)];
foreach ($animals as $animal) {
    echo $animal->describe() . "\n"; // polymorphic dispatch
}
// "Rex says: Woof!"
// "Whiskers says: Meow!"

// Checking hierarchy
$dog = new Dog('Rex', 3, 'GSD');
var_dump($dog instanceof Dog);    // true
var_dump($dog instanceof Animal); // true — inheritance chain

// parent:: in override
class LoggingDog extends Dog
{
    public function speak(): string
    {
        $sound = parent::speak(); // calls Dog::speak
        error_log("{$this->name} spoke: $sound");
        return $sound;
    }
}

// final class — cannot be extended
final class ImmutablePoint
{
    public function __construct(
        public readonly float $x,
        public readonly float $y
    ) {}
}
// class MyPoint extends ImmutablePoint {} // Fatal Error