Re: [PHP-DEV] Static (factory) methods in attributes and initializers
On Mon, Sep 27, 2021 at 5:58 PM Andreas Hennings 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
Re: [PHP-DEV] Static (factory) methods in attributes and initializers
Hello Levi, I thought it was clear. By static "factory" method I just mean any static method that returns an object. Don't interpret too much into the term :) So to clarify, here is an example: class OtherAttribute {} class MyAttribute { public function __construct(string $name) {..} #[Attribute] public static function create(string $name): self { return new self($name); } #[Attribute] public static function createOther($x): OtherAttribute { return new OtherAttribute($x); } } class SomeValue { public static function create() {return new self();} } #[MyAttribute::create('hello')] #[MyAttribute::createOther(SomeValue::create())] class C {} // This is the other part, static method calls in regular initializers. function f($x = SomeValue::create()) Things to observe: - The classes MyAttribute and OtherAttribute are not marked as attribute via #[Attribute] annotation. Only the static methods are. - MyAttribute::create() and MyAttribute::createOther() are allowed to return any object that they want. On Mon, 27 Sept 2021 at 18:26, Levi Morrison via internals wrote: > > I'm not exactly sure what you are proposing. However, it's always > worth noting other languages which have something in the feature > space, and Dart has both named constructors and factory constructors. > You should start with researching what they do, and what other > languages may have the same as _language features_, and not as > patterns e.g. Java has a factory pattern, but it's not part of the > language (at least not that I last checked, I don't follow Java). > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
Re: [PHP-DEV] Static (factory) methods in attributes and initializers
I'm not exactly sure what you are proposing. However, it's always worth noting other languages which have something in the feature space, and Dart has both named constructors and factory constructors. You should start with researching what they do, and what other languages may have the same as _language features_, and not as patterns e.g. Java has a factory pattern, but it's not part of the language (at least not that I last checked, I don't follow Java). -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php
[PHP-DEV] Static (factory) methods in attributes and initializers
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 -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php