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

- Thread: https://externals.io/message/116419
- Original archive: https://news-web.php.net/php.internals/116419
- Started by andre@webkr.de on 2021-11-16T17:22:01+00:00
- Messages: 13
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #116419 — andre@webkr.de — 2021-11-16T17:22:01+00:00

URL: https://externals.io/message/116419#116419

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 — Marco Pivetta — 2021-11-16T17:29:06+00:00

URL: https://externals.io/message/116419#116420 · In reply to: #116419

Hey André,




> 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 — Kamil Tekiela — 2021-11-16T17:34:14+00:00

URL: https://externals.io/message/116419#116421 · In reply to: #116419

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 — andre@webkr.de — 2021-11-16T18:30:56+00:00

URL: https://externals.io/message/116419#116422 · In reply to: #116421

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 — Kamil Tekiela — 2021-11-16T18:56:56+00:00

URL: https://externals.io/message/116419#116423 · In reply to: #116422

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.

---

## #116424 — Claude Pache — 2021-11-16T18:57:05+00:00

URL: https://externals.io/message/116419#116424 · In reply to: #116421

> 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

---

## #116425 — Dusk — 2021-11-16T21:18:51+00:00

URL: https://externals.io/message/116419#116425 · In reply to: #116423

> 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 — Kamil Tekiela — 2021-11-16T22:02:59+00:00

URL: https://externals.io/message/116419#116426 · In reply to: #116425

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 — Mel Dafert — 2021-11-16T22:46:52+00:00

URL: https://externals.io/message/116419#116429 · In reply to: #116426

>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 — Dusk — 2021-11-16T23:10:42+00:00

URL: https://externals.io/message/116419#116430 · In reply to: #116426

> 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().

---

## #116462 — Guilliam Xavier — 2021-11-19T11:54:37+00:00

URL: https://externals.io/message/116419#116462 · In reply to: #116421

>
> 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 — Guilliam Xavier — 2021-11-19T12:27:59+00:00

URL: https://externals.io/message/116419#116463 · In reply to: #116462

>
> For example, this is valid:
> > […]
>
> 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 — Stephen Reay — 2021-11-19T14:22:40+00:00

URL: https://externals.io/message/116419#116464 · In reply to: #116419

> 
> 
> 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?
> 
> -- 
> 
> 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
