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?
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
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.
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) {
// ...
}
}
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.
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.
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.
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/EKnEdThe 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.
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().
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
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
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
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