Le mar. 6 juil. 2021 à 15:38, Nikita Popov <nikita....@gmail.com> a écrit :
> On Tue, Jul 6, 2021 at 2:30 PM Nicolas Grekas < > nicolas.grekas+...@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() >>> >> >> >> 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.