final is a way of saying: “this is complete. Nobody may change it by inheriting.”
A final class
A final class cannot be extended.
<?php
final class Money
{
public function __construct(public readonly int $cents) {}
}
echo "Money is final";
Money is final
Trying to extend it:
<?php
final class Money {}
class MyMoney extends Money {}
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.
<?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();
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+)
<?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.
<?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();
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.
A good default
final. It is easy to open it later, and hard to take extension away once people rely on it.