During PHP execution, it is possible to reflect on most of the code currently running. For example, it is easy to get the name of a class and using reflection utilities: names of properties, methods, and so on. However, getting the name of a callable passed via (...)
is complicated, even though the engine knows it. Getting the name of a constant not bound to a class or a simple variable, is virtually impossible. This RFC proposes a nameof
operator that makes it relatively easy to get the name of virtually any identifier passed to it.
This can be used in attributes:
#[SerializeWith(nameof(Person->serialize))] class Person { public function serialize() { /* do stuff */ } }
In error messages:
function divide(float $numerator, float $denominator): float { if($denominator === 0) throw new InvalidArgumentException(nameof($denominator) . ' is zero'); }
In match statements:
match($propertyName) { nameof(MyClass->property) => $value, nameof(MyClass->otherProperty) => $otherValue, }
Or anywhere a literal string can be used:
$arr[nameof(myfunc(...))] = true;
As named parameters become more prevalent, providing the end user with the name of the parameter is more important than ever, in a way that it is easy to locate when refactoring, especially as part of a deprecation strategy:
function myAwesomeFunc(mixed $param1 = null, int $param2 = 0) { // potentially many lines later... if(is_called_with_deprecated_value($oldParam)) { $logger->warn(nameof($oldParam) . ' is deprecated in the next version, please use ' . nameof($newParam); } }
Here are some further examples showing how the identifier is transformed:
echo nameof($variable); // variable echo nameof(MyClass->property); // property echo nameof(MyClass::staticProperty); // staticProperty echo nameof(Enum::Case); // Case echo nameof(Object::Const); // Const echo nameof(myFunction(...)); // myFunction echo nameof(\MyNamespace\myFunction(...)); // \MyNamespace\myFunction echo nameof(MY_CONSTANT); // MY_CONSTANT
It's important to note that some things have an ambiguous name that is a compile error:
echo nameof($a[$b]); // is it a or b? (can call nameof($a) or nameof($b) instead) echo nameof($a === $b); // ?? echo nameof($a($b)); // same as array access echo nameof($$a); // ask for $a or use the value of $a echo nameof($a->prop); // what is $a?
These will generate a compile error: “Cannot get the name of an ambiguous expression.”
Note that only the right-most part of the identifier is translated to a string. This is because this is almost always what you want. For example, in the case of nameof(MyClass->property)
, you are most likely not interested in the name of MyClass
(otherwise, you'd ask for the MyClass::class
), but you are interested in the name of property
because that is what you asked for. The key difference between writing “property”
and nameof(MyClass->property)
comes down to humans reading and modifying the code. It is much safer to refactor code when you know the pedigree of a string that should also be refactored.
To continue the “property”
example, it isn't clear what object “property” comes from without digging into the code, perhaps requiring debugging running code. When it is clear that it comes from MyClass
, you can know from whence it came. You can also grep the codebase for “MyClass->property” and locate all usages.
A nameof
operator will be introduced into PHP. During compilation, it will validate that all parts of the identifier exist, and if they do not, a warning will be emitted. The right-most identifier will be turned into a string literal.
There is a special case for objects:
1. A ::
will denote a static property/method.
2. A ->
will denote an instanced property/method
3. The left-hand side must be the type name; it can be an abstract type or an interface.
4. The left-hand side cannot be a variable.
If the first two rules are broken (e.g., a ::
where a ->
should be used), a warning will be emitted: “name of a static property/method is asked for, using instanced syntax.” If the 3rd rule is broken (e.g., a type cannot be found), then the standard error will be emitted for a missing type, and if the 4th rule is broken, a compilation error will be emitted: “cannot get the name of a variables property/method.” The exceptions for rule 4 are $this
, static::
, and self::
, which may be used on the left-hand side in appropriate contexts.
There are a limited number of expressions that can resolve to a name using nameof()
:
nameof($var)
resolves to 'var'nameof($this->prop)
, nameof(MyClass->prop)
resolves to 'prop'nameof(strlen(...))
resolves to 'strlen'nameof(A::Prop)
resolves to 'Prop'nameof(MY_CONST)
resolves to 'MY_CONST'The name will always be fully qualified, with a beginning slash, if it is a first-class callable or constant and it refers to a globally accessible identifier. For example:
namespace Example; class A { public function doStuff(): void {} } function doStuff(): void {} echo nameof(doStuff(...)); // \Example\doStuff echo nameof(A->doStuff(...)); // doStuff
Additionally, consider traits and aliasing:
trait A { public function example() {} } trait B { public function example() {} } class C { use A, B { A::example insteadof B; B::example as exampleB; } } echo nameof(C->exampleB(...)); // outputs: "exampleB" echo nameof(B->example(...)); // outputs: "example"
This adheres to the basic refactoring safety mentioned earlier, since refactoring B->example()
will show up in C
, which will then be found by looking for C->exampleB
, if it needs to be refactored at all. If nameof(C->exampleB)
returned 'example,' other code may accidentally call C->example()
, which would not be ideal, not to mention counter-intuitive.
There are no backward incompatible changes at this time except that `nameof` will become a reserved word.
This could be expanded in the future to allow classes and types. For example, nameof(int)
or nameof(MyClass)
but can already be supported via the silence operator.
\nameof
becomes a reserved word, emitting a deprecation warning.Should be none
Will existing extensions be affected? No
a new AST node (similar to isset) will need to be handled.
None
PHP will largely be unaffected by this change.
None.
This is a simple yes-or-no vote to include this feature. 2/3 majority required to pass.
experimental implementation: https://github.com/php/php-src/pull/11172/files (to be updated)
After the project is implemented, this section should contain
- internals discussion: https://externals.io/message/120256
Classes need not be supported by nameof()
because there is already an easy way to get the class via ::class
. This would also be ambiguous when a class name matches a constant name.
There were some suggestions to use ::name
instead of nameof
; however, this introduces some ambiguity. Take, for example:
class A { public const name = 'bob'; } function example(): A { return new A(); } echo example()::name; echo A::name;
The author believes name
is a rather popular name for variables and would inevitably confuse things. In the example above, “bob” is output, but it looks like we could be getting the name of example
if we don't look hard enough. Further, it prevents us from getting the names of constants:
const A = 'billy'; class A { public const name = 'bob'; } echo A::name;
Are we getting the string “A” or “bob” in this example? As it is currently, we would output “bob,” and the author feels rather strongly that this is correct. Thus, something like ::name
is rejected.