Skip to content
Theory · Inheritance and interfaces · lesson 1 of 8

Inheritance and parent::

What you'll learn

What you'll learn:

  • extend a class with extends, override its methods and call the parent's version through parent::;
  • describe a shared contract with an abstract class or an interface and check it with instanceof;
  • plug ready-made methods into a class with a trait and know whose method wins when the names match;
  • declare enum enumerations and get a case from a string with from and tryFrom.
Two house blueprints, the second redraws only the kitchen, an arrow extends points to the main one

In the previous theme, a class was a house blueprint and objects were the houses built from it. Now picture a whole family of blueprints. An architect does not redraw a house with a different kitchen from scratch: they take the existing blueprint and write "same as the main one, but a different kitchen".

The new blueprint keeps only the differences and takes the rest from the main one. If someone fixes the main blueprint, the fix shows up in the new one at once. That is why PHP has inheritance: a class receives all the properties and methods of another class and may change some of them.

Without inheritance you would copy the same methods into every similar class, and then fix the same bug in every copy.

extends: everything from the parent

The class you inherit from is called the parent class, and the class that inherits is the child class. You write the link between them with the word extends:

PHP
<?php

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

    public function speak(): string
    {
        return $this->name . ': hum';
    }
}

class Cat extends Animal {}
echo (new Cat('Tom'))->speak(), "\n"; // Tom: hum

Let's walk through it step by step:

  1. Animal is an ordinary class: it has a constructor that promotes the property $name, and a method speak().
  2. class Cat extends Animal {} declares a child class with an empty body. Cat has no code of its own, yet it can already do everything Animal can.
  3. new Cat('Tom') calls the constructor Cat received from its parent, and $name gets 'Tom'.
  4. (new Cat('Tom'))->speak() creates an object and calls a method on it right away. Cat has no speak() method, so PHP takes the one from Animal.

A class in PHP has only one parent: class Cat extends Animal, Pet is a parse error. A parent, on the other hand, may have any number of child classes, and they may have their own.

Overriding and parent::

A child class may declare a method with the same name as the parent's. This is called overriding: objects of the child class use the new version. The parent's version does not disappear, and you can reach it through parent:::

PHP
class Dog extends Animal
{
    public function speak(): string
    {
        return $this->name . ': woof';
    }

    public function greet(): string
    {
        return parent::speak() . ', ' . $this->speak();
    }
}

echo (new Dog('Rex'))->greet(), "\n"; // Rex: hum, Rex: woof

Let's see what happens here:

  1. Dog overrides speak(): the dog answers woof, not hum.
  2. Inside greet(), the call parent::speak() takes the version from Animal and gets 'Rex: hum'.
  3. The call $this->speak() in the same method takes Dog's own version and gets 'Rex: woof'.

Override a method with the same parameters and the same return type as the parent's. A method marked final cannot be overridden: PHP refuses to run such a file and reports a fatal error.

The object picks the method

Say the method talk() is written in Animal and calls $this->speak() inside. Which speak() runs when the object is a Dog?

PHP looks at the class of the object in $this, not at the class where the code is written. So for a Dog object, the code from Animal lands in Dog::speak. Press "Next step" and watch what sits in $this and which speak() opens.

LiveWhose speak does talk callstep 1 of 10 · frames: 1

A plain Animal object with nothing of Dog in it.

Watch what sits in $this and which speak opens

The rule the widget shows:

  • $this inside a method is the object the method was called on, even when the method is written in the parent;
  • $this->speak() looks for the method in that object's class first, and only then in its parents.

Here the blueprint picture falls short. In a real house, a room built from the main blueprint knows nothing about the new one. In PHP, the method talk() from Animal asks the object itself how to speak, and a Dog object answers with its own version.

This behavior is called polymorphism: one call works differently depending on the object's class. Interviewers like to ask: "What does $this->method() in the parent's code call when the object is a child?" The answer: the version from the object's class.

The child's constructor

When a child class has its own constructor, it replaces the parent's completely. PHP does not call the parent's constructor by itself, so the child calls it explicitly, through parent::__construct():

PHP
class Parrot extends Animal
{
    public function __construct(string $name, private string $word)
    {
        parent::__construct($name);
    }

    public function speak(): string
    {
        return $this->name . ': ' . $this->word;
    }
}

echo (new Parrot('Kesha', 'hello'))->speak(), "\n"; // Kesha: hello
  1. The Parrot constructor takes a name and a word. It keeps the word in its own property $word.
  2. It passes the name to parent::__construct($name), and the Animal constructor writes it into $name.
  3. speak() reads both properties and returns 'Kesha: hello'.

protected and private

In Animal, the property $name is declared protected, so the methods of Dog and Parrot can read it. Here is how the three visibility levels behave under inheritance:

  • public is visible everywhere: in the class itself, in child classes and outside;
  • protected is visible in the class itself and in all its child classes, but not outside;
  • private is visible only in the class that declared it.

If $name were private, reading $this->name in a Dog method would give a warning and null instead of the name:

TEXT
Warning: Undefined property: Dog::$name

If the parent's property is private, a child class may declare its own property with the same name: it will be a different property. Interviewers often ask about the difference between private and protected under inheritance too.

Common mistake

The child class got its own constructor, and the call to the parent's constructor was forgotten:

PHP
class Hamster extends Animal
{
    public function __construct(private int $age) {}
}

echo (new Hamster(1))->speak(), "\n"; // ✗
TEXT
Fatal error: Uncaught Error: Typed property Animal::$name must not be accessed before initialization

The Hamster constructor replaced the Animal constructor, and nobody wrote a value into $name. The method speak() reads a property that was never assigned, and PHP stops the script. The fix is to take the name in the constructor and pass it on through parent::__construct($name), as Parrot does.

Key points

  • class Dog extends Animal gives the child class all the parent's properties and methods; a class has one parent.
  • An overriding method works for objects of the child class, and the parent's version is reachable through parent::.
  • $this->method() in the parent's code calls the version from the object's class: that is polymorphism.
  • A child class with its own constructor calls parent::__construct() explicitly; protected is visible in child classes, private is not.