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
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
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
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
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 Parentgives Child every public/protected member of Parent automatically.parent::__construct(...)andparent::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.