The final Keyword

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

What Does final Mean? In simple terms: final means "this cannot be changed by child classes". A final class cannot be extended at all; a final method cannot be overridden; a final constant cannot be r

What Does final Mean?

In simple terms: final means "this cannot be changed by child classes". A final class cannot be extended at all; a final method cannot be overridden; a final constant cannot be redefined. It is a way of saying: "this part is finished and must work exactly as written".

Technically: The final modifier can be applied to classes, methods, class constants (PHP 8.1+), and properties (PHP 8.4+). PHP checks these restrictions when classes are declared, so any violation is a compile-time fatal error.

UsageEffect
final class MoneyNo class can extends Money
final public function process()Child classes cannot override process()
final public const VERSION = 2;Child classes cannot redefine VERSION (PHP 8.1+)
final public string $nameChild classes cannot redeclare or override the property (PHP 8.4+)

Why Use final?

Inheritance is powerful, but it also allows child classes to change behavior in ways the original author never intended. final protects:

  • Invariants — rules that must always hold (for example, that a Money object is never negative).
  • Security-relevant logic — validation or authorization steps that must not be skipped.
  • Design intent — classes not designed for extension, whose internals may change at any time.

Final Classes

<?php
final class EmailAddress
{
    private string $value;

    public function __construct(string $email)
    {
        $email = strtolower(trim($email));

        if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
            throw new InvalidArgumentException("Invalid email address: $email");
        }

        $this->value = $email;
    }

    public function value(): string
    {
        return $this->value;
    }

    public function domain(): string
    {
        return substr($this->value, strrpos($this->value, '@') + 1);
    }
}

$email = new EmailAddress('  Asha@GoCourse.in ');

echo $email->value() . "\n";
echo $email->domain() . "\n";

Output

asha@gocourse.in
gocourse.in

Trying to extend it fails as soon as the child class is declared:

class LooseEmailAddress extends EmailAddress {}
PHP Fatal error:  Class LooseEmailAddress cannot extend final class EmailAddress

Why This Class Should Be Final

EmailAddress guarantees that every instance contains a valid, normalized address. A subclass could override value() to return something else, or skip the constructor's validation by defining its own constructor — and since a subclass object is also an EmailAddress, every function accepting EmailAddress could receive invalid data. Making the class final keeps the guarantee absolute.

Typical Candidates for Final Classes

Kind of ClassReason
Value objects (Money, EmailAddress, DateRange)Their validation and equality rules must not change
Services implementing an interfaceExtension happens by writing another implementation, not by subclassing
Data transfer objectsSimple data carriers with no reason to be extended
Internal classes of a libraryTheir internals can change without breaking users

Many modern projects and coding standards declare classes final by default and remove the keyword only when extension is deliberately designed. Composition and interfaces then cover most needs that inheritance would otherwise address.


Final Methods

A final method can be inherited and used, but not overridden. This is useful when a parent class defines a fixed procedure that calls customizable steps — the Template Method pattern:

<?php
abstract class PaymentProcessor
{
    final public function process(int $amountPaise, int $orderId): string
    {
        if ($amountPaise <= 0) {
            throw new InvalidArgumentException('Amount must be positive.');
        }

        $reference = $this->charge($amountPaise, $orderId);

        return sprintf('Charged ₹%s via %s (ref %s)', number_format($amountPaise / 100, 2), $this->name(), $reference);
    }

    abstract protected function charge(int $amountPaise, int $orderId): string;

    abstract protected function name(): string;
}

final class UpiProcessor extends PaymentProcessor
{
    protected function charge(int $amountPaise, int $orderId): string
    {
        return "UPI-$orderId";
    }

    protected function name(): string
    {
        return 'UPI';
    }
}

$upi = new UpiProcessor();
echo $upi->process(129900, 501) . "\n";

try {
    $upi->process(0, 502);
} catch (InvalidArgumentException $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

Output

Charged ₹1,299.00 via UPI (ref UPI-501)
Error: Amount must be positive.

Why process() Is Final

  • process() contains the validation that every payment must pass. If a subclass could override it, a careless implementation might skip the amount check.
  • Subclasses customize only the steps they are meant to: charge() and name().
  • Overriding process() is rejected:
PHP Fatal error:  Cannot override final method PaymentProcessor::process()

UpiProcessor itself is also final: it is a complete implementation, not a base for further classes. (Abstract classes are covered in PHP Abstract Classes.)


Final Constants (PHP 8.1+)

<?php
class ApiClient
{
    final public const VERSION = 'v2';
    public const TIMEOUT = 10;
}

class SlowApiClient extends ApiClient
{
    public const TIMEOUT = 30;          // allowed: not final
    // public const VERSION = 'v3';     // fatal error
}

echo SlowApiClient::VERSION . ' / ' . SlowApiClient::TIMEOUT . "\n";

Output

v2 / 30

Redefining VERSION in the child would cause:

PHP Fatal error:  SlowApiClient::VERSION cannot override final constant ApiClient::VERSION

Constants declared in interfaces can also be overridden by implementing classes since PHP 8.1, unless they are marked final.


Things to Know

Private Methods and final

A private method is never overridden (child classes cannot see it), so final private makes no sense. PHP 8 emits a warning: "Private methods cannot be final as they are never overridden by other classes". The only exception is the constructor, where final private function __construct() is allowed.

Enums Are Implicitly Final

Enums cannot be extended (see PHP Enums).

final and Testing

Some mocking tools create test doubles by extending classes, which is impossible for final classes. The recommended solution is to depend on interfaces: the code under test receives an interface, and tests provide a different implementation. This is also better design, which is one reason "final by default" works well in practice.


When Not to Use final

SituationReason
A base class explicitly designed for extensionIts purpose is to be extended (often it is abstract)
Framework classes users are expected to extend (controllers, models)Extension is the intended usage
Public libraries where users rely on extending a classAdding final later is a breaking change

Removing final later is always backward compatible; adding it later breaks every subclass. That asymmetry is another argument for starting with final and opening classes only when needed.


Common Mistakes

Extending a Final Class

Effect: Fatal error when the child class is declared.

Fix: Use composition: wrap the final class in a new class, or implement the same interface.

Making Everything in a Base Class Final

Effect: The base class cannot be customized at all.

Fix: Make the workflow methods final and keep designated extension points protected and overridable (or abstract).

Using final private Methods

Effect: A warning and no effect.

Fix: Remove final from private methods.

Adding final to Published Library Classes Without Notice

Effect: Breaks users' subclasses.

Fix: Announce it as a breaking change in a major version.


Best Practices

  • Declare value objects, DTOs, and concrete service implementations final.
  • Use final methods to protect workflows and validation in base classes.
  • Use final constants for values that must stay the same throughout a class hierarchy.
  • Prefer interfaces and composition for extensibility and testability.
  • Remove final deliberately, only when a class is designed for extension.

Official reference: https://www.php.net/manual/en/language.oop5.final.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 The final Keyword and get answers drawn from it.

Signed-in readers only.