[php-src] Issue #11223: Proposal: abstract constants
| From: | Lenald | Date: | Wed, 10 May 2023 17:03:23 +0000 |
| Subject: | [php-src] Issue #11223: Proposal: abstract constants | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-244425@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/11223
Author: Lenald
### Description
### User Story
As a PHP developer, I want to be able to declare abstract constants inside a class so that I can
enforce their implementation in subclasses, either by their own implementation or through interface
implementation. This will allow me to safely and more obvious use late static binding to reference
these constants within the class and ensure their proper implementation in subclasses.
### Behavior
If an abstract constant is not implemented in a subclass, either on its own or through interface
implementation, then the subclass should be declared as abstract. Otherwise, a fatal error should be
thrown during class loading or instance creation, with a message such as “Fatal error: Class
[ClassName] contains N abstract constant(s) and must therefore be declared abstract or implement the
remaining constants ([AbstractConstantName]) in [ClassFilePath] on line [LineNumberOfError]”.
Direct reads of abstract constants should also trigger a fatal error with a message such as “Fatal
error: Direct read of abstract constant [ClassName]::[AbstractConstantName] in [ClassFilePath] on
line [LineNumberOfError]”. When an abstract constant is declared, its value should not be set,
similar to how abstract methods are declared.
### Purposes
This feature would be useful to enforce the implementation of a constant in subclasses, without the
need for workarounds such as declaring this constant in the parent class with a default value and
checking for overrides in the subclass.
### Example Code
```PHP
<?php
abstract class Parent
{
abstract public const SOMETHING;
public function foo(): string
{
echo static::SOMETHING . PHP_EOL;
}
}
class ChildImplementsOnTheirOwn extends Parent
{
public const SOMETHING = 'Implemented by Child class';
}
interface SomethingInterface
{
public const SOMETHING = 'Implemented by Interface';
}
class ChildImplementsByInterface extends Parent implements SomethingInterface
{
}
class NotImplemented
{
}
Parent::SOMETHING; //Fatal: Direct read of abstract
ChildImplementsOnTheirOwn::SOMETHING; //'Implemented by Child class'
(new ChildImplementsOnTheirOwn())->foo(); //'Implemented by Child class'
SomethingInterface::SOMETHING; //'Implemented by Interface'
ChildImplementsByInterface::SOMETHING; //'Implemented by Interface'
(new ChildImplementsByInterface())->foo(); //'Implemented by Interface'
NotImplemented::SOMETHING; //Fatal: Must implement or be declared as abstract
$notImplemented = new NotImplemented(); //Fatal: Must implement or be declared as abstract
(new NotImplemented())->foo(); //Fatal: Must implement or be declared as abstract (must be thrown
already from __construct())
```