Skip to content

PHP Tutorial Advanced OOP

Final Classes and Methods

Lesson 216 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

final is a way of saying: “this is complete. Nobody may change it by inheriting.”

A final class

A final class cannot be extended.

index.php
PHP
<?php
final class Money
{
    public function __construct(public readonly int $cents) {}
}

echo "Money is final";
Output
OUTPUT
Money is final

Trying to extend it:

index.php
PHP
<?php
final class Money {}

class MyMoney extends Money {}
Output
OUTPUT
Fatal error: Class MyMoney cannot extend final class Money in /path/to/index.php on line 5

A final method

A final method cannot be overridden by a child class, though the class itself can still be extended.

index.php
PHP
<?php
class Payment
{
    final public function process(): string
    {
        return $this->validate() . " then charge";
    }

    protected function validate(): string
    {
        return "basic validation";
    }
}

class CardPayment extends Payment
{
    protected function validate(): string
    {
        return "card validation";
    }
}

echo (new CardPayment())->process();
Output
OUTPUT
card validation then charge

The important method process() keeps its order of steps. The child can only change the validate() step.

Final constants (PHP 8.1+)

index.php
PHP
<?php
class Config
{
    final public const VERSION = "1.0";
}

A child class cannot redefine VERSION.

Why use final

  • Safety. A class that should not be extended is protected from misuse.
  • Clarity. Readers know the behaviour is fixed.
  • Freedom to change. You can change a final class’s internals without worrying about subclasses that depend on them.

When not to use it

In a library that others should extend, final can get in the way. Frameworks leave many classes open on purpose.

A modern habit

Many developers now write final class by default for classes that are not designed for inheritance (value objects, services). They remove final only when a real need to extend appears.

index.php
PHP
<?php
final class OrderTotal
{
    public function __construct(
        private int $subtotalCents,
        private int $taxCents,
    ) {}

    public function cents(): int
    {
        return $this->subtotalCents + $this->taxCents;
    }
}

echo (new OrderTotal(10000, 1800))->cents();
Output
OUTPUT
11800

final and testing

A final class cannot be mocked by inheritance. That is why you test against interfaces, and keep the final class behind one.

lightbulb
A good default
If you are unsure, make a new class final. It is easy to open it later, and hard to take extension away once people rely on it.