Static (factory) methods in attributes and initializers
| From: | Andreas Hennings | Date: | Mon, 27 Sep 2021 15:57:33 +0000 |
| Subject: | Static (factory) methods in attributes and initializers | ||
| Groups: | php.internals | ||
| Request: | Send a blank email to internals+get-116181@lists.php.net to get a copy of this message | ||
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.
-----
Side note: I think "attributes" is a really poor name for findability.
But this ship has sailed.
-----
Cheers
Andreas