skip to content

In PHP, how does a child class override an inherited method, and how does it still run the parent's version with parent::?

level: juniorimportance: must knowfreq 72%

answer

  1. one parent per class
  2. same name, compatible signature
  3. parent:: keeps the same $this
  4. child constructor replaces parent's
  5. private methods are never overridden

basics

~20 s

A PHP class extends exactly one parent and overrides a method by declaring one with the same name and a compatible signature. Inside it, parent::name() runs the inherited body on the same object; a child constructor must call parent::__construct() itself.

solid answer

~40 s

`class SmsNotification extends Notification` inherits every public and protected method, property and constant of one parent; PHP has no multiple class inheritance. Declaring a method with the same name and a compatible signature **overrides** it, so calls on an `SmsNotification` object run the child's body. Inside the override, `parent::render($body)` runs the parent's implementation as an ordinary instance call: `$this` is still the child object, so the parent body sees the child's state and calls the child's other overrides. Constructors follow the same rule: a child that declares `__construct()` replaces the parent's, and the parent's constructor runs only if the child calls `parent::__construct(...)` explicitly. Private parent methods are not inherited for overriding at all: a child method with the same name is a separate method.

code

php · 27 lines
php
<?php
declare(strict_types=1);

class Notification
{
    public function __construct(protected string $recipient) {}

    public function render(string $body): string
    {
        return "To {$this->recipient}: {$body}";
    }
}

final class SmsNotification extends Notification
{
    public function __construct(string $recipient, private int $maxLength = 160)
    {
        parent::__construct($recipient);
    }

    public function render(string $body): string
    {
        return mb_substr(parent::render($body), 0, $this->maxLength);
    }
}

echo (new SmsNotification('+15550100', 20))->render('Your code is 4812');

go deeper

for a junior

Recall that a class extends one parent, overrides by redeclaring the method, and uses parent::method() to reuse the inherited body. Remember that the parent constructor needs an explicit parent::__construct() call.

for a middle

Explain that parent:: is an instance call on the same $this, so the parent body sees child state and child overrides, and why private methods are not overridden.

for a senior

Show you can diagnose an uninitialised typed property back to a missing parent::__construct(), and argue for calling parent methods instead of duplicating their bodies in overrides.

for a principal

Discuss when a deep extends chain with parent:: calls becomes hard to change, and how you would keep extension points explicit with protected hooks instead.

## What extends gives a class In PHP a class names **at most one parent** with `extends`. The child inherits the parent's **public and protected** methods, properties and constants; private members stay the parent's own. There is no `extends A, B`: the grammar accepts a single class name after `extends`, so a comma list is a parse error. Sharing code sideways is the job of traits and interfaces, which are separate topics. ## Overriding a method A child **overrides** an inherited method by declaring a method with the **same name** (method names are case-insensitive in PHP) and a **compatible signature**. From then on, every call on an instance of the child, including calls the parent's own code makes through `$this`, runs the child's version. No keyword is needed; the optional `#[\Override]` attribute only adds a check that a parent method really exists. What "compatible" means is covered by PHP's signature rules (wider parameter types, narrower return types, no extra required parameters, no reduced visibility). If the child breaks them, the class declaration itself fails with `Declaration of ... must be compatible with ...`. ## Running the parent's version with parent:: An override often wants to **extend** the parent's behaviour rather than replace it. `parent::method(...)` calls the implementation defined in the parent class: - It is an **instance call**, not a static one: `$this` inside the parent body is still the child object. - The parent body therefore sees the child's properties, and if it calls `$this->other()`, it reaches the child's override of `other()`. - `parent::` is resolved from the class where the code is **written**, so in a three-level chain `parent::` in the middle class always means the top class. - Using `parent::` in a class that has no parent fails with `Cannot use "parent" when current class scope has no parent`. | Call written in the child | Which body runs | What `$this` is | |---|---|---| | `$this->render($b)` | the child's `render()` | the child object | | `parent::render($b)` | the parent's `render()` | the same child object | ## Constructors are not chained for you PHP applies the override rule to constructors too, and this is where most inheritance bugs start: 1. If the child declares **no** constructor, it inherits the parent's, and `new Child(...)` takes the parent's parameters. 2. If the child **does** declare `__construct()`, the parent's constructor is **not** called implicitly. 3. The child must call `parent::__construct(...)` itself, usually first, passing what the parent needs. Forgetting step 3 leaves the parent's initialisation undone. With typed properties the failure is loud but late: the first read of a property the parent should have set throws `Error: Typed property ... must not be accessed before initialization`. Calling `parent::__construct()` when the parent has no constructor at all throws `Error: Cannot call constructor`. ## Private methods are not overridden A parent's `private` method is invisible to the child. If the child declares a method with the same name, that is a **new, unrelated** method with no signature rules applied. Code in the parent that calls `$this->helper()` on its own private `helper()` still runs the parent's version, because a private method is resolved inside the class that declares it. To let children replace a step, the parent must make it `protected` (or `public`). ## Walking through a notification hierarchy Take a base `Notification` whose `render()` prefixes the recipient, and an `SmsNotification` that must also cut the text to one SMS: 1. `new SmsNotification('+15550100', 20)` runs the **child** constructor, which stores `$maxLength` and then calls `parent::__construct($recipient)` so the parent's promoted property is set. 2. `->render('Your code is 4812')` runs the **child** override, because the object's class is `SmsNotification`. 3. The override calls `parent::render($body)`, which runs the parent body with the same `$this` and returns the prefixed string. 4. The override truncates that string and returns it. An `EmailNotification` sibling could override `render()` differently, wrapping the parent's result in HTML, while code that only knows it holds a `Notification` calls `render()` without caring which subclass it got. That is the payoff of overriding: one call site, several behaviours, and the shared part written once in the parent. ## Common mistakes - Assuming the parent constructor runs automatically, then debugging an uninitialised property far from the cause. - Copy-pasting the parent's body into the override instead of calling `parent::method()`, so a later fix in the parent never reaches the child. - Expecting a same-named method to replace a parent's `private` helper. - Treating `parent::render()` as a static call and expecting it to lose `$this`.

  • What goes wrong if SmsNotification's constructor forgets to call parent::__construct()?
    The parent's constructor never runs, so `$recipient` is never assigned. Because it is a typed property, the first read of it, here inside `render()`, throws `Error: Typed property Notification::$recipient must not be accessed before initialization`. The failure surfaces where the property is used, not where the constructor was written, which is why missing parent calls are slow to diagnose.
  • The parent calls $this->helper() and helper() is private in the parent. The child declares its own helper(). Which one runs?
    The parent's. A private method is not inherited for overriding, so the child's `helper()` is an unrelated method that shares a name. Calls to `$this->helper()` written inside the parent resolve to the parent's private method. If the parent intends children to replace that step, it must declare `helper()` as `protected`.
  • What happens if a child calls parent::__construct() but the parent class declares no constructor?
    PHP throws `Error: Cannot call constructor` at the call. A child that is unsure whether its parent has a constructor should not call it blindly; in practice the parent's API is known, and adding a constructor to the parent later is the change that makes existing child calls valid.

saying these in an interview costs you the question

  • PHP runs the parent constructor automatically before the child constructor
  • A PHP class can extend several parent classes with a comma list
  • parent::render() is a static call, so $this is not available in it
  • A child method with the same name replaces the parent's private method
  • An override needs a keyword, otherwise the parent method still runs