Class name resolution for \array::class

php.internals

Marcos Passos

7 years ago
Currently, class name resolution supports all types except an array. https://3v4l.org/OXFMW In fact, it is syntactically invalid since the parser fails to recognize `array::class` as an array expression: Parse error: syntax error, unexpected 'array' (T_ARRAY), expecting
> identifier (T_STRING) > https://3v4l.org/5IOoI
Is possible to change the grammar to support array also? - Marcos

Marco Pivetta

7 years ago
Since `array` is not a class, it doesn't make sense to resolve its type. What's the use-case for such pseudo-constant in your code? Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/ On Mon, Oct 1, 2018 at 1:30 AM Marcos Passos <marcospassos.com@gmail.com> wrote:

Rowan Collins

7 years ago
On Mon, 1 Oct 2018 at 00:30, Marcos Passos <marcospassos.com@gmail.com> wrote:
> Currently, class name resolution supports all types except an array. > https://3v4l.org/OXFMW >
It seems to me that the bug is allowing an expression like "int::class" to resolve, when "int" is no longer a legal class name. I suspect the reason "array::class" doesn't work is that it was already a reserved word in PHP 5, whereas other types were legal class names. In PHP 5, "int::class" can plausibly resolve to the name of an actual class, but in PHP 7, it can't, but clearly this part of the grammar wasn't updated to reflect that. The parser is being too forgiving here, I think, and should complain if the word before :: is not a valid class name. Regards,
-- Rowan Collins [IMSoP]

Marcos Passos

7 years ago
Rowan, thank you for sharing your thoughts. You can also see it as a language construct that expects a type at the left-hand side of the name resolution operator. In that sense, primitive types are perfectly valid. Regards, Marcos Em seg, 1 de out de 2018 às 05:45, Rowan Collins <rowan.collins@gmail.com> escreveu:

Marco Pivetta

7 years ago
They are still not classes On Mon, 1 Oct 2018, 14:24 Marcos Passos, <marcospassos.com@gmail.com> wrote:

Rowan Collins

7 years ago
On Mon, 1 Oct 2018 at 13:24, Marcos Passos <marcospassos.com@gmail.com> wrote:
> > You can also see it as a language construct that expects a type at the > left-hand side of the name resolution operator. In that sense, primitive > types are perfectly valid. >
While I can see that as an interpretation, it doesn't make a lot of sense: - the keyword "class" implies that this will always return a class name; it is the static equivalent of get_class(), which does not support non-objects - if instead it was resolving a "type", you might expect it to return the same values as gettype(), which for any object is simply the string "object" - the main purpose of the feature is to resolve namespace lookups and aliases, but basic types are not subject to namespaces; in other words, "int::class" can only ever return "int" As Marco says, if you have a use case where allowing this is helpful, please share it, and we can discuss if this, or some new related feature (e.g. "array::type"), would be worth supporting. Regards,
-- Rowan Collins [IMSoP]

Marcos Passos

7 years ago
Sorry, it looks like I've replied to Marco only. About use case, we use it to specify types in standardized fashion: if (!$value instanceof Foo) {
> throw InvalidArgumentException::unexpectedType($value, Foo::class); > } > if (!\is_int($value)) { > throw new InvalidArgumentException::unexpectedType($value, > \int::class); > } > > $converter->convert($value, \int::class);
I also said that it might have some benefits for generic types: I don't disagree with you, but it's already supported right now, so what
> I'm only proposing to make it consistent. > Looking forward, supporting name resolution for primitive may have some > benefits for generic types > <https://github.com/PHPGenerics/php-generics-rfc/issues/20#issuecomment-407600337> > .
Beside this, deprecating it now would represent a BC break, right? Em seg, 1 de out de 2018 às 10:15, Rowan Collins <rowan.collins@gmail.com> escreveu:

Rowan Collins

7 years ago
On Mon, 1 Oct 2018 at 14:30, Marcos Passos <marcospassos.com@gmail.com> wrote:
> Sorry, it looks like I've replied to Marco only. > > About use case, we use it to specify types in standardized fashion: > > if (!$value instanceof Foo) { >> throw InvalidArgumentException::unexpectedType($value, Foo::class); >> } >> if (!\is_int($value)) { >> throw new InvalidArgumentException::unexpectedType($value, >> \int::class); >> } >> >> $converter->convert($value, \int::class); > >
This is just obfuscation for no gain, IMO: - \int::class is 6 characters longer than 'int', and guaranteed to have the same value. - It doesn't protect against typos like a constant would, because \itn::class will reliably and silently resolve to 'itn' whether or not such a class exists. - It implies that "int" is a class, which it is not; and that the methods always take a class name, which they do not.
> I also said that it might have some benefits for generic types: > > I don't disagree with you, but it's already supported right now, so what >> I'm only proposing to make it consistent. >> Looking forward, supporting name resolution for primitive may have some >> benefits for generic types >> <https://github.com/PHPGenerics/php-generics-rfc/issues/20#issuecomment-407600337> >> . > >
I note that in that discussion, the suggestion of ::type has already come up, and given its own thread: https://github.com/PHPGenerics/php-generics-rfc/issues/27 In my view, that would make more sense than officially supporting "::class" for things other than classes.
> Beside this, deprecating it now would represent a BC break, right? >
Nit-pick: deprecation is never a BC break; it just implies eventual removal, which is. But yes, outright banning it now would be a BC break. IMO it should have been banned at the same time "int", "string", etc were made illegal class names (in PHP 7.0), but I can see an argument that our next chance is PHP 8.0. I still think removing it is the right thing to do, however; it only works by mistake, as demonstrated by array::class not being supported when the feature was first added. Regards,
-- Rowan Collins [IMSoP]

Marcos Passos

7 years ago
> > - It doesn't protect against typos like a constant would, because > \itn::class will reliably and silently resolve to 'itn' whether or not such > a class exists.
It's a good point, I didn't realized that. Having a ::type pseudo-constant is preferable, indeed. The whole point was about making it consistent before you stated that this use case become valid by mistake. Em seg, 1 de out de 2018 às 10:52, Rowan Collins <rowan.collins@gmail.com> escreveu: