PHPInheritance

Inheritance

Inheritance lets one class reuse and specialize another class's code instead of duplicating it. A DigitalProduct and a Product share almost everything — a name, a price, a way to describe themselves — but a digital product also needs a download URL and doesn't need shipping weight. Rather than writing DigitalProduct from scratch, PHP lets it extend Product and add only what's different.

The extends keyword

A base Product class

PHP
<?php
class Product {
    public function __construct(
        public string $name,
        public float $price,
    ) {
    }

    public function describe(): string
    {
        return "{$this->name} - \${$this->price}";
    }
}

class DigitalProduct extends Product {
    public function __construct(
        string $name,
        float $price,
        public string $downloadUrl,
    ) {
        parent::__construct($name, $price);
    }
}

$ebook = new DigitalProduct('PHP Handbook', 24.99, 'https://example.test/dl/php-handbook');

echo $ebook->describe(), "\n";
echo $ebook->downloadUrl, "\n";
PHP Handbook - $24.99
https://example.test/dl/php-handbook

DigitalProduct extends Product means every public and protected member of Product — including describe(), which DigitalProduct never redefined — is available on a DigitalProduct instance too. DigitalProduct only had to declare what's genuinely new: $downloadUrl.

Calling the parent constructor

parent::__construct($name, $price) in the example above hands the shared fields back up to Product's own constructor instead of re-implementing that assignment logic. This is the general pattern for parent:: — it calls the parent class's original version of whatever method you're currently inside, using the same method name.

Overriding a method, then calling the original

A subclass can redefine an inherited method entirely — this is called overriding. Often the override doesn't want to replace the parent's behavior outright, just extend it, and parent::methodName() is how you invoke the original implementation from inside the override.

DigitalProduct extends describe() instead of replacing it

PHP
<?php
class DigitalProduct extends Product {
    public function __construct(
        string $name,
        float $price,
        public string $downloadUrl,
    ) {
        parent::__construct($name, $price);
    }

    public function describe(): string
    {
        $base = parent::describe();
        return "{$base} (digital download)";
    }
}

$ebook = new DigitalProduct('PHP Handbook', 24.99, 'https://example.test/dl/php-handbook');

echo $ebook->describe(), "\n";
PHP Handbook - $24.99 (digital download)

parent::describe() runs Product's original method body and returns its string, which the override then appends to. Without parent::describe(), the override would have had to rebuild the name-and-price formatting itself, duplicating logic that already existed in Product.

The is-a test

Inheritance is easy to reach for and easy to misuse. The question that decides whether it's appropriate is always the same: does the relationship read naturally as "X is a Y"? A DigitalProduct is a Product — it has a name and a price, plus one extra detail. That's a legitimate extends.

Contrast that with a has-a relationship. A ShoppingCart is not a kind of Product — a cart has products inside it. Modeling that with class ShoppingCart extends Product would be wrong even though it compiles fine; the cart would inherit a $price and a describe() that make no sense for something that holds many products. The correct shape is composition: ShoppingCart holds an array of Product objects as a property, rather than extending Product.

Has-a: composition, not inheritance

PHP
<?php
class ShoppingCart {
    /** @var Product[] */
    private array $items = [];

    public function add(Product $product): void
    {
        $this->items[] = $product;
    }

    public function total(): float
    {
        return array_sum(array_map(
            fn (Product $p) => $p->price,
            $this->items,
        ));
    }
}

$cart = new ShoppingCart();
$cart->add(new Product('Keyboard', 49.0));
$cart->add(new DigitalProduct('PHP Handbook', 24.99, 'https://example.test/dl'));

echo $cart->total(), "\n";
73.99
PHP only allows single inheritance
A class can `extends` exactly **one** other class — there is no `class Foo extends Bar, Baz`. If you find yourself wanting a class to inherit behavior from two unrelated sources, that's a sign single-class inheritance is the wrong tool, not a limitation to work around by forcing an artificial parent-child relationship.
What PHP offers instead of multiple inheritance

PHP deliberately keeps class inheritance single-parent, but it gives you two other mechanisms for composing behavior from multiple sources without the ambiguity that multiple inheritance can cause. Interfaces let a class promise it implements several different contracts at once (a class can implements many interfaces, even though it can extends only one class). Traits let you literally mix reusable chunks of method implementation into a class from more than one source. Both are covered in their own pages — for now, the important takeaway is that "I need behavior from two places" is solved in PHP by interfaces or traits, not by extending two classes.

  • class Child extends Parent gives Child every public/protected member of Parent automatically.

  • parent::__construct(...) and parent::methodName() call the parent's original implementation from inside a subclass.

  • Use inheritance only for genuine "is-a" relationships; model "has-a" relationships with composition instead.

  • A PHP class can extend only one parent class — use interfaces or traits when you need to combine multiple behaviors.

Tip
Before writing `extends`, say the relationship out loud: "a DigitalProduct is a Product." If the sentence sounds forced, or if you're only inheriting to reuse one method rather than because the types are truly related, composition (a has-a property) will age better than inheritance as the codebase grows.