Lesson 22 of 100
Classes12 min5 checksPHP 8.5

Inheritance: Extending and Overriding Classes

Learn how a PHP child class inherits behavior from a parent, overrides a method safely, and calls parent::method() to reuse the original logic.

01 · THE IDEAREAD → TRY → LEARN

Why inheritance?

Sometimes two classes share most of their behavior and differ in one detail. Copying the shared code into both classes means every fix has to be made twice. Inheritance lets a child class reuse a parent class and change only what is different.

You declare a child with the extends keyword. The child inherits the public and protected methods, properties and constants of its parent. Unless the child overrides a method, that method keeps its original behavior.

A first example

Example
<?php
class Animal {
    public function __construct(protected string $name) {}
    public function speak(): string { return $this->name . ' makes a sound'; }
    public function name(): string { return $this->name; }
}
class Dog extends Animal {
    public function speak(): string { return $this->name . ' barks'; }
}
$animal = new Animal('Generic');
$dog = new Dog('Rex');
echo json_encode([$animal->speak(), $dog->speak(), $dog->name()], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
["Generic makes a sound","Rex barks","Rex"]

Dog never declares a constructor or a name() method, yet both work, because they are inherited. Dog overrides speak(), so Dog objects use the new version. Because $name is protected, the child can read it directly, while outside code cannot.

Overriding without breaking the contract

An override should behave like a drop-in replacement for the parent method. Callers written against the parent should still work when they receive a child. In practice this means:

  • keep the same parameters, and the same return type or a compatible one;
  • return the same kind of value, with the same meaning;
  • do not make visibility stricter. PHP lets a protected method become public, but a public one cannot become private or protected.

A child often wants to add to the parent's result instead of replacing it. Inside a method, parent::methodName() calls the parent's version.

Example
<?php
class Receipt {
    public function line(int $cents): string { return 'Total: ' . $cents; }
}
class TaxReceipt extends Receipt {
    public function line(int $cents): string {
        return parent::line($cents + intdiv($cents, 10)) . ' incl. tax';
    }
}
echo (new TaxReceipt())->line(250), PHP_EOL;
Output
Total: 275 incl. tax

The child adds ten percent, passes the new amount to the parent, then appends text. It still returns a string and still accepts an integer, so the contract is preserved.

Private methods are not inherited

A child cannot access a parent's private methods. If the child declares a private method with the same name, it is a separate method, not an override. Code inside the parent keeps using the parent's own private method.

Example
<?php
class Base {
    private function secret(): string { return 'base'; }
    public function reveal(): string { return $this->secret(); }
}
class Child extends Base {
    private function secret(): string { return 'child'; }
}
$child = new Child();
echo json_encode([$child->reveal(), $child instanceof Base], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
["base",true]

The result is "base" because reveal() is defined in Base, so it sees Base::secret(). Also notice that $child instanceof Base is true: a child object is also a parent object.

Other rules to remember

  • Unless autoloading is used, declare the parent class before the child.
  • You cannot override a normal read-write property with a readonly one, or the other way around.
  • Constructors are an exception to the visibility rule: a child may make a constructor more restricted.

Common mistakes

  1. Changing the return type or meaning in an override, so callers of the parent break.
  2. Copying the parent's logic into the child instead of calling parent::method().
  3. Expecting a private parent method to be replaced by a same-named child method.
  4. Using private for a property the child needs; use protected when subclasses should read it.

Exercise contract

The starter file defines a Ticket class with price(): int, which returns its base price in cents, and an empty StudentTicket subclass. Override price() in StudentTicket so it applies a 20 percent discount by calling parent::price(). Use integer math, intdiv(parent::price() * 80, 100), so the result rounds down and stays an int. Do not change the ticketPrice(string $type, int $basePrice): int function: it creates a StudentTicket when $type is 'student', otherwise a plain Ticket. Your results are checked on returned integers, including small prices like 1 cent, where rounding matters.

Recap

Use extends to reuse a class and override only what differs. Keep overrides compatible with the parent's contract, call parent::method() to build on existing behavior, and remember that private methods are not part of inheritance.

Before you start

Classes

You’ll practice
  • Extend a base class
  • Override a method while preserving its contract
Your task

Make ticketPrice() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.