Is there an RFC/discussion for ::class being a specific type?

  116419
November 16, 2021 17:22 andre@webkr.de (=?iso-8859-1?Q?Andr=E9_H=E4nsel?=)
It is common (with DI systems for example) and to my knowledge not
particularly discouraged to have function parameters that are supposed to
accept something like Foo::class, which currently is a string.

It seems logical to ask for a special type that can hold class names, so
that parameters that can accept a class name can be type hinted more
specifically than just (any) "string".

Regardless of whether or not such a proposal would be accepted or declined
(for complexity reasons maybe) I couldn't even find any such proposal. Has
this really never been asked?
  116420
November 16, 2021 17:29 ocramius@gmail.com (Marco Pivetta)
Hey André,


On Tue, Nov 16, 2021 at 6:22 PM André Hänsel <andre@webkr.de> wrote:

> It is common (with DI systems for example) and to my knowledge not > particularly discouraged to have function parameters that are supposed to > accept something like Foo::class, which currently is a string. > > It seems logical to ask for a special type that can hold class names, so > that parameters that can accept a class name can be type hinted more > specifically than just (any) "string". > > Regardless of whether or not such a proposal would be accepted or declined > (for complexity reasons maybe) I couldn't even find any such proposal. Has > this really never been asked? >
In `vimeo/psalm` and `phpstan/phpstan`, there is a pseudo-type called `class-string`. This `class-string` type is kinda problematic though, because `Foo::class` does **not** autoload `Foo` (by design), and therefore a custom runtime type would be a bit too "optimistic" about its values, as it would not verify anything. Greets, Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/
  116421
November 16, 2021 17:34 tekiela246@gmail.com (Kamil Tekiela)
Interesting, but what would such a type actually be? How would PHP check
the type? What would be the rules?

At the moment, ::class is just a preprocessor macro. It is not part of the
runtime. It also doesn't mean the name of the class. It just means "textual
representation of the value on the left". For example, this is valid:

echo "string"::class;

You can use ::class on almost anything that the compiler would treat as a
string literal. In the end, it will be compiled down to a string. It is
handy to think of ::class as ::text or ::name.

I don't think we could enforce class type without actually asking PHP to
check if class exists. You can use static analysers for this (see
class-string), but I doubt it will be possible in PHP.
  116422
November 16, 2021 18:30 andre@webkr.de (=?UTF-8?Q?Andr=C3=A9_H=C3=A4nsel?=)
Maybe I'm missing something but now that you said it's kind of a macro, I think it would actually be pretty easy to implement, at least when not taking backwards compatibility or performance into account:

class ClassName {
  private $name;
  function __construct(string $name) {
    $this->name = $name;
  }
  function__toString()
  {
    return $name;
  }
}

Then make the preprocessor convert Foo::class into `new ClassName(Foo::class)`. Done. Now you can type hint a function like this:

class DiContainer {
  function get(ClassName $klass) {
    // ...
  }
}
  116423
November 16, 2021 18:56 tekiela246@gmail.com (Kamil Tekiela)
Ok, but a popular usage is also with functions. For example, strlen::class.
What should the compiler use in this case?
Replacing a string with an object of a strigable class is not the same. Say
I have code like this:

function callFoo(callable $func) {
    echo $func('bar');
}
callFoo(strlen::class);

This would trigger an error as the ClassName is not an invokable class.
  116425
November 16, 2021 21:18 dusk@woofle.net (Dusk)
On Nov 16, 2021, at 10:56, Kamil Tekiela <tekiela246@gmail.com> wrote:
> Ok, but a popular usage is also with functions. For example, strlen::class. > What should the compiler use in this case?
Popular in what context? I'm not sure this usage is even correct. strlen::class isn't the name of the strlen function; it's the name of the class that you'd be instantiating with "new strlen()", which would be relative to the current namespace. If your code is running in the root namespace, this will happen to resolve to "strlen", but that doesn't mean it's returning a callable.
  116426
November 16, 2021 22:02 tekiela246@gmail.com (Kamil Tekiela)
Hi Dusk,

Perhaps, you misunderstood me. Take a look at the documentation
https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class.class
::class is just a compile time transformation. It will give you the fully
qualified name of *something* as a string literal. The only exception to
this is when using ::class on objects, as this is a runtime transformation,
and when using with keyword static.

When ::class is used with a class name, it will give you the name of that
class as a string. When used with a name of a function, it will give you
the name of that function as a string. When used with a string, it will
give you that string as a string.
See this example https://3v4l.org/EKnEd

The fact that this gives you a callable is just coincidental with all
strings being callable in PHP. The reason why it works right now is
because a string can represent a name of a class, interface, trait or
function.
If we were to replace the transformation with something else, like it is
proposed here, we would have to ensure that it is as versatile as a plain
string. Otherwise we would be losing functionality.
  116429
November 16, 2021 22:46 mel@dafert.at (Mel Dafert)
On 16 November 2021 23:02:59 CET, Kamil Tekiela <tekiela246@gmail.com> wrote:
>Hi Dusk, > >Perhaps, you misunderstood me. Take a look at the documentation >https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class.class >::class is just a compile time transformation. It will give you the fully >qualified name of *something* as a string literal. The only exception to >this is when using ::class on objects, as this is a runtime transformation, >and when using with keyword static. > >When ::class is used with a class name, it will give you the name of that >class as a string. When used with a name of a function, it will give you >the name of that function as a string. When used with a string, it will >give you that string as a string. >See this example https://3v4l.org/EKnEd > >The fact that this gives you a callable is just coincidental with all >strings being callable in PHP. The reason why it works right now is >because a string can represent a name of a class, interface, trait or >function. >If we were to replace the transformation with something else, like it is >proposed here, we would have to ensure that it is as versatile as a plain >string. Otherwise we would be losing functionality.
In my opinion, this is extremely unintuitive and surprising. I would not have expected this to work, and would consider this more of an unintended side effect rather than an actual feature of ::class. In my opinion, this is an argument for ::class producing a ClassName type, and for migrating such code that misuses it for functions to the first-class callable syntax (ie. strlen(...)). It might be even possible to issue a deprecation warning whenever a ClassName is coerced to a callable - I am not sure how feasible it would be to implement this, however.
  116430
November 16, 2021 23:10 dusk@woofle.net (Dusk)
On Nov 16, 2021, at 14:02, Kamil Tekiela <tekiela246@gmail.com> wrote:
> When used with a name of a function, it will give you the name of that function as a string.
Again, this is not true. Names of classes and functions are not resolved the same way. Consider: namespace Some\Namespace; use function Other\func; print func::class; This prints "Some\Namespace\func", but calling func() in the same context will refer either to Other\func() or \func(), depending on which is defined -- never to Some\Namespace\func().
  116424
November 16, 2021 18:57 claude.pache@gmail.com (Claude Pache)
> Le 16 nov. 2021 à 18:34, Kamil Tekiela <tekiela246@gmail.com> a écrit : > > > At the moment, ::class is just a preprocessor macro. It is not part of the > runtime.
This is not true in general. For example `static::class` is not resolvable at compile-time. (In fact, many years ago, when I played with that new feature, I almost filed a bug report saying that `static::class` is not supposed to work, since it is not resolvable at compile-time.) (Additionally, since PHP 8, it is also possible to write `$object::class`, see: https://wiki.php.net/rfc/class_name_literal_on_object <https://wiki.php.net/rfc/class_name_literal_on_object>. But `static::class` worked since 5.5.) —Claude
  116462
November 19, 2021 11:54 guilliam.xavier@gmail.com (Guilliam Xavier)
> > For example, this is valid: > > echo "string"::class; >
What! Why is it allowed when it gives the same as string::class, and $string::class is an error? https://3v4l.org/hfvXm#v8.1rc3 -- Guilliam Xavier
  116463
November 19, 2021 12:27 guilliam.xavier@gmail.com (Guilliam Xavier)
> > For example, this is valid: >> >> echo "string"::class; >> > > What! Why is it allowed when it gives the same as string::class, and > $string::class is an error? https://3v4l.org/hfvXm#v8.1rc3 >
Sorry I forgot the namespace... so it really gives the same as "string". https://3v4l.org/rlZFF#v8.1rc3 Still surprising... maybe possibly useful for dynamically generated code? :/ -- Guilliam Xavier
  116464
November 19, 2021 14:22 php-lists@koalephant.com (Stephen Reay)
> On 17 Nov 2021, at 00:22, André Hänsel <andre@webkr.de> wrote: > > It is common (with DI systems for example) and to my knowledge not > particularly discouraged to have function parameters that are supposed to > accept something like Foo::class, which currently is a string. > > It seems logical to ask for a special type that can hold class names, so > that parameters that can accept a class name can be type hinted more > specifically than just (any) "string". > > Regardless of whether or not such a proposal would be accepted or declined > (for complexity reasons maybe) I couldn't even find any such proposal. Has > this really never been asked? > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > Hi André,
I’ve wondered about this kind of functionality myself, but IMO to be particularly useful it’d need to support the ability to accept a classname that is a subtype of a parent class or interface. I can’t think of too many places where I’d want to know something is a classname reference, and not also want to know that it's a subclass or implementation of something specific. This typeof functionality could support some very expressive solutions, when combined with union types, and anonymous classes. Cheers Stephen