The RFC for improving ReflectionType1 is now in voting phase. The voting
window is June 30th through July 8th. I have not finished the patch but
I'll have it done before the end of voting.
The RFC for improving ReflectionType1 is now in voting phase. The voting
window is June 30th through July 8th. I have not finished the patch but
I'll have it done before the end of voting.
Replying on-list as multiple people asked:
I'm voting against this RFC, because it introduced not only the
ReflectionNamedType class (which is reasonable), but also the
ReflectionClassType class (which is not).
My main objection to ReflectionClassType is that it is autoloading
dependent (*). Something like getReturnType() on a class type hint will
either return a ReflectionClassType if the class can be loaded, or a
ReflectionNamedType if it can't. I think this is confusing and I'm sure
that this will lead to broken code: For example, people will try to use
"$type instanceof ReflectionClassType" to check whether something is a
class type hint, while the currently correct way, which works independently
of class loading, is to check isBuiltin() instead.
I don't think that most consumers of ReflectionType are interested in
obtaining a ReflectionClass for the type hint anyway (which is the only
functionality that ReflectionClassType provides), and if necessary this can
be easily done in userland. There is no need to over-complicate the
ReflectionType functionality in this manner, especially with the proposed
semantics.
Nikita
Den 2016-06-30 kl. 23:57, skrev Nikita Popov:
The RFC for improving ReflectionType1 is now in voting phase. The voting
window is June 30th through July 8th. I have not finished the patch but
I'll have it done before the end of voting.Replying on-list as multiple people asked:
I'm voting against this RFC, because it introduced not only the
ReflectionNamedType class (which is reasonable), but also the
ReflectionClassType class (which is not).My main objection to ReflectionClassType is that it is autoloading
dependent (*). Something like getReturnType() on a class type hint will
either return a ReflectionClassType if the class can be loaded, or a
ReflectionNamedType if it can't. I think this is confusing and I'm sure
that this will lead to broken code: For example, people will try to use
"$type instanceof ReflectionClassType" to check whether something is a
class type hint, while the currently correct way, which works independently
of class loading, is to check isBuiltin() instead.I don't think that most consumers of ReflectionType are interested in
obtaining a ReflectionClass for the type hint anyway (which is the only
functionality that ReflectionClassType provides), and if necessary this can
be easily done in userland. There is no need to over-complicate the
ReflectionType functionality in this manner, especially with the proposed
semantics.Nikita
Maybe one should split the vote into separate for each function.
I mean pity if vote fails because one function is not attractive while
the other one is...
What do you think?
Regards //Björn Larsson
On Mon, Jul 4, 2016 at 10:51 AM, Björn Larsson
bjorn.x.larsson@telia.com wrote:
Den 2016-06-30 kl. 23:57, skrev Nikita Popov:
The RFC for improving ReflectionType1 is now in voting phase. The
voting
window is June 30th through July 8th. I have not finished the patch but
I'll have it done before the end of voting.Replying on-list as multiple people asked:
I'm voting against this RFC, because it introduced not only the
ReflectionNamedType class (which is reasonable), but also the
ReflectionClassType class (which is not).My main objection to ReflectionClassType is that it is autoloading
dependent (*). Something like getReturnType() on a class type hint will
either return a ReflectionClassType if the class can be loaded, or a
ReflectionNamedType if it can't. I think this is confusing and I'm sure
that this will lead to broken code: For example, people will try to use
"$type instanceof ReflectionClassType" to check whether something is a
class type hint, while the currently correct way, which works
independently
of class loading, is to check isBuiltin() instead.I don't think that most consumers of ReflectionType are interested in
obtaining a ReflectionClass for the type hint anyway (which is the only
functionality that ReflectionClassType provides), and if necessary this
can
be easily done in userland. There is no need to over-complicate the
ReflectionType functionality in this manner, especially with the proposed
semantics.Nikita
Maybe one should split the vote into separate for each function.
I mean pity if vote fails because one function is not attractive while
the other one is...What do you think?
Regards //Björn Larsson
I think that it's disappointing that I received no feedback on this
RFC at all and yet have this ratio of votes in the negative.
I'll take this opportunity to share a portion of a conversation I had
with Nikita that demonstrates why ReflectionClassType is actually
useful:
Nikita: But does it really help them? I don't see a lot of differences between these two:
if ($type instanceof ReflectionClassType) {
$r = $type->getClass();
}
// and
if (class_exists($type->getName())) {
$r = new ReflectionClass($type->getName());
}
Levi: ...[I]t will fail for interfaces.] ReflectionClass can be built for those butclass_exists()
will be false.
Nikita: True :)
The (currently) correct code would be:
if (class_exists($type->getName()) || interface_exists($type->getName())) {
$r = new ReflectionClass($type->getName());
}
But if we add some other type such as enums we may have to add more
conditions. As long as ReflectionClass can be made from the type then
doing it in the engine is fully forward compatible.
I hope people who voted no will share why they have done so, but I
also hope they'll switch to yes if they voted no because they don't
see it as being useful.
if ($type instanceof ReflectionClassType) { $r = $type->getClass(); }
The (currently) correct code would be:
if (class_exists($type->getName()) || interface_exists($type->getName())) { $r = new ReflectionClass($type->getName()); }
This seems to be combining two checks into one:
- is this a "simple / builtin type" or a "class / interface type"?
- is the class / interface referenced currently defined or autoloadable?
The first question can be answered with the existing
ReflectionType::isBuiltin() method. The fact that the second is
currently difficult to answer doesn't seem to have anything to do with
reflecting type hints.
It seems like if there was a use case for "is this type hint a currently
defined or autoloadable class / interface?" it would ideally be written
something like this:
if ( ! $type->isBuiltin() && object_type_exists($type->getName()) )
The term "object type" being bikesheddable as a way of encompassing your
hypothetical enums or other future "class-like" types. Of course, the
fact that "class_exists" and "ReflectionClass" use different definitions
of "class" already is somewhat awkward...
Or if the aim is to simplify the reflection usage, why require the if
statement at all:
try {
$r = $type->getReflectionClass();
} catch ( ReflectionException $e ) {
// type is builtin or refers to an undefined class
}
Regards,
Rowan Collins
[IMSoP]
Or if the aim is to simplify the reflection usage, why require the if
statement at all:try {
$r = $type->getReflectionClass();
} catch ( ReflectionException $e ) {
// type is builtin or refers to an undefined class
}
I don't think this is actually simpler if you expand the comment to
handle both cases:
try {
$r = $type->getReflectionClass();
handle_class($type);
} catch (ReflectionException $e) {
if ($type->isBuiltin()) {
handle_builtin();
} else {
handle_undefined($type);
}
}
Compare that to using only if-else for control flow:
if ($type->isBuiltin()) {
handle_builtin();
} else if ($type instanceof ReflectionClassType) {
handle_class($type);
} else {
handle_undefined($type);
}
I'd much prefer the latter.
Another option is adding a method hasClass()
that would return
true
if a getClass()
call would be considered valid and false
otherwise. To me this doesn't seem as good as subtypes but consider it
better than forcing a caller to handle an exception in a situation
that I don't consider exceptional.
It would have been great if people actually contributed to the
discussion before voting phase, but such is life.
Compare that to using only if-else for control flow:
if ($type->isBuiltin()) { handle_builtin(); } else if ($type instanceof ReflectionClassType) { handle_class($type); } else { handle_undefined($type); }
I'd much prefer the latter.
Yeah, I agree the control flow looks more natural with an elseif there.
But that fits fine with my other suggestion:
if ($type->isBuiltin()) {
handle_builtin();
} else if ( object_type_exists($type->getName()) ) {
handle_class($type);
} else {
handle_undefined($type);
}
As I say, if you dislike the verbosity and future-proofing of
"class_exists() || interface_exists()
", then that's not uniqe to this
situation, but a general lack in the language. Why add methods or whole
types to this one area of reflection rather than providing a more basic
building block?
It would have been great if people actually contributed to the
discussion before voting phase, but such is life.
Yes, for my part, I apologise that I didn't pay any attention to this
RFC previously, and just happened upon this sub-thread. I can understand
your frustration at this all coming so late.
Regards,
Rowan Collins
[IMSoP]
It would have been great if people actually contributed to the
discussion before voting phase, but such is life.Yes, for my part, I apologise that I didn't pay any attention to this
RFC previously, and just happened upon this sub-thread. I can understand
your frustration at this all coming so late.
I agree that it is unfortunate that some of the discussion only happened
after the RFC has been put to voting. However, there have only been 15
days between announcing the RFC and starting the vote (plus an
additional 6 days for a pre RFC announcement), and during this time
several other RFCs were also under discussion or in voting phase, what
made it hard to assess all of them in a timely manner.
--
Christoph M. Becker
It would have been great if people actually contributed to the
discussion before voting phase, but such is life.Yes, for my part, I apologise that I didn't pay any attention to this
RFC previously, and just happened upon this sub-thread. I can understand
your frustration at this all coming so late.I agree that it is unfortunate that some of the discussion only happened
after the RFC has been put to voting. However, there have only been 15
days between announcing the RFC and starting the vote (plus an
additional 6 days for a pre RFC announcement), and during this time
several other RFCs were also under discussion or in voting phase, what
made it hard to assess all of them in a timely manner.
Just to clarify this: all of the discussion happened after the RFC
was put into vote, not just some of it. I've talked to a few people
off list to see why they voted no. There are several different reasons
but nobody bothered to voice any of them until voting. How can we make
better RFCs if people don't discuss until its too late?
On Thu, Jul 7, 2016 at 8:48 AM, Christoph Becker cmbecker69@gmx.de
wrote:It would have been great if people actually contributed to the
discussion before voting phase, but such is life.Yes, for my part, I apologise that I didn't pay any attention to this
RFC previously, and just happened upon this sub-thread. I can understand
your frustration at this all coming so late.I agree that it is unfortunate that some of the discussion only happened
after the RFC has been put to voting. However, there have only been 15
days between announcing the RFC and starting the vote (plus an
additional 6 days for a pre RFC announcement), and during this time
several other RFCs were also under discussion or in voting phase, what
made it hard to assess all of them in a timely manner.Just to clarify this: all of the discussion happened after the RFC
was put into vote, not just some of it. I've talked to a few people
off list to see why they voted no. There are several different reasons
but nobody bothered to voice any of them until voting. How can we make
better RFCs if people don't discuss until its too late?
My objections have been known before voting. I agree that it would have
been preferable to raise them on-list as well.
Nikita
On Mon, Jul 4, 2016 at 10:51 AM, Björn Larsson
bjorn.x.larsson@telia.com wrote:Den 2016-06-30 kl. 23:57, skrev Nikita Popov:
The RFC for improving ReflectionType1 is now in voting phase. The
voting
window is June 30th through July 8th. I have not finished the patch but
I'll have it done before the end of voting.Replying on-list as multiple people asked:
I'm voting against this RFC, because it introduced not only the
ReflectionNamedType class (which is reasonable), but also the
ReflectionClassType class (which is not).My main objection to ReflectionClassType is that it is autoloading
dependent (*). Something like getReturnType() on a class type hint will
either return a ReflectionClassType if the class can be loaded, or a
ReflectionNamedType if it can't. I think this is confusing and I'm sure
that this will lead to broken code: For example, people will try to use
"$type instanceof ReflectionClassType" to check whether something is a
class type hint, while the currently correct way, which works
independently
of class loading, is to check isBuiltin() instead.I don't think that most consumers of ReflectionType are interested in
obtaining a ReflectionClass for the type hint anyway (which is the only
functionality that ReflectionClassType provides), and if necessary this
can
be easily done in userland. There is no need to over-complicate the
ReflectionType functionality in this manner, especially with the proposed
semantics.Nikita
Maybe one should split the vote into separate for each function.
I mean pity if vote fails because one function is not attractive while
the other one is...What do you think?
Regards //Björn Larsson
I think that it's disappointing that I received no feedback on this
RFC at all and yet have this ratio of votes in the negative.
I have not yet voted, but I tend to be -1 on this RFC.
I'll take this opportunity to share a portion of a conversation I had
with Nikita that demonstrates why ReflectionClassType is actually
useful:Nikita: But does it really help them? I don't see a lot of differences between these two:
if ($type instanceof ReflectionClassType) {
$r = $type->getClass();
}
// and
if (class_exists($type->getName())) {
$r = new ReflectionClass($type->getName());
}
Levi: ...[I]t will fail for interfaces.] ReflectionClass can be built for those butclass_exists()
will be false.
Nikita: True :)The (currently) correct code would be:
if (class_exists($type->getName()) || interface_exists($type->getName())) { $r = new ReflectionClass($type->getName()); }
ACK. If this code is regarded as too verbose, a function
class_or_interface_exists() (or something like that) can easily be
defined in userland. Anyhow, using (class|interface)_exists() offers
the option to use autoloading or not, contrary to the RFC which always
uses autoloading, what might not be desired.
But if we add some other type such as enums we may have to add more
conditions. As long as ReflectionClass can be made from the type then
doing it in the engine is fully forward compatible.
Iff we add some other type, we can always reconsider to add another
subclass. In my opinion, it doesn't make sense to introduce complexity
(albeit minor in this case) to cater to potential future changes.
Another (minor) issue I have with the RFC is the introduction of
ReflectionNamedType. A simpler alternative appears to be to add the
getName() method to ReflectionType; for unnamed types this could simply
return (string) $reflectionType. That might not be the most farsighted
design decision, though.
--
Christoph M. Becker
Another (minor) issue I have with the RFC is the introduction of
ReflectionNamedType. A simpler alternative appears to be to add the
getName() method to ReflectionType; for unnamed types this could simply
return (string) $reflectionType. That might not be the most farsighted
design decision, though.
The problem is that if something such as union types or intersection
types does pass those types are not named. While the current iteration
of union types failed it is possible a future RFC can pass (for the
record the vote was 11-18).
To add to this, another issue we discussed OTR but didn't really come to a conclusion on, is the handling of self and parent type hints by ReflectionClassType.
The implementation I have will return ReflectionNamedType for self and
parent. There isn't any special casing for it. Rather since we forbid
defining types with these names they won't exist when the check
happens and it will fall back to ReflectionNamedType. As pointed out
these cannot always be resolved because of traits so this behavior
seems sensible. I think the RFC should be amended to specify this
behavior.
On Mon, Jul 4, 2016 at 10:51 AM, Björn Larsson
bjorn.x.larsson@telia.com wrote:Den 2016-06-30 kl. 23:57, skrev Nikita Popov:
The RFC for improving ReflectionType1 is now in voting phase. The
voting
window is June 30th through July 8th. I have not finished the patch but
I'll have it done before the end of voting.Replying on-list as multiple people asked:
I'm voting against this RFC, because it introduced not only the
ReflectionNamedType class (which is reasonable), but also the
ReflectionClassType class (which is not).My main objection to ReflectionClassType is that it is autoloading
dependent (*). Something like getReturnType() on a class type hint will
either return a ReflectionClassType if the class can be loaded, or a
ReflectionNamedType if it can't. I think this is confusing and I'm sure
that this will lead to broken code: For example, people will try to use
"$type instanceof ReflectionClassType" to check whether something is a
class type hint, while the currently correct way, which works
independently
of class loading, is to check isBuiltin() instead.I don't think that most consumers of ReflectionType are interested in
obtaining a ReflectionClass for the type hint anyway (which is the only
functionality that ReflectionClassType provides), and if necessary this
can
be easily done in userland. There is no need to over-complicate the
ReflectionType functionality in this manner, especially with the
proposed
semantics.Nikita
Maybe one should split the vote into separate for each function.
I mean pity if vote fails because one function is not attractive while
the other one is...What do you think?
Regards //Björn Larsson
I think that it's disappointing that I received no feedback on this
RFC at all and yet have this ratio of votes in the negative.I'll take this opportunity to share a portion of a conversation I had
with Nikita that demonstrates why ReflectionClassType is actually
useful:Nikita: But does it really help them? I don't see a lot of differences
between these two:
if ($type instanceof ReflectionClassType) {
$r = $type->getClass();
}
// and
if (class_exists($type->getName())) {
$r = new ReflectionClass($type->getName());
}
Levi: ...[I]t will fail for interfaces.] ReflectionClass can be built
for those butclass_exists()
will be false.
Nikita: True :)The (currently) correct code would be:
if (class_exists($type->getName()) ||
interface_exists($type->getName())) {
$r = new ReflectionClass($type->getName());
}But if we add some other type such as enums we may have to add more
conditions. As long as ReflectionClass can be made from the type then
doing it in the engine is fully forward compatible.I hope people who voted no will share why they have done so, but I
also hope they'll switch to yes if they voted no because they don't
see it as being useful.
To add to this, another issue we discussed OTR but didn't really come to a
conclusion on, is the handling of self and parent type hints by
ReflectionClassType.
On the one hand, ReflectionClassType can be advantageous here, because it
can resolve self/parent itself and return the appropriate ReflectionClass.
Otherwise the user would have to do this themselves (if getting a
ReflectionClass is a goal).
On the other hand, self/parent are not always statically resolvable. For
methods in traits self does not refer to the trait, but rather to the class
the trait will be used in. This means that when reflecting on a self type
on a trait you wouldn't get a ReflectionClassType (for something that is
clearly a class type hint). A similar issue also exists for self/parent on
closures.
Regards,
Nikita
The RFC for improving ReflectionType1 is now in voting phase. The voting
window is June 30th through July 8th. I have not finished the patch but I'll
have it done before the end of voting.
The final vote was 5 in favor and 8 against. This RFC has been rejected.
The final vote was 5 in favor and 8 against. This RFC has been rejected.
While this RFC was rejected, ReflectionType::__toString() should still be updated to include a ? for nullable types. This is a consequence of the nullable types RFC. As mentioned in this RFC [1], the string representation of ReflectionType should be a syntax-valid representation of the type. Without adding ?, this will no longer be true. I do not view this as a BC break. In fact, it is a BC break for PHPUnit, PHPSpec, and Mockery to not make this change, as they currently depend on the string representation of ReflectionType to generate code compatible with the parent class or interface.
Additionally, I propose adding a getName() method to ReflectionType that returns only the name of the type, regardless of nullability. Casting should not be required to get information from an object, but currently this is the only way to get the type name from ReflectionType. Most other reflection classes include a getName() method, this seems to have been an oversight.
Joe and Davey, what are your thoughts on this?
Aaron Piotrowski
[1] https://wiki.php.net/rfc/reflectiontypeimprovements#backward_incompatible_changes
The final vote was 5 in favor and 8 against. This RFC has been rejected.
While this RFC was rejected, ReflectionType::__toString() should still be
updated to include a ? for nullable types. This is a consequence of the
nullable types RFC. As mentioned in this RFC [1], the string representation
of ReflectionType should be a syntax-valid representation of the type.
Without adding ?, this will no longer be true. I do not view this as a BC
break. In fact, it is a BC break for PHPUnit, PHPSpec, and Mockery to not
make this change, as they currently depend on the string representation of
ReflectionType to generate code compatible with the parent class or
interface.Additionally, I propose adding a getName() method to ReflectionType that
returns only the name of the type, regardless of nullability. Casting
should not be required to get information from an object, but currently
this is the only way to get the type name from ReflectionType. Most other
reflection classes include a getName() method, this seems to have been an
oversight.Joe and Davey, what are your thoughts on this?
Aaron Piotrowski
[1]
https://wiki.php.net/rfc/reflectiontypeimprovements#backward_incompatible_changes
I agree with this suggestion.
Nikita
The final vote was 5 in favor and 8 against. This RFC has been rejected.
While this RFC was rejected, ReflectionType::__toString() should still be updated to include a ? for nullable types. This is a consequence of the nullable types RFC. As mentioned in this RFC [1], the string representation of ReflectionType should be a syntax-valid representation of the type. Without adding ?, this will no longer be true. I do not view this as a BC break. In fact, it is a BC break for PHPUnit, PHPSpec, and Mockery to not make this change, as they currently depend on the string representation of ReflectionType to generate code compatible with the parent class or interface.
Additionally, I propose adding a getName() method to ReflectionType that returns only the name of the type, regardless of nullability. Casting should not be required to get information from an object, but currently this is the only way to get the type name from ReflectionType. Most other reflection classes include a getName() method, this seems to have been an oversight.
This wasn't an oversight. If we add union or intersection types then
not all types are named (for instance ArrayAccess & Countable & Traversable
is not a named type). This is why it doesn't exist on the
base ReflectionType.
I have surveyed some of the people who have voted no. Their reasons
vary but based on these conversations it seems to me that by dropping
ReflectionClassType and the associated autoloading mechanism that
overall we'd be happier. I do agree with Aaron that at least some
changes really need to go into 7.1. How do people feel about my
proposal to just drop autoloading and ReflectionClassType
?
Hi Levi,
Additionally, I propose adding a getName() method to ReflectionType that returns only the name of the type, regardless of nullability. Casting should not be required to get information from an object, but currently this is the only way to get the type name from ReflectionType. Most other reflection classes include a getName() method, this seems to have been an oversight.
This wasn't an oversight. If we add union or intersection types then
not all types are named (for instanceArrayAccess & Countable & Traversable
is not a named type). This is why it doesn't exist on the
base ReflectionType.
Good point, then I agree getName() should be in an extending class as in the RFC.
I have surveyed some of the people who have voted no. Their reasons
vary but based on these conversations it seems to me that by dropping
ReflectionClassType and the associated autoloading mechanism that
overall we'd be happier. I do agree with Aaron that at least some
changes really need to go into 7.1. How do people feel about my
proposal to just drop autoloading andReflectionClassType
?
This sounds reasonable to me.
Aaron Piotrowski
Hi Levi,
Additionally, I propose adding a getName() method to ReflectionType that
returns only the name of the type, regardless of nullability. Casting should
not be required to get information from an object, but currently this is the
only way to get the type name from ReflectionType. Most other reflection
classes include a getName() method, this seems to have been an oversight.This wasn't an oversight. If we add union or intersection types then
not all types are named (for instanceArrayAccess & Countable & Traversable
is not a named type). This is why it doesn't exist on the
base ReflectionType.Good point, then I agree getName() should be in an extending class as in the
RFC.I have surveyed some of the people who have voted no. Their reasons
vary but based on these conversations it seems to me that by dropping
ReflectionClassType and the associated autoloading mechanism that
overall we'd be happier. I do agree with Aaron that at least some
changes really need to go into 7.1. How do people feel about my
proposal to just drop autoloading andReflectionClassType
?This sounds reasonable to me.
Aaron Piotrowski
This has been quiet for a few days. I am assuming this means there are
no objections. I will work on updating the RFC and revisit this as
soon as I can.
On Mon, Jul 11, 2016 at 10:30 AM, Aaron Piotrowski aaron@trowski.com
wrote:Hi Levi,
On Sat, Jul 9, 2016 at 8:16 AM, Aaron Piotrowski aaron@trowski.com
wrote:Additionally, I propose adding a getName() method to ReflectionType that
returns only the name of the type, regardless of nullability. Casting
should
not be required to get information from an object, but currently this is
the
only way to get the type name from ReflectionType. Most other reflection
classes include a getName() method, this seems to have been an oversight.This wasn't an oversight. If we add union or intersection types then
not all types are named (for instanceArrayAccess & Countable & Traversable
is not a named type). This is why it doesn't exist on the
base ReflectionType.Good point, then I agree getName() should be in an extending class as in
the
RFC.I have surveyed some of the people who have voted no. Their reasons
vary but based on these conversations it seems to me that by dropping
ReflectionClassType and the associated autoloading mechanism that
overall we'd be happier. I do agree with Aaron that at least some
changes really need to go into 7.1. How do people feel about my
proposal to just drop autoloading andReflectionClassType
?This sounds reasonable to me.
Aaron Piotrowski
This has been quiet for a few days. I am assuming this means there are
no objections. I will work on updating the RFC and revisit this as
soon as I can.
Sadly I wasn't able to get this done in time. If anyone else has time to
add this to 7.1 and the RM's will accept it great; otherwise I'll revisit
this in version 7.2.