final
The "final" keyword is a feature of object-oriented programming in PHP that allows a method, class or property to be marked as final, preventing it from being
The PHP final Keyword
final is an object-oriented keyword in PHP that locks down part of a class so that subclasses cannot change it. You can apply it to a class (no other class may extend it), to a method (no subclass may override it), or, since PHP 8.1, to a constant (no subclass may redefine it).
Marking something final is a deliberate design statement: "this behavior is intentional, and child classes must not alter it." Used well, it prevents accidental overrides, documents intent in code, and lets you reason about a class without worrying that a subclass quietly changed how it works. This page covers each form of final, when to reach for it, and the gotchas that trip people up.
Already comfortable with subclasses and overriding?
finalonly makes sense in the context of PHP inheritance and classes and objects.
Final classes
A final class cannot be extended. Any attempt to declare a subclass produces a fatal error at compile time:
<?php
final class PaymentGateway
{
public function charge(float $amount): string
{
return "Charged $amount";
}
}
// Fatal error: Class CryptoGateway cannot extend final class PaymentGateway
class CryptoGateway extends PaymentGateway {}Use a final class when its behavior is complete and inheritance would be a misuse — for example a value object, a service whose contract you control, or a security-sensitive class where an overriding subclass could bypass a check.
Final methods
You can keep a class extendable but lock down individual methods. A final method can be inherited and called by subclasses, but it cannot be overridden:
<?php
class Account
{
protected float $balance = 0;
// Subclasses may add behavior, but must not change how withdrawing works.
final public function withdraw(float $amount): void
{
if ($amount > $this->balance) {
throw new RuntimeException("Insufficient funds");
}
$this->balance -= $amount;
}
}
class SavingsAccount extends Account
{
public function addInterest(float $rate): void
{
$this->balance += $this->balance * $rate;
}
// Fatal error: Cannot override final method Account::withdraw()
// public function withdraw(float $amount): void {}
}This is the most common use of final: you want a class to be open for extension in general, but a specific method enforces an invariant (here, you can never withdraw more than the balance) that no subclass should be able to weaken.
A private method is effectively final already — it is not visible to subclasses, so marking a private method final is redundant (and PHP 8 emits a warning for final private, except on the constructor).
Final constants (PHP 8.1+)
Before PHP 8.1, a subclass could redefine an inherited constant. PHP 8.1 added final for constants so you can prevent that:
<?php
class HttpStatus
{
final public const OK = 200;
}
class ApiStatus extends HttpStatus
{
// Fatal error: ApiStatus::OK cannot override final constant HttpStatus::OK
// public const OK = 201;
}If you need constants that are part of a fixed contract — status codes, role names, configuration keys — final keeps every subclass honest. Learn more in class constants.
Final properties (PHP 8.4+)
Before PHP 8.4, final could not be applied to a property at all. PHP 8.4 added final properties: a final property can still be read and written, but a subclass may not redeclare it.
<?php
class Car
{
final public string $model = "Toyota";
}
class Toyota extends Car
{
// Fatal error: Cannot override final property Car::$model
// public string $model = "Corolla";
}Note that final controls redeclaration, not mutability — the value can still be reassigned at runtime. To make a value unchangeable after construction, use a readonly property; the two modifiers are often combined (final public readonly string $model). On PHP 8.3 and earlier, marking any property final is a fatal error.
final vs. abstract
final and abstract are opposites and cannot be combined:
abstractmeans must be implemented by a subclass — it requires inheritance.finalmeans cannot be extended or overridden — it forbids inheritance.
Declaring final abstract class is a fatal error. See abstract classes for the other side of this coin.
When to use final
- Lock an invariant. A
finalmethod guarantees critical logic (validation, security checks, billing) can't be silently changed downstream. - Signal "done" classes. A
finalclass tells readers the design is intentional and not meant to be subclassed. - Prefer composition over inheritance. When you'd rather consumers wrap your class than extend it,
finalnudges them that way.
A common counter-argument: final can make code harder to test or mock, since you can't subclass a final class. The modern answer is to depend on interfaces (so you mock the interface, not the concrete class) — see interfaces. Reach for final when the constraint is genuinely part of the design, not as a blanket habit.
Conclusion
The final keyword closes a class, method, or constant to modification by subclasses, turning a design intention into a compiler-enforced rule. Use it on classes that are complete, on methods that protect invariants, and on constants that form a fixed contract — but remember it cannot be applied to properties and cannot be combined with abstract.