PHP 8.1: final class constants

Version8.1
TypeNew Feature

PHP 8.1 supports the final flag on class constants.

Constants declared on PHP classes can be overridden by sub classes. Prior to PHP 8.1, the final flag was not allowed on class constants, which would have protected class constants from being overridden by sub classes. Attempting to use the final flag on class constants resulted in a fatal error.

In PHP 8.1 and later, the final flag is allowed on class/interface constants, and none of the sub classes are allowed to extend/override final constants.

class Foo {
    final public const TEST = '1';
}
class Bar extends Foo {
    public const TEST = '2';
}
Fatal error: Bar::TEST cannot override final constant Foo::TEST in %s on line %d

final private const

Adding final flag to a private constant is not allowed because private constants/methods/properties cannot be accessed outside the class itself.

Declaring a final private constant results in a fatal error:

class Foo {
    final private const TEST = '1';
}
Fatal error: Private constant Foo::TEST cannot be final as it is never overridden in ... on line ...

Interface constant changes

Prior to PHP 8.1, overriding interface constants was not allowed, and resulted in a fatal error. This behavior has changed in PHP 8.1.

interface I {
    const C = 16;
}

class Cl implements I{
    const C = 1;
}

In the snippet above, I::C constant is overridden by the class Cl. Although I::C constant is not declared final, this was not allowed prior to PHP 8.1:

Fatal error: Cannot inherit previously-inherited or override constant C from interface I in ... on line ... 

With the introduction of final constants in PHP 8.1, classes and interfaces are allowed to override a previously-declared class, unless the constant is declared with a final flag.

In PHP 8.1, Interfaces constants support the final flag as well, and attempting to override results in an error, just like it does when overriding a class constant:

interface I {
    final public const C = 16;
}

class Cl implements I{
    const C = 1;
}
Fatal error: Cl::C cannot override final constant I::C in ... on line ...

The same also applies when an Interface extends another Interface and override a final constant.

Reflection API Changes

ReflectionClassConstant class from the Reflection API has a new method isFinal:

class ReflectionClassConstant {
    // ...
    public function isFinal(): bool {}
}

ReflectionClassConstant::isFinal returns a bool value whether a given class constant is declared final.

class Foo {
    final private const TEST = '1';
}

$reflector = new ReflectionClassConstant(Foo::class, 'TEST');
$reflector->isFinal(); // true

Related Changes

Backwards Compatibility Impact

This is a language-level change, it is not possible to port this feature to older PHP versions. Using final flag on PHP versions older than 8.1 causes a fatal error:

Fatal error: Cannot use 'final' as constant modifier in ... on line ...

Note that the behavior of Interfaces has changed; PHP 8.1 and later allows re-declaring a non-final Interface constant. Existing code that relied on this behavior and assumed that Interface constants will never be overridden might need to add the final flag to the Interface constant to ensure it is not overridden in PHP 8.1 and later versions.


RFC Discussion Implementation