rfc:dynamic_class_constant_fetch

This is an old revision of the document!


PHP RFC: Dynamic class constant fetch

Proposal

PHP implements various ways of looking up members name.

  1. Variables $$foo
  2. Properties $foo->$bar
  3. Static properties Foo::${$bar}
  4. Methods $foo->{$bar}()
  5. Static methods Foo::{$bar}()
  6. Classes for static properties $foo::$bar
  7. Classes for static methods $foo::bar()

One notable exception are class constants.

class Foo {
    const BAR = 'bar';
}
$bar = 'BAR';
 
// This is currently a syntax error
echo Foo::{$bar}; 
 
// Instead, the `constant` function must be used
echo constant(Foo::class . '::' . $bar);

This limitation seems rather arbitrary. This RFC proposes to introduce the syntax described above.

Semantics

Non-existent class constants

Trying to access a non-existent class constant by name throws a Error. This behavior is equivalent to a normal class constant fetch.

class Foo {}
 
$bar = 'BAR';
echo Foo::{$bar};
// Error: Undefined constant Foo::BAR

{} expression type

The result of the expression in the braces {} must be of type string. If it is not, an Error is thrown.

echo Foo::{[]};
// Error: Illegal offset type

Order of execution

Unfortunately, the order of execution for member lookups is inconsistent.

function test($value) {
    echo $value . "\n";
    return $value;
}
 
class Foo implements ArrayAccess {
    public function __get($property) {
        echo 'Property ' . $property . "\n";
        return $this;
    }
 
    public function __call($method, $arguments) {
        echo 'Method ' . $method . "\n";
        return $this;
    }
 
    public static function __callStatic($method, $arguments) {
        echo 'Static method ' . $method . "\n";
        return static::class;
    }
 
    public function offsetGet($offset): mixed {
        echo 'Offset ' . $offset . "\n";
        return $this;
    }
    public function offsetExists($offset): bool {}
    public function offsetSet($offset, $value): void {}
    public function offsetUnset($offset): void {}
}
 
$foo = new Foo();
 
$foo->{test('foo')}->{test('bar')};
// foo
// bar
// Property foo
// Property bar
 
$foo->{test('foo')}()->{test('bar')}();
// foo
// Method foo
// bar
// Method bar
 
Foo::{test('foo')}()::{test('bar')}();
// foo
// Static method foo
// bar
// Static method bar
 
$foo[test('foo')][test('bar')];
// foo
// bar
// Offset foo
// Offset bar
 
// Can't be demonstrated because there is no __getStatic
// Foo::${test('foo')}::${test('bar')};
// foo
// Static property foo
// bar
// Static property bar
</php>
 
Property and array accesses evaluate all expressions in the chain before performing any of the actual operations. For
constants, order is less relevant because it's very rare for them to be chained. Thus, the simpler approach is chosen,
which is evaluating them in order.
 
<code php>
// Foo::{test('foo')}::{test('bar')};
// foo
// Class constant foo
// bar
// Class constant bar

Magic 'class' constant

For completeness, accessing the magic class constant dynamically is allowed.

namespace Foo;
 
$class = 'class';
echo Bar::{$class};
// Foo\Bar

Enums

The feature works for enum cases as expected.

Future scope

Interaction with ??

This RFC proposes no change in the interaction between class constant fetches and the null-coalescing operator ??. That is, Foo::{$bar} ?? null; will throw an Error if the given constant does not exist. It would be possible to suppress this error as is done for other types of member accesses. However, it's not clear whether this is desirable, especially for explicit class constant fetches. This change can be made in the future with no backwards compatibility break.

Vote

Voting starts ??? and ends ???.

As this is a language change, a 2/3 majority is required.

Add dynamic class constant fetches to PHP 8.3?
Real name Yes No
asgrim (asgrim)  
ashnazg (ashnazg)  
bmajdak (bmajdak)  
crell (crell)  
danack (danack)  
galvao (galvao)  
girgias (girgias)  
ilutov (ilutov)  
nicolasgrekas (nicolasgrekas)  
ocramius (ocramius)  
patrickallaert (patrickallaert)  
ramsey (ramsey)  
santiagolizardo (santiagolizardo)  
seld (seld)  
sergey (sergey)  
svpernova09 (svpernova09)  
twosee (twosee)  
weierophinney (weierophinney)  
wjx (wjx)  
Final result: 15 4
This poll has been closed.
rfc/dynamic_class_constant_fetch.1667571143.txt.gz · Last modified: 2022/11/04 14:12 by ilutov