On Mon, Sep 27, 2021 at 5:58 PM Andreas Hennings <[email protected]>
wrote:

> Hello list,
>
> currently, the default mode for attributes is to create a new class.
> For general initializers, with
> https://wiki.php.net/rfc/new_in_initializers we get the option to call
> 'new C()' for parameter default values, attribute arguments, etc.
>
> Personally I find class construction to be limiting, I often like to
> be able to use static factories instead.
> This allows:
> - Alternative "constructors" for the same class.
> - A single constructor can conditionally instantiate different classes.
> - Swap out the class being returned, without changing the factory name
> and signature.
>
> In fact, static factories for initializers were already mentioned in
> "Future Scope" in https://wiki.php.net/rfc/new_in_initializers.
> However this does not mention static factories for the main attribute
> object.
>
> For general initializers this is quite straightforward.
> For attributes, we could do this?
>
> // Implicitly call new C():
> #[C()]
> # Call the static factory instead:
> #[C::create()]
>
> So the only difference here would be that in the "traditional" case we
> omit the "new " part.
>
> We probably want to guarantee that attributes are always objects.
> We can only evaluate this when somebody calls ->newInstance(), because
> before that we don't want to autoload the class with the factory. So
> we could throw an exception if the return value is something other
> than an object.
> I was also considering to require an explicit return type hint on the
> factory method, but again this can only be evaluated when somebody
> calls ->newInstance(), so the benefit of that would be limited.
>
> The #[Attribute] annotation would allow methods as target.
>
> Reflection:
>
> ::getArguments() -> same as before.
> ::getName() -> returns "$class_qcn::$method_name".
> ::getTarget() -> same as before.
> ::isRepeated() -> This is poorly documented on php.net, but it seems
> to just look for other attributes with the same ->getName(). So it
> could do the same here.
> ::newInstance() -> calls the method. Throws an error if return value
> is non-object.
>
> we could add more methods like ReflectionAttribute::isClass() or
> ReflectionAttribute::isMethod(), or a more generic ::getType(), but
> these are not absolutely required.
>
> We could also allow regular functions, but this would cause ambiguity
> if a class and a function have the same name. Also, functions cannot
> be autoloaded, so the benefit would be small. I'd rather stick to just
> methods.
>

I see where you're coming from here, and I think an argument could be made
that our attribute syntax should have been #[new C()], allowing us to
associate arbitrary values -- which would naturally allow the use of static
factory methods once/if they are supported in constant expressions.

As that particular ship has sailed, I'm not convinced that supporting
static factory methods as "attributes" would be worthwhile. It's a
significant complication to the system (that users need to be aware of, and
consumers of the reflection API need to handle) for what ultimately seems
like a personal style choice to me. Do you have any examples where using
static factories over constructors for attributes would be particularly
compelling?

Regards,
Nikita

Reply via email to