Hi internals,
I've opened voting on https://wiki.php.net/rfc/new_in_initializers. Voting
will close on 2021-07-14.
Note that relative to the original RFC, new support is limited to parameter
default values, attribute arguments, static variable initializers and
global constant initializers, and not supported in property initializers
and class constant initializers. The discussion thread
https://externals.io/message/113347 has some extensive information on how
we got here.
Regards,
Nikita
Hi NIkita,
I've opened voting on https://wiki.php.net/rfc/new_in_initializers. Voting
will close on 2021-07-14.
Note that relative to the original RFC, new support is limited to parameter
default values, attribute arguments, static variable initializers and
global constant initializers, and not supported in property initializers
and class constant initializers. The discussion thread
https://externals.io/message/113347 has some extensive information on how
we got here.
I voted yes and I'm happy this will come to PHP.
I realized I still have one concern that I want to share here, related to
attributes:
The RFC breaks the possibility to parse the arguments of an attribute in a
generic and safe way.
What I mean is that right now, attributes can be inspected while the
corresponding classes are not installed, due eg to a missing optional
dependency.
This behavior is what makes attributes truly declarative: one can ignore
what they don't care about. Extra semantics can be carried out by classes
without making the related attributes a mandatory dependency.
I think there is a way to preserve this behavior and that we should look
for it.
If I may propose one: we might add a new
ReflectionAttribute::getUninitializedArguments() method, that would return
the same as ReflectionAttribute::getArguments(), except that it would put a
ReflectionAttribute (or similar) instance in place of objects in the data
structure. As a corollary, we might also want to enforce that only child
classes of the Attribute class can be nested inside another Attribute (at
least if we want to reuse ReflectionAttribute as a placeholder.)
WDYT?
Nicolas
On Tue, Jul 6, 2021 at 9:31 AM Nicolas Grekas nicolas.grekas+php@gmail.com
wrote:
Hi NIkita,
I've opened voting on https://wiki.php.net/rfc/new_in_initializers. Voting
will close on 2021-07-14.
Note that relative to the original RFC, new support is limited to
parameter
default values, attribute arguments, static variable initializers and
global constant initializers, and not supported in property initializers
and class constant initializers. The discussion thread
https://externals.io/message/113347 has some extensive information on
how
we got here.I voted yes and I'm happy this will come to PHP.
I realized I still have one concern that I want to share here, related to
attributes:
The RFC breaks the possibility to parse the arguments of an attribute in a
generic and safe way.
What I mean is that right now, attributes can be inspected while the
corresponding classes are not installed, due eg to a missing optional
dependency.
This is not 100% correct, you can have an attribte #[Foo(Foo::class)] and
then calling ReflectionAttribute::getArguments would also require to
resolve the type Foo. So this is not different than what could happen right
now already.
What you can do is use ReflectionAttribute::getName()
to filter the
attributes you want to work on and only then call getArguments().
This behavior is what makes attributes truly declarative: one can ignore
what they don't care about. Extra semantics can be carried out by classes
without making the related attributes a mandatory dependency.I think there is a way to preserve this behavior and that we should look
for it.If I may propose one: we might add a new
ReflectionAttribute::getUninitializedArguments() method, that would return
the same as ReflectionAttribute::getArguments(), except that it would put a
ReflectionAttribute (or similar) instance in place of objects in the data
structure. As a corollary, we might also want to enforce that only child
classes of the Attribute class can be nested inside another Attribute (at
least if we want to reuse ReflectionAttribute as a placeholder.)
A function like this could return all arguments that are not AST Nodes but
"literals" (instances of scalar / array types).
Foo::class or new Foo() are both AST Node types that are resolved the same
way in getArguments
.
WDYT?
Nicolas
I've opened voting on https://wiki.php.net/rfc/new_in_initializers. Voting
will close on 2021-07-14.
Note that relative to the original RFC, new support is limited to
parameter
default values, attribute arguments, static variable initializers and
global constant initializers, and not supported in property initializers
and class constant initializers. The discussion thread
https://externals.io/message/113347 has some extensive information on
how
we got here.I voted yes and I'm happy this will come to PHP.
I realized I still have one concern that I want to share here, related to
attributes:
The RFC breaks the possibility to parse the arguments of an attribute in a
generic and safe way.
What I mean is that right now, attributes can be inspected while the
corresponding classes are not installed, due eg to a missing optional
dependency.This is not 100% correct, you can have an attribte #[Foo(Foo::class)] and
then calling ReflectionAttribute::getArguments would also require to
resolve the type Foo. So this is not different than what could happen right
now already.
Right. Yet this can be easily worked around by using a string literal, so
at least this issue can be avoided with a CS fixer when it matters.
What you can do is use ReflectionAttribute::getName()
to filter the
attributes you want to work on and only then call getArguments().
Yes, my comment is about nested instances, not the root ones.
#[Any(new Foo, new Bar)] <= Foo and Bar classes have to be defined, but
this should be inspectable without instantiation, like root attributes.
This behavior is what makes attributes truly declarative: one can ignore
what they don't care about. Extra semantics can be carried out by classes
without making the related attributes a mandatory dependency.I think there is a way to preserve this behavior and that we should look
for it.If I may propose one: we might add a new
ReflectionAttribute::getUninitializedArguments() method, that would return
the same as ReflectionAttribute::getArguments(), except that it would put
a
ReflectionAttribute (or similar) instance in place of objects in the data
structure. As a corollary, we might also want to enforce that only child
classes of the Attribute class can be nested inside another Attribute (at
least if we want to reuse ReflectionAttribute as a placeholder.)A function like this could return all arguments that are not AST Nodes but
"literals" (instances of scalar / array types).
Foo::class or new Foo() are both AST Node types that are resolved the same
way ingetArguments
.
I guess so, but I'm not sure to get the exact point you want to make here :)
This is not 100% correct, you can have an attribte #[Foo(Foo::class)] and
then calling ReflectionAttribute::getArguments would also require to
resolve the type Foo. So this is not different than what could happen right
now already.
Despite its name, "::class" doesn't care about class definitions, it
just performs a string substitution based on the "namespace" and "use"
statements in the current file.
In most cases, that happens entirely at compile time, so the following
two source files compile identically:
Short form:
namespace Somebody\Something;
#[ Foo( Foo::class ) ]
class Whatever {}
Expanded form:
namespace Somebody\Something;
#[ \Somebody\Something\Foo( '\Somebody\Something\Foo' ) ]
class Whatever {}
There is no need for the class \Somebody\Something\Foo to actually exist
in either case, the argument is just a string: https://3v4l.org/bgNa2#v8.0.8
Regards,
--
Rowan Tommins
[IMSoP]
On Tue, Jul 6, 2021 at 12:58 PM Rowan Tommins rowan.collins@gmail.com
wrote:
This is not 100% correct, you can have an attribte #[Foo(Foo::class)] and
then calling ReflectionAttribute::getArguments would also require to
resolve the type Foo. So this is not different than what could happen
right
now already.Despite its name, "::class" doesn't care about class definitions, it
just performs a string substitution based on the "namespace" and "use"
statements in the current file.In most cases, that happens entirely at compile time, so the following
two source files compile identically:
Hah, I realized after sending the example was bad :) I should have used an
example using actual constants (vs magic ones):
#[Foo(Foo::BAR)]
This would trigger autoloading and resolving during getArguments()
Short form:
namespace Somebody\Something;
#[ Foo( Foo::class ) ]
class Whatever {}Expanded form:
namespace Somebody\Something;
#[ \Somebody\Something\Foo( '\Somebody\Something\Foo' ) ]
class Whatever {}There is no need for the class \Somebody\Something\Foo to actually exist
in either case, the argument is just a string:
https://3v4l.org/bgNa2#v8.0.8Regards,
--
Rowan Tommins
[IMSoP]--
To unsubscribe, visit: https://www.php.net/unsub.php
This is not 100% correct, you can have an attribte #[Foo(Foo::class)]
and
then calling ReflectionAttribute::getArguments would also require to
resolve the type Foo. So this is not different than what could happen
right
now already.Despite its name, "::class" doesn't care about class definitions, it
just performs a string substitution based on the "namespace" and "use"
statements in the current file.In most cases, that happens entirely at compile time, so the following
two source files compile identically:Hah, I realized after sending the example was bad :) I should have used an
example using actual constants (vs magic ones):#[Foo(Foo::BAR)]
This would trigger autoloading and resolving during getArguments()
Right!
Extending on my proposal, getUninitializedArguments() could return a
ReflectionConstant in place of such values.
On Tue, Jul 6, 2021 at 2:30 PM Nicolas Grekas nicolas.grekas+php@gmail.com
wrote:
This is not 100% correct, you can have an attribte #[Foo(Foo::class)]
andthen calling ReflectionAttribute::getArguments would also require to
resolve the type Foo. So this is not different than what could happen
right
now already.Despite its name, "::class" doesn't care about class definitions, it
just performs a string substitution based on the "namespace" and "use"
statements in the current file.In most cases, that happens entirely at compile time, so the following
two source files compile identically:Hah, I realized after sending the example was bad :) I should have used an
example using actual constants (vs magic ones):#[Foo(Foo::BAR)]
This would trigger autoloading and resolving during getArguments()
Right!
Extending on my proposal, getUninitializedArguments() could return a
ReflectionConstant in place of such values.
This doesn't extend to any more complex scenario: It's not just
#[Foo(A::B)], it could also be #[Foo(A::FLAG_1 | A::FLAG_2)] and so on. The
only way to do this is to go all the way back to Dmitry's attribute
proposal (https://wiki.php.net/rfc/attributes) which allows fetching the
AST of attribute arguments. That could represent arbitrary arguments
without evaluating them. (In fact, that proposal also allowed attribute
arguments that PHP cannot constant-evaluate at all.)
I also think that viewing this as "nested attributes" is not quite the
right way to think about it. Yes, the Assert\All use case is nested
attributes, but that's just a special case. More generally this just allows
you to use an object as an attribute argument, and that object does not
necessarily have to be an attribute itself. To give a silly example, if we
were to write argument and return types as attributes, you could have
something like #[ReturnType(new IntersectionType(Foo::class, Bar::class))],
where IntersectionType is just the representation of a particular type, but
is not (and shouldn't be) an attribute itself.
Regards,
Nikita
Le mar. 6 juil. 2021 à 15:38, Nikita Popov nikita.ppv@gmail.com a écrit :
On Tue, Jul 6, 2021 at 2:30 PM Nicolas Grekas <
nicolas.grekas+php@gmail.com> wrote:This is not 100% correct, you can have an attribte #[Foo(Foo::class)]
andthen calling ReflectionAttribute::getArguments would also require to
resolve the type Foo. So this is not different than what could happen
right
now already.Despite its name, "::class" doesn't care about class definitions, it
just performs a string substitution based on the "namespace" and "use"
statements in the current file.In most cases, that happens entirely at compile time, so the following
two source files compile identically:Hah, I realized after sending the example was bad :) I should have used
an
example using actual constants (vs magic ones):#[Foo(Foo::BAR)]
This would trigger autoloading and resolving during getArguments()
Right!
Extending on my proposal, getUninitializedArguments() could return a
ReflectionConstant in place of such values.This doesn't extend to any more complex scenario: It's not just
#[Foo(A::B)], it could also be #[Foo(A::FLAG_1 | A::FLAG_2)] and so on. The
only way to do this is to go all the way back to Dmitry's attribute
proposal (https://wiki.php.net/rfc/attributes) which allows fetching the
AST of attribute arguments. That could represent arbitrary arguments
without evaluating them. (In fact, that proposal also allowed attribute
arguments that PHP cannot constant-evaluate at all.)I also think that viewing this as "nested attributes" is not quite the
right way to think about it. Yes, the Assert\All use case is nested
attributes, but that's just a special case. More generally this just allows
you to use an object as an attribute argument, and that object does not
necessarily have to be an attribute itself. To give a silly example, if we
were to write argument and return types as attributes, you could have
something like #[ReturnType(new IntersectionType(Foo::class, Bar::class))],
where IntersectionType is just the representation of a particular type, but
is not (and shouldn't be) an attribute itself.
Types are a good example of a structure that the language already knows how
to parse without actually requiring all symbols to be loaded: Foo|Bar
doesn't require having both types loaded to work.
I think we don't need to account for constants in my scenario.
ReflectionAttribute::getUninitializedArguments() would then only replace
objects with uninitialized placeholders that represent their class and
arguments.
That should cover the need.
Hi internals,
I've opened voting on https://wiki.php.net/rfc/new_in_initializers.
Voting will close on 2021-07-14.Note that relative to the original RFC, new support is limited to
parameter default values, attribute arguments, static variable initializers
and global constant initializers, and not supported in property
initializers and class constant initializers. The discussion thread
https://externals.io/message/113347 has some extensive information on how
we got here.
This RFC has been accepted with 43 votes in favor and 2 against.
Regards,
Nikita