Method Overriding

Yo Yogesh.M Updated 17 Oct 2026
7 min read ·Lesson 68 of 109
In this post

What Is Method Overriding? In simple terms: When a child class inherits a method but needs it to work differently, it can override it: it declares a method with the same name, and that version replace

What Is Method Overriding?

In simple terms: When a child class inherits a method but needs it to work differently, it can override it: it declares a method with the same name, and that version replaces the parent's version for objects of the child class. A sale product inherits everything from a product, but its price() method returns a discounted price.

Technically: A method in a child class with the same name as an inherited public or protected method overrides it. PHP decides at runtime which version to call based on the actual class of the object (dynamic dispatch). The overriding method must have a compatible signature, may not reduce visibility, and can call the original implementation with parent::methodName().


A First Example

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

    public function price(): float
    {
        return $this->basePrice;
    }

    public function label(): string
    {
        return $this->name . ': ₹' . number_format($this->price(), 2);
    }
}

class SaleProduct extends Product
{
    public function __construct(string $name, float $basePrice, private float $discountPercent)
    {
        parent::__construct($name, $basePrice);
    }

    public function price(): float
    {
        return round(parent::price() * (1 - $this->discountPercent / 100), 2);
    }
}

$products = [
    new Product('Gel Pen', 20),
    new SaleProduct('Desk Lamp', 1299, 20),
];

foreach ($products as $product) {
    echo $product->label() . "\n";
}

Output

Gel Pen: ₹20.00
Desk Lamp: ₹1,039.20

How It Works

  1. SaleProduct overrides price(). For a SaleProduct object, every call to price() uses the child's version.
  2. label() is not overridden. It is defined in Product and calls $this->price(). Because $this is a SaleProduct for the lamp, the overridden method runs — the parent's code automatically uses the child's behavior.
  3. parent::price() calls the parent's implementation, so the child extends the original calculation instead of duplicating it.

This combination — a general method in the parent that calls methods the child can override — is a powerful way to customize behavior without copying code.


Extending vs Replacing

An override can replace the parent's behavior completely or extend it by calling parent:::

<?php
class Logger
{
    public function log(string $message): void
    {
        echo "[LOG] $message\n";
    }
}

class TimestampLogger extends Logger
{
    public function log(string $message): void
    {
        parent::log('2026-10-15 10:00 ' . $message);   // extend
    }
}

class SilentLogger extends Logger
{
    public function log(string $message): void
    {
        // replace: do nothing (useful in tests)
    }
}

(new TimestampLogger())->log('Order 501 placed');
(new SilentLogger())->log('This is not printed');
echo "Done\n";

Output

[LOG] 2026-10-15 10:00 Order 501 placed
Done

parent:: can be called anywhere inside the overriding method — before, after, or in the middle of the child's own logic.


Signature Compatibility Rules

An overriding method must be usable wherever the parent method is used. This is the Liskov Substitution Principle: a child object must be able to replace a parent object without breaking the code that uses it. PHP enforces it with these rules:

AspectRuleExample
VisibilitySame or wider (protected → public is allowed; public → protected is not)—
Number of parametersMay add parameters only if they are optionallog(string $m) → log(string $m, array $context = [])
Parameter typesSame or wider (contravariance)int → int|float
Return typeSame or narrower (covariance)object → Product, iterable → array
Static / non-staticMust match—

Example: Covariant Return Types

<?php
class Product
{
    public function __construct(public string $name) {}
}

class ProductRepository
{
    public function find(int $id): ?object
    {
        return null;
    }
}

class CachedProductRepository extends ProductRepository
{
    public function find(int $id): ?Product   // narrower return type: allowed
    {
        return new Product('Gel Pen');
    }
}

echo (new CachedProductRepository())->find(1)?->name . "\n";

Output

Gel Pen

The parent promises "an object or null"; the child promises something more specific — "a Product or null" — which still keeps the parent's promise.

Incompatible Overrides Are Fatal Errors

class Product
{
    public function price(): float { return 20.0; }
}

class BrokenProduct extends Product
{
    public function price(): string { return 'twenty'; }
}
PHP Fatal error:  Declaration of BrokenProduct::price(): string must be compatible with Product::price(): float

Reducing visibility is also rejected:

PHP Fatal error:  Access level to BrokenProduct::price() must be public (as in class Product)

These checks happen when the class is loaded, so incompatible overrides are discovered immediately, not when the method is called.


The #[\Override] Attribute (PHP 8.3+)

A typo in an overriding method's name silently creates a new method instead of overriding the old one. The #[\Override] attribute tells PHP that a method is meant to override a parent method:

<?php
class Report
{
    protected function title(): string
    {
        return 'Report';
    }
}

class SalesReport extends Report
{
    #[\Override]
    protected function titel(): string   // typo
    {
        return 'Sales Report';
    }
}

Output

PHP Fatal error:  SalesReport::titel() has #[\Override] attribute, but no matching parent method exists

Without the attribute, the typo would go unnoticed: title() would still return "Report". The attribute also protects against a parent method being renamed or removed later. It works for methods from parent classes and from implemented interfaces.


Overriding the Constructor

Constructors are an exception to the signature rules: a child constructor may have completely different parameters (as SaleProduct above shows). Remember to call parent::__construct() when the parent needs initialization (see PHP Inheritance).


What Cannot Be Overridden

MemberReason
final methodsExplicitly locked by the parent (see The final Keyword)
private methodsNot visible to the child; a child method with the same name is a separate, unrelated method
<?php
class ParentClass
{
    public function run(): string
    {
        return $this->step();
    }

    private function step(): string
    {
        return 'parent step';
    }
}

class ChildClass extends ParentClass
{
    public function step(): string   // a new method, not an override
    {
        return 'child step';
    }
}

echo (new ChildClass())->run() . "\n";

Output

parent step

run() is defined in ParentClass and calls the private step() of ParentClass. The child's step() does not replace it. Use protected for methods that subclasses are meant to override.


Practical Example: Customizing a Notification Workflow

<?php
class Notification
{
    public function __construct(protected string $recipient, protected string $message) {}

    public function send(): string
    {
        return sprintf('[%s] to %s: %s', $this->channel(), $this->formatRecipient(), $this->formatMessage());
    }

    protected function channel(): string
    {
        return 'EMAIL';
    }

    protected function formatRecipient(): string
    {
        return $this->recipient;
    }

    protected function formatMessage(): string
    {
        return $this->message;
    }
}

class SmsNotification extends Notification
{
    #[\Override]
    protected function channel(): string
    {
        return 'SMS';
    }

    #[\Override]
    protected function formatRecipient(): string
    {
        return '+91 ' . substr($this->recipient, 0, 5) . ' ' . substr($this->recipient, 5);
    }

    #[\Override]
    protected function formatMessage(): string
    {
        $message = parent::formatMessage();
        return mb_strlen($message) > 40 ? mb_substr($message, 0, 37) . '...' : $message;
    }
}

echo (new Notification('asha@example.com', 'Your order #501 has been shipped and will arrive on Friday.'))->send() . "\n";
echo (new SmsNotification('9876543210', 'Your order #501 has been shipped and will arrive on Friday.'))->send() . "\n";

Output

[EMAIL] to asha@example.com: Your order #501 has been shipped and will arrive on Friday.
[SMS] to +91 98765 43210: Your order #501 has been shipped and ...

Step-by-Step Explanation

  1. send() defines the overall format once, in the parent.
  2. The parent exposes three protected extension points: channel(), formatRecipient(), and formatMessage().
  3. SmsNotification overrides all three: a different channel name, phone number formatting, and message shortening for SMS length limits (the first 37 characters plus ...). The shortening extends the parent's formatting via parent::formatMessage().
  4. #[\Override] documents each override and guards against typos.

Common Mistakes

Misspelling the Method Name

Effect: A new method is created; the parent's version still runs.

Fix: Use #[\Override] (PHP 8.3+).

Changing the Signature Incompatibly

Effect: Fatal "must be compatible" errors.

Fix: Keep parameter types the same or wider, return types the same or narrower, and only add optional parameters.

Forgetting to Call the Parent Method

Effect: Important parent behavior (validation, logging, initialization) is lost.

Fix: Call parent::method() when extending rather than replacing.

Trying to Override Private Methods

Effect: The parent keeps calling its own private method.

Fix: Make intended extension points protected.

Overrides That Break Expectations

Effect: A child that throws exceptions where the parent never did, or returns surprising results, breaks code written for the parent.

Fix: Ensure overrides honor the parent method's contract.


Best Practices

  • Override methods to specialize behavior while keeping the parent's contract.
  • Use parent::method() to extend instead of duplicating parent logic.
  • Mark overrides with #[\Override] in PHP 8.3+.
  • Design parent classes with small, protected extension points.
  • Keep signatures compatible: wider parameters, narrower return types.

Official reference: https://www.php.net/manual/en/language.oop5.inheritance.php

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Home PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else Slicing Strings User-defined Functions Slicing Strings PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators
Ask about this post
AI Ask about this post

Ask questions about Method Overriding and get answers drawn from it.

Signed-in readers only.