PHP RFC: Final class constants
- Date: 2021-04-23
- Author: Máté Kocsis kocsismate@php.net
- Status: Implemented
- Implementation: https://github.com/php/php-src/pull/6878
- Target Version: PHP 8.1
Introduction
Currently, class constants are always overridable by child classes. This causes a few smaller caveats:
First of all, the engine can't optimize class constant references when late static binding is involved, so it has to pessimistically assume that FOO
is overridden in case of static::FOO
or $this::FOO
invocations.
What's more important is that class constants are not guaranteed to stay constant. Even if the declaring class always references them by using self::
, doing so won't prevent a child class from changing their value or even their type, unless the parent is a final class. Although constants not being constants is usually considered only as a theoretical problem, being able to add a final
modifier would make the intention explicit that child classes shouldn't try to override them (e.g. because the parent doesn't use late static binding).
A related interesting fact is that interface constants are already final
:
interface I { public const X = "i"; } class C implements I { public const X = "bar"; } // Fatal error: Cannot inherit previously-inherited or override constant X from interface I
This leads to a weird inconsistency. By introducing an intermediate class, overriding still becomes possible:
interface I { public const X = "i"; } class C implements I { } class D extends C { public const X = "d"; } // No error
Proposal
The final modifier can be added to class constants. Doing so prevents overriding of a constant:
class Foo { final public const X = "foo"; } class Bar extends Foo { public const X = "bar"; } // Fatal error: Bar::X cannot override final constant Foo::X
Besides, interface constants would become overridable by default, and the final
modifier could be used to retain the original behavior.
interface I { public const X = "i"; final public const Y = "i"; } class C implements I { public const X = "c"; // Overriding I::X is possible public const Y = "c"; // Overriding I::Y is not possible } // Fatal error: C::Y cannot override final constant I::Y
Reflection
A ReflectionClassConstant::isFinal()
method is added in order to be able to retrieve if a constant is final.
Backward Incompatible Changes
None.
Vote
Voting started on 2021-05-19 08:00 UTC and ends 2021-06-02 08:00 UTC. The vote requires 2/3 majority to be accepted.