|
Administrator
|
Greetings, all --
Per the roadmap, one change we're planning on making is making plugin usage consistent across components: plugins will implement __invoke(), which makes them inherently callable. In our refactoring branch, I've done this now with Zend_Filter. However, it required a ton of refactoring on the unit tests (had to either call __invoke() directly, or $filter($value)), and it got me to thinking: Would it make sense to keep the original interfaces (filter, isValid, render, etc.), but also implement __invoke() -- having it proxy to the appropriate interface method? As an example: interface Filter { public function filter($value); public function __invoke($value); } class Digits implements Filter { public function filter($value) { // ... logic... } public function __invoke($value) { return $this->filter($value); } } The benefits: * Fewer changes necessary to existing unit tests * interface method names may be more descriptive of intent Problems: * Using __invoke() introduces overhead -- 2 method calls instead of one * Redundancy (two methods doing the same thing) * A few extra tests to write (to ensure __invoke works as expecte) After discussion with Ralph and Alex, I'm leaning towards having __invoke() as an _additional_ method, instead of replacing existing methods. Any thoughts or comments? -- Matthew Weier O'Phinney Project Lead | [hidden email] Zend Framework | http://framework.zend.com/ PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc |
|
+1 on this! It's ok to have two methods for the same thing! And the
benefit of clearer interfaces is overwhelming the disadvantages! Just my 2 cents! Am 31.03.2010 um 16:21 schrieb Matthew Weier O'Phinney <[hidden email]>: > Greetings, all -- > > Per the roadmap, one change we're planning on making is making plugin > usage consistent across components: plugins will implement __invoke(), > which makes them inherently callable. > > In our refactoring branch, I've done this now with Zend_Filter. > However, > it required a ton of refactoring on the unit tests (had to either call > __invoke() directly, or $filter($value)), and it got me to thinking: > > Would it make sense to keep the original interfaces (filter, isValid, > render, etc.), but also implement __invoke() -- having it proxy to the > appropriate interface method? > > As an example: > > interface Filter > { > public function filter($value); > public function __invoke($value); > } > > class Digits implements Filter > { > public function filter($value) > { > // ... logic... > } > > public function __invoke($value) > { > return $this->filter($value); > } > } > > The benefits: > > * Fewer changes necessary to existing unit tests > * interface method names may be more descriptive of intent > > Problems: > > * Using __invoke() introduces overhead -- 2 method calls instead of > one > * Redundancy (two methods doing the same thing) > * A few extra tests to write (to ensure __invoke works as expecte) > > After discussion with Ralph and Alex, I'm leaning towards having > __invoke() as an _additional_ method, instead of replacing existing > methods. > > Any thoughts or comments? > > -- > Matthew Weier O'Phinney > Project Lead | [hidden email] > Zend Framework | http://framework.zend.com/ > PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc |
|
In reply to this post by weierophinney
Hello!
Matthew Weier O'Phinney wrote: > Would it make sense to keep the original interfaces (filter, isValid, > render, etc.), but also implement __invoke() -- having it proxy to the > appropriate interface method? +1. It is good for both speed and compatibility. -- Dmitry Dulepov Twitter: http://twitter.com/dmitryd/ Web: http://dmitry-dulepov.com/ |
|
In reply to this post by weierophinney
Hey Matthew, Hm, my 2 cents on this issue, all magic method calls are automagically at least 2 times slower than a direct method invocation. Is this really necessary given that plugins will probably be used in the hundrets to thousands during a request? Additionally although its nice to know that all plugins use __invoke you still interface them (as you mentioned in your post), which is a much more restrictive constraint programming wise. IDEs will even auto-complete you the methods for implementing interfaces. You need to know the interface name anyways to be able to build the plugin, it doesn't really help you to know that one of the methods is called __invoke. In the light of this, I don't see the benefit of using __invoke at all. greetings, Benjamin On Wed, 31 Mar 2010 10:21:52 -0400, Matthew Weier O'Phinney <[hidden email]> wrote: > Greetings, all -- > > Per the roadmap, one change we're planning on making is making plugin > usage consistent across components: plugins will implement __invoke(), > which makes them inherently callable. > > In our refactoring branch, I've done this now with Zend_Filter. However, > it required a ton of refactoring on the unit tests (had to either call > __invoke() directly, or $filter($value)), and it got me to thinking: > > Would it make sense to keep the original interfaces (filter, isValid, > render, etc.), but also implement __invoke() -- having it proxy to the > appropriate interface method? > > As an example: > > interface Filter > { > public function filter($value); > public function __invoke($value); > } > > class Digits implements Filter > { > public function filter($value) > { > // ... logic... > } > > public function __invoke($value) > { > return $this->filter($value); > } > } > > The benefits: > > * Fewer changes necessary to existing unit tests > * interface method names may be more descriptive of intent > > Problems: > > * Using __invoke() introduces overhead -- 2 method calls instead of one > * Redundancy (two methods doing the same thing) > * A few extra tests to write (to ensure __invoke works as expecte) > > After discussion with Ralph and Alex, I'm leaning towards having > __invoke() as an _additional_ method, instead of replacing existing > methods. > > Any thoughts or comments? |
|
In reply to this post by denniswinter
-1
Talking to my coworkers, a big piece of people take a little more time to understand something when there is more than one way to do what they want to do. I think that when we start a new project with unnecessary duplicated features it's not good. This usually happens after years, we should get this situations away. Obviously sometimes it's interesting we provide alternative ways to achieve a goal, but for those situations (filter, validator...) it's not. >> The benefits: >> >> * Fewer changes necessary to existing unit tests time is the solution ;-) >> * interface method names may be more descriptive of intent they should pick a better name for their variables, like $filterFloat, $filterAlpha >> Problems: >> >> * Using __invoke() introduces overhead -- 2 method calls instead of one >> * Redundancy (two methods doing the same thing) that's my point! this extra way is not relevant, this one adds no new feature >> * A few extra tests to write (to ensure __invoke works as expecte) my 1 cent ;-) -- Renan de Lima Barbosa gtalk/msn: [hidden email] skype: renandelima +55 61 8166-7755 renandelima.com On 31 March 2010 11:38, Dennis Winter <[hidden email]> wrote: > +1 on this! It's ok to have two methods for the same thing! And the benefit > of clearer interfaces is overwhelming the disadvantages! > Just my 2 cents! > > Am 31.03.2010 um 16:21 schrieb Matthew Weier O'Phinney <[hidden email]>: > >> Greetings, all -- >> >> Per the roadmap, one change we're planning on making is making plugin >> usage consistent across components: plugins will implement __invoke(), >> which makes them inherently callable. >> >> In our refactoring branch, I've done this now with Zend_Filter. However, >> it required a ton of refactoring on the unit tests (had to either call >> __invoke() directly, or $filter($value)), and it got me to thinking: >> >> Would it make sense to keep the original interfaces (filter, isValid, >> render, etc.), but also implement __invoke() -- having it proxy to the >> appropriate interface method? >> >> As an example: >> >> interface Filter >> { >> public function filter($value); >> public function __invoke($value); >> } >> >> class Digits implements Filter >> { >> public function filter($value) >> { >> // ... logic... >> } >> >> public function __invoke($value) >> { >> return $this->filter($value); >> } >> } >> >> The benefits: >> >> * Fewer changes necessary to existing unit tests >> * interface method names may be more descriptive of intent >> >> Problems: >> >> * Using __invoke() introduces overhead -- 2 method calls instead of one >> * Redundancy (two methods doing the same thing) >> * A few extra tests to write (to ensure __invoke works as expecte) >> >> After discussion with Ralph and Alex, I'm leaning towards having >> __invoke() as an _additional_ method, instead of replacing existing >> methods. >> >> Any thoughts or comments? >> >> -- >> Matthew Weier O'Phinney >> Project Lead | [hidden email] >> Zend Framework | http://framework.zend.com/ >> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc > |
|
-1 from me, too - I'm totally agreeing with Renan.
Additionally I don't think design decisions should be based on how much work it is to write the corresponding unit tests. Don't get me wrong, of course the code must be testable. But especially for framework code I think that a low overhead should be preferred against easier testability - infact framework code is written (or changed) far more seldom than executed. If, however you decide to use __invoke as proxy, I would recommend to somehow prevent the proxied methods (filter, validate) to be called from outside or at least mark them as deprecated so client code _must_ use invoke and we could then remove the proxies later on if it should be decided that the tests are rewritten. best regards Lukas Köll > -1 > > Talking to my coworkers, a big piece of people take a little more time > to understand something when there is more than one way to do what > they want to do. I think that when we start a new project with > unnecessary duplicated features it's not good. This usually happens > after years, we should get this situations away. Obviously sometimes > it's interesting we provide alternative ways to achieve a goal, but > for those situations (filter, validator...) it's not. > >>> The benefits: >>> >>> * Fewer changes necessary to existing unit tests > > time is the solution ;-) > >>> * interface method names may be more descriptive of intent > > they should pick a better name for their variables, like $filterFloat, > $filterAlpha > >>> Problems: >>> >>> * Using __invoke() introduces overhead -- 2 method calls instead of one >>> * Redundancy (two methods doing the same thing) > > that's my point! this extra way is not relevant, this one adds no new feature > >>> * A few extra tests to write (to ensure __invoke works as expecte) > > my 1 cent ;-) > > -- > Renan de Lima Barbosa > gtalk/msn: [hidden email] > skype: renandelima > +55 61 8166-7755 > renandelima.com > > > > On 31 March 2010 11:38, Dennis Winter <[hidden email]> wrote: >> +1 on this! It's ok to have two methods for the same thing! And the benefit >> of clearer interfaces is overwhelming the disadvantages! >> Just my 2 cents! >> >> Am 31.03.2010 um 16:21 schrieb Matthew Weier O'Phinney <[hidden email]>: >> >>> Greetings, all -- >>> >>> Per the roadmap, one change we're planning on making is making plugin >>> usage consistent across components: plugins will implement __invoke(), >>> which makes them inherently callable. >>> >>> In our refactoring branch, I've done this now with Zend_Filter. However, >>> it required a ton of refactoring on the unit tests (had to either call >>> __invoke() directly, or $filter($value)), and it got me to thinking: >>> >>> Would it make sense to keep the original interfaces (filter, isValid, >>> render, etc.), but also implement __invoke() -- having it proxy to the >>> appropriate interface method? >>> >>> As an example: >>> >>> interface Filter >>> { >>> public function filter($value); >>> public function __invoke($value); >>> } >>> >>> class Digits implements Filter >>> { >>> public function filter($value) >>> { >>> // ... logic... >>> } >>> >>> public function __invoke($value) >>> { >>> return $this->filter($value); >>> } >>> } >>> >>> The benefits: >>> >>> * Fewer changes necessary to existing unit tests >>> * interface method names may be more descriptive of intent >>> >>> Problems: >>> >>> * Using __invoke() introduces overhead -- 2 method calls instead of one >>> * Redundancy (two methods doing the same thing) >>> * A few extra tests to write (to ensure __invoke works as expecte) >>> >>> After discussion with Ralph and Alex, I'm leaning towards having >>> __invoke() as an _additional_ method, instead of replacing existing >>> methods. >>> >>> Any thoughts or comments? >>> >>> -- >>> Matthew Weier O'Phinney >>> Project Lead | [hidden email] >>> Zend Framework | http://framework.zend.com/ >>> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc >> |
|
-1 from me too.
Looking at the interface it's absolutely not clear what are those two methods intended for. Interfaces should not require looking at the actual implementation to get that some methods are just for the sake of making testing easier (?) and in all implementations just provide a proxy. Personally, I think that once you can do things in more than 1 way, it's confusing and misleading (not in all senses, but in a sense of method call). Now it *might* work if that proxy is provided by abstract class (just to make code more backwards compatible/any other reason), but still since we are talking about 2.0 here, I'd rather have only __invoke. Speaking of tests, I didn't completely get where the problem lies: $filter->filter($value); becomes: $filter($value); is this the refactoring you are mentioning? -- Juozas Kaziukėnas ([hidden email]) My website - JuoKaz (http://www.juokaz.com) On Wed, Mar 31, 2010 at 8:38 PM, Lukas Köll <[hidden email]> wrote: > -1 from me, too - I'm totally agreeing with Renan. > Additionally I don't think design decisions should be based on how much work it > is to write the corresponding unit tests. Don't get me wrong, of course the code > must be testable. But especially for framework code I think that a low overhead > should be preferred against easier testability - infact framework code is > written (or changed) far more seldom than executed. > > If, however you decide to use __invoke as proxy, I would recommend to somehow > prevent the proxied methods (filter, validate) to be called from outside or at least mark them as deprecated > so client code _must_ use invoke and we could then remove the proxies later on if > it should be decided that the tests are rewritten. > > best regards > > Lukas Köll > >> -1 >> >> Talking to my coworkers, a big piece of people take a little more time >> to understand something when there is more than one way to do what >> they want to do. I think that when we start a new project with >> unnecessary duplicated features it's not good. This usually happens >> after years, we should get this situations away. Obviously sometimes >> it's interesting we provide alternative ways to achieve a goal, but >> for those situations (filter, validator...) it's not. >> >>>> The benefits: >>>> >>>> * Fewer changes necessary to existing unit tests >> >> time is the solution ;-) >> >>>> * interface method names may be more descriptive of intent >> >> they should pick a better name for their variables, like $filterFloat, >> $filterAlpha >> >>>> Problems: >>>> >>>> * Using __invoke() introduces overhead -- 2 method calls instead of one >>>> * Redundancy (two methods doing the same thing) >> >> that's my point! this extra way is not relevant, this one adds no new feature >> >>>> * A few extra tests to write (to ensure __invoke works as expecte) >> >> my 1 cent ;-) >> >> -- >> Renan de Lima Barbosa >> gtalk/msn: [hidden email] >> skype: renandelima >> +55 61 8166-7755 >> renandelima.com >> >> >> >> On 31 March 2010 11:38, Dennis Winter <[hidden email]> wrote: >>> +1 on this! It's ok to have two methods for the same thing! And the benefit >>> of clearer interfaces is overwhelming the disadvantages! >>> Just my 2 cents! >>> >>> Am 31.03.2010 um 16:21 schrieb Matthew Weier O'Phinney <[hidden email]>: >>> >>>> Greetings, all -- >>>> >>>> Per the roadmap, one change we're planning on making is making plugin >>>> usage consistent across components: plugins will implement __invoke(), >>>> which makes them inherently callable. >>>> >>>> In our refactoring branch, I've done this now with Zend_Filter. However, >>>> it required a ton of refactoring on the unit tests (had to either call >>>> __invoke() directly, or $filter($value)), and it got me to thinking: >>>> >>>> Would it make sense to keep the original interfaces (filter, isValid, >>>> render, etc.), but also implement __invoke() -- having it proxy to the >>>> appropriate interface method? >>>> >>>> As an example: >>>> >>>> interface Filter >>>> { >>>> public function filter($value); >>>> public function __invoke($value); >>>> } >>>> >>>> class Digits implements Filter >>>> { >>>> public function filter($value) >>>> { >>>> // ... logic... >>>> } >>>> >>>> public function __invoke($value) >>>> { >>>> return $this->filter($value); >>>> } >>>> } >>>> >>>> The benefits: >>>> >>>> * Fewer changes necessary to existing unit tests >>>> * interface method names may be more descriptive of intent >>>> >>>> Problems: >>>> >>>> * Using __invoke() introduces overhead -- 2 method calls instead of one >>>> * Redundancy (two methods doing the same thing) >>>> * A few extra tests to write (to ensure __invoke works as expecte) >>>> >>>> After discussion with Ralph and Alex, I'm leaning towards having >>>> __invoke() as an _additional_ method, instead of replacing existing >>>> methods. >>>> >>>> Any thoughts or comments? >>>> >>>> -- >>>> Matthew Weier O'Phinney >>>> Project Lead | [hidden email] >>>> Zend Framework | http://framework.zend.com/ >>>> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc >>> > > |
|
In reply to this post by Lukas Köll
-1 from me
I see no benefit of having 2 methods doing the same work Additionally forcing __invoke() means that ZF does no longer work below PHP 5.3. Even if we limit ZF 2.0 to 5.3 it does not mean that single components are not able to work below 5.3. And working with OOP I see no benefit of using objects by calling them as function. It's like a step backwards from OOP. my 3 cents Greetings Thomas Weidner, I18N Team Leader, Zend Framework http://www.thomasweidner.com ----- Original Message ----- From: "Lukas Köll" <[hidden email]> To: <[hidden email]> Sent: Wednesday, March 31, 2010 9:38 PM Subject: Re: [zf-contributors] Plugin implementations -1 from me, too - I'm totally agreeing with Renan. Additionally I don't think design decisions should be based on how much work it is to write the corresponding unit tests. Don't get me wrong, of course the code must be testable. But especially for framework code I think that a low overhead should be preferred against easier testability - infact framework code is written (or changed) far more seldom than executed. If, however you decide to use __invoke as proxy, I would recommend to somehow prevent the proxied methods (filter, validate) to be called from outside or at least mark them as deprecated so client code _must_ use invoke and we could then remove the proxies later on if it should be decided that the tests are rewritten. best regards Lukas Köll > -1 > > Talking to my coworkers, a big piece of people take a little more time > to understand something when there is more than one way to do what > they want to do. I think that when we start a new project with > unnecessary duplicated features it's not good. This usually happens > after years, we should get this situations away. Obviously sometimes > it's interesting we provide alternative ways to achieve a goal, but > for those situations (filter, validator...) it's not. > >>> The benefits: >>> >>> * Fewer changes necessary to existing unit tests > > time is the solution ;-) > >>> * interface method names may be more descriptive of intent > > they should pick a better name for their variables, like $filterFloat, > $filterAlpha > >>> Problems: >>> >>> * Using __invoke() introduces overhead -- 2 method calls instead of one >>> * Redundancy (two methods doing the same thing) > > that's my point! this extra way is not relevant, this one adds no new > feature > >>> * A few extra tests to write (to ensure __invoke works as expecte) > > my 1 cent ;-) > > -- > Renan de Lima Barbosa > gtalk/msn: [hidden email] > skype: renandelima > +55 61 8166-7755 > renandelima.com > > > > On 31 March 2010 11:38, Dennis Winter <[hidden email]> > wrote: >> +1 on this! It's ok to have two methods for the same thing! And the >> benefit >> of clearer interfaces is overwhelming the disadvantages! >> Just my 2 cents! >> >> Am 31.03.2010 um 16:21 schrieb Matthew Weier O'Phinney >> <[hidden email]>: >> >>> Greetings, all -- >>> >>> Per the roadmap, one change we're planning on making is making plugin >>> usage consistent across components: plugins will implement __invoke(), >>> which makes them inherently callable. >>> >>> In our refactoring branch, I've done this now with Zend_Filter. However, >>> it required a ton of refactoring on the unit tests (had to either call >>> __invoke() directly, or $filter($value)), and it got me to thinking: >>> >>> Would it make sense to keep the original interfaces (filter, isValid, >>> render, etc.), but also implement __invoke() -- having it proxy to the >>> appropriate interface method? >>> >>> As an example: >>> >>> interface Filter >>> { >>> public function filter($value); >>> public function __invoke($value); >>> } >>> >>> class Digits implements Filter >>> { >>> public function filter($value) >>> { >>> // ... logic... >>> } >>> >>> public function __invoke($value) >>> { >>> return $this->filter($value); >>> } >>> } >>> >>> The benefits: >>> >>> * Fewer changes necessary to existing unit tests >>> * interface method names may be more descriptive of intent >>> >>> Problems: >>> >>> * Using __invoke() introduces overhead -- 2 method calls instead of one >>> * Redundancy (two methods doing the same thing) >>> * A few extra tests to write (to ensure __invoke works as expecte) >>> >>> After discussion with Ralph and Alex, I'm leaning towards having >>> __invoke() as an _additional_ method, instead of replacing existing >>> methods. >>> >>> Any thoughts or comments? >>> >>> -- >>> Matthew Weier O'Phinney >>> Project Lead | [hidden email] >>> Zend Framework | http://framework.zend.com/ >>> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc >> |
|
Administrator
|
In reply to this post by Juozas
-- Juozas <[hidden email]> wrote
(on Wednesday, 31 March 2010, 08:59 PM +0100): > -1 from me too. > > Looking at the interface it's absolutely not clear what are those two > methods intended for. Interfaces should not require looking at the > actual implementation to get that some methods are just for the sake > of making testing easier (?) and in all implementations just provide a > proxy. Actually, I'm not talking about the implementation being harder, but the fact that conversion and migration takes a lot more effort, due to the fact that more code needs to change in the unit tests. As an example, I had the whole Filter tree migrated in just over an hour yesterday -- but the tests took another 5-6 hours to complete, due to the number of changes necessary to make them comply with the new signatures. But if it makes the implementation better, then it's worth it. I'm just questioning whether that's the case. > Personally, I think that once you can do things in more than 1 way, > it's confusing and misleading (not in all senses, but in a sense of > method call). Now it *might* work if that proxy is provided by > abstract class (just to make code more backwards compatible/any other > reason), but still since we are talking about 2.0 here, I'd rather > have only __invoke. > > Speaking of tests, I didn't completely get where the problem lies: > > $filter->filter($value); > > becomes: > > $filter($value); > > is this the refactoring you are mentioning? Yes -- and then a bit more. Often setUp() creates the filter: $this->_filter = new SomeFilter(); One problem that arises is that PHP is kind of stupid about calling __invoke on object members. You'd think this would work: $this->_filter($value); But it doesn't -- instead, PHP thinks you're calling the method "_filter" on the current object, and then FATALs on you. So you have to do this: $filter = $this->_filter; $filtered = $filter($value); Knowing this limitation, I can see other potential issues down the line -- you can't simply call the object as if it were a function; you have to think about whether you're operating on an object member or not, and then change your methodology accordingly. I see that as a potential drawback to using __invoke(). > On Wed, Mar 31, 2010 at 8:38 PM, Lukas Köll <[hidden email]> wrote: > > -1 from me, too - I'm totally agreeing with Renan. > > Additionally I don't think design decisions should be based on how much work it > > is to write the corresponding unit tests. Don't get me wrong, of course the code > > must be testable. But especially for framework code I think that a low overhead > > should be preferred against easier testability - infact framework code is > > written (or changed) far more seldom than executed. > > > > If, however you decide to use __invoke as proxy, I would recommend to somehow > > prevent the proxied methods (filter, validate) to be called from outside or at least mark them as deprecated > > so client code _must_ use invoke and we could then remove the proxies later on if > > it should be decided that the tests are rewritten. > > > > best regards > > > > Lukas Köll > > > >> -1 > >> > >> Talking to my coworkers, a big piece of people take a little more time > >> to understand something when there is more than one way to do what > >> they want to do. I think that when we start a new project with > >> unnecessary duplicated features it's not good. This usually happens > >> after years, we should get this situations away. Obviously sometimes > >> it's interesting we provide alternative ways to achieve a goal, but > >> for those situations (filter, validator...) it's not. > >> > >>>> The benefits: > >>>> > >>>> * Fewer changes necessary to existing unit tests > >> > >> time is the solution ;-) > >> > >>>> * interface method names may be more descriptive of intent > >> > >> they should pick a better name for their variables, like $filterFloat, > >> $filterAlpha > >> > >>>> Problems: > >>>> > >>>> * Using __invoke() introduces overhead -- 2 method calls instead of one > >>>> * Redundancy (two methods doing the same thing) > >> > >> that's my point! this extra way is not relevant, this one adds no new feature > >> > >>>> * A few extra tests to write (to ensure __invoke works as expecte) > >> > >> my 1 cent ;-) > >> > >> -- > >> Renan de Lima Barbosa > >> gtalk/msn: [hidden email] > >> skype: renandelima > >> +55 61 8166-7755 > >> renandelima.com > >> > >> > >> > >> On 31 March 2010 11:38, Dennis Winter <[hidden email]> wrote: > >>> +1 on this! It's ok to have two methods for the same thing! And the benefit > >>> of clearer interfaces is overwhelming the disadvantages! > >>> Just my 2 cents! > >>> > >>> Am 31.03.2010 um 16:21 schrieb Matthew Weier O'Phinney <[hidden email]>: > >>> > >>>> Greetings, all -- > >>>> > >>>> Per the roadmap, one change we're planning on making is making plugin > >>>> usage consistent across components: plugins will implement __invoke(), > >>>> which makes them inherently callable. > >>>> > >>>> In our refactoring branch, I've done this now with Zend_Filter. However, > >>>> it required a ton of refactoring on the unit tests (had to either call > >>>> __invoke() directly, or $filter($value)), and it got me to thinking: > >>>> > >>>> Would it make sense to keep the original interfaces (filter, isValid, > >>>> render, etc.), but also implement __invoke() -- having it proxy to the > >>>> appropriate interface method? > >>>> > >>>> As an example: > >>>> > >>>> interface Filter > >>>> { > >>>> public function filter($value); > >>>> public function __invoke($value); > >>>> } > >>>> > >>>> class Digits implements Filter > >>>> { > >>>> public function filter($value) > >>>> { > >>>> // ... logic... > >>>> } > >>>> > >>>> public function __invoke($value) > >>>> { > >>>> return $this->filter($value); > >>>> } > >>>> } > >>>> > >>>> The benefits: > >>>> > >>>> * Fewer changes necessary to existing unit tests > >>>> * interface method names may be more descriptive of intent > >>>> > >>>> Problems: > >>>> > >>>> * Using __invoke() introduces overhead -- 2 method calls instead of one > >>>> * Redundancy (two methods doing the same thing) > >>>> * A few extra tests to write (to ensure __invoke works as expecte) > >>>> > >>>> After discussion with Ralph and Alex, I'm leaning towards having > >>>> __invoke() as an _additional_ method, instead of replacing existing > >>>> methods. > >>>> > >>>> Any thoughts or comments? > >>>> > >>>> -- > >>>> Matthew Weier O'Phinney > >>>> Project Lead | [hidden email] > >>>> Zend Framework | http://framework.zend.com/ > >>>> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc > >>> > > > > > -- Matthew Weier O'Phinney Project Lead | [hidden email] Zend Framework | http://framework.zend.com/ PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc |
|
Administrator
|
In reply to this post by Thomas Weidner
-- Thomas Weidner <[hidden email]> wrote
(on Wednesday, 31 March 2010, 10:04 PM +0200): > -1 from me > > I see no benefit of having 2 methods doing the same work > > Additionally forcing __invoke() means that ZF does no longer work > below PHP 5.3. > Even if we limit ZF 2.0 to 5.3 it does not mean that single > components are not able to work below 5.3. Thomas, you're forgetting one really important thing here: ZF 2 will *not* work on versions less than 5.3 anyways, due to the fact that all code will be using namespaces. ;-) > And working with OOP I see no benefit of using objects by calling > them as function. It's like a step backwards from OOP. Not necessarily. For purposes of plugins, strategy patterns, command patterns, etc., the main purpose is to provide a single method that is invoked by the caller. For purposes of consistency, it makes sense for all classes in the framework that implement these sorts of patterns to follow the same general API. While we could do something like "execute" or "run" as the method, PHP's __invoke() fulfills the same sort of purpose on the language level. Additionally, unlike using functions, you have the benefits of state -- you can pass arguments to the constructor, or call other methods to configure the object prior to "invoking" it. This is far from a step backwards from OOP -- it's simply a different pattern for using OOP in PHP. > ----- Original Message ----- From: "Lukas Köll" <[hidden email]> > To: <[hidden email]> > Sent: Wednesday, March 31, 2010 9:38 PM > Subject: Re: [zf-contributors] Plugin implementations > > > -1 from me, too - I'm totally agreeing with Renan. > Additionally I don't think design decisions should be based on how > much work it > is to write the corresponding unit tests. Don't get me wrong, of > course the code > must be testable. But especially for framework code I think that a > low overhead > should be preferred against easier testability - infact framework code is > written (or changed) far more seldom than executed. > > If, however you decide to use __invoke as proxy, I would recommend > to somehow > prevent the proxied methods (filter, validate) to be called from > outside or at least mark them as deprecated > so client code _must_ use invoke and we could then remove the > proxies later on if > it should be decided that the tests are rewritten. > > best regards > > Lukas Köll > > >-1 > > > >Talking to my coworkers, a big piece of people take a little more time > >to understand something when there is more than one way to do what > >they want to do. I think that when we start a new project with > >unnecessary duplicated features it's not good. This usually happens > >after years, we should get this situations away. Obviously sometimes > >it's interesting we provide alternative ways to achieve a goal, but > >for those situations (filter, validator...) it's not. > > > >>>The benefits: > >>> > >>>* Fewer changes necessary to existing unit tests > > > >time is the solution ;-) > > > >>>* interface method names may be more descriptive of intent > > > >they should pick a better name for their variables, like $filterFloat, > >$filterAlpha > > > >>>Problems: > >>> > >>>* Using __invoke() introduces overhead -- 2 method calls instead of one > >>>* Redundancy (two methods doing the same thing) > > > >that's my point! this extra way is not relevant, this one adds no > >new feature > > > >>>* A few extra tests to write (to ensure __invoke works as expecte) > > > >my 1 cent ;-) > > > >-- > >Renan de Lima Barbosa > >gtalk/msn: [hidden email] > >skype: renandelima > >+55 61 8166-7755 > >renandelima.com > > > > > > > >On 31 March 2010 11:38, Dennis Winter > ><[hidden email]> wrote: > >>+1 on this! It's ok to have two methods for the same thing! And > >>the benefit > >>of clearer interfaces is overwhelming the disadvantages! > >>Just my 2 cents! > >> > >>Am 31.03.2010 um 16:21 schrieb Matthew Weier O'Phinney > >><[hidden email]>: > >> > >>>Greetings, all -- > >>> > >>>Per the roadmap, one change we're planning on making is making plugin > >>>usage consistent across components: plugins will implement __invoke(), > >>>which makes them inherently callable. > >>> > >>>In our refactoring branch, I've done this now with Zend_Filter. However, > >>>it required a ton of refactoring on the unit tests (had to either call > >>>__invoke() directly, or $filter($value)), and it got me to thinking: > >>> > >>>Would it make sense to keep the original interfaces (filter, isValid, > >>>render, etc.), but also implement __invoke() -- having it proxy to the > >>>appropriate interface method? > >>> > >>>As an example: > >>> > >>> interface Filter > >>> { > >>> public function filter($value); > >>> public function __invoke($value); > >>> } > >>> > >>> class Digits implements Filter > >>> { > >>> public function filter($value) > >>> { > >>> // ... logic... > >>> } > >>> > >>> public function __invoke($value) > >>> { > >>> return $this->filter($value); > >>> } > >>> } > >>> > >>>The benefits: > >>> > >>>* Fewer changes necessary to existing unit tests > >>>* interface method names may be more descriptive of intent > >>> > >>>Problems: > >>> > >>>* Using __invoke() introduces overhead -- 2 method calls instead of one > >>>* Redundancy (two methods doing the same thing) > >>>* A few extra tests to write (to ensure __invoke works as expecte) > >>> > >>>After discussion with Ralph and Alex, I'm leaning towards having > >>>__invoke() as an _additional_ method, instead of replacing existing > >>>methods. > >>> > >>>Any thoughts or comments? > >>> > >>>-- > >>>Matthew Weier O'Phinney > >>>Project Lead | [hidden email] > >>>Zend Framework | http://framework.zend.com/ > >>>PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc > >> > -- Matthew Weier O'Phinney Project Lead | [hidden email] Zend Framework | http://framework.zend.com/ PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc |
|
In reply to this post by weierophinney
I'm starting to question __invoke now... Are initial ideas behind this
somewhere? http://framework.zend.com/wiki/display/ZFDEV2/Plugin+Architectures found this one, but it hardly explains "why?". It does say that it's consistent which is as consistent as filter(). You are mentioning that it makes it consistent between different plugins. But, I like when method calls are self explanatory, for example if I have: $anything->filter($value); I know that it's filtering something, or: $anything->render($value); it renders it somehow. Now basing all calls on __invoke magic method means that I the only thing what describes what I'm doing now, is variable name, and they are not always *that* good for doing this, especially when you try to keep them short (not $a, but standards-short). ..., and that it's simple. The more I look at this, the more I think that it's not simple at all. For such things as anonymous functions it does look ok, but for objects... Really think standard methods are much more straightforward. And lets say we go with __invoke as interface method (only). Not sure why, but it doesn't feel right. My 2 British pennies. -- Juozas Kaziukėnas ([hidden email]) My website - JuoKaz (http://www.juokaz.com) On Wed, Mar 31, 2010 at 9:37 PM, Matthew Weier O'Phinney <[hidden email]> wrote: > -- Juozas <[hidden email]> wrote > (on Wednesday, 31 March 2010, 08:59 PM +0100): >> -1 from me too. >> >> Looking at the interface it's absolutely not clear what are those two >> methods intended for. Interfaces should not require looking at the >> actual implementation to get that some methods are just for the sake >> of making testing easier (?) and in all implementations just provide a >> proxy. > > Actually, I'm not talking about the implementation being harder, but the > fact that conversion and migration takes a lot more effort, due to the > fact that more code needs to change in the unit tests. As an example, I > had the whole Filter tree migrated in just over an hour yesterday -- but > the tests took another 5-6 hours to complete, due to the number of > changes necessary to make them comply with the new signatures. > > But if it makes the implementation better, then it's worth it. I'm just > questioning whether that's the case. > >> Personally, I think that once you can do things in more than 1 way, >> it's confusing and misleading (not in all senses, but in a sense of >> method call). Now it *might* work if that proxy is provided by >> abstract class (just to make code more backwards compatible/any other >> reason), but still since we are talking about 2.0 here, I'd rather >> have only __invoke. >> >> Speaking of tests, I didn't completely get where the problem lies: >> >> $filter->filter($value); >> >> becomes: >> >> $filter($value); >> >> is this the refactoring you are mentioning? > > Yes -- and then a bit more. > > Often setUp() creates the filter: > > $this->_filter = new SomeFilter(); > > One problem that arises is that PHP is kind of stupid about calling > __invoke on object members. You'd think this would work: > > $this->_filter($value); > > But it doesn't -- instead, PHP thinks you're calling the method > "_filter" on the current object, and then FATALs on you. So you have to > do this: > > $filter = $this->_filter; > $filtered = $filter($value); > > Knowing this limitation, I can see other potential issues down the line > -- you can't simply call the object as if it were a function; you have > to think about whether you're operating on an object member or not, and > then change your methodology accordingly. I see that as a potential > drawback to using __invoke(). > >> On Wed, Mar 31, 2010 at 8:38 PM, Lukas Köll <[hidden email]> wrote: >> > -1 from me, too - I'm totally agreeing with Renan. >> > Additionally I don't think design decisions should be based on how much work it >> > is to write the corresponding unit tests. Don't get me wrong, of course the code >> > must be testable. But especially for framework code I think that a low overhead >> > should be preferred against easier testability - infact framework code is >> > written (or changed) far more seldom than executed. >> > >> > If, however you decide to use __invoke as proxy, I would recommend to somehow >> > prevent the proxied methods (filter, validate) to be called from outside or at least mark them as deprecated >> > so client code _must_ use invoke and we could then remove the proxies later on if >> > it should be decided that the tests are rewritten. >> > >> > best regards >> > >> > Lukas Köll >> > >> >> -1 >> >> >> >> Talking to my coworkers, a big piece of people take a little more time >> >> to understand something when there is more than one way to do what >> >> they want to do. I think that when we start a new project with >> >> unnecessary duplicated features it's not good. This usually happens >> >> after years, we should get this situations away. Obviously sometimes >> >> it's interesting we provide alternative ways to achieve a goal, but >> >> for those situations (filter, validator...) it's not. >> >> >> >>>> The benefits: >> >>>> >> >>>> * Fewer changes necessary to existing unit tests >> >> >> >> time is the solution ;-) >> >> >> >>>> * interface method names may be more descriptive of intent >> >> >> >> they should pick a better name for their variables, like $filterFloat, >> >> $filterAlpha >> >> >> >>>> Problems: >> >>>> >> >>>> * Using __invoke() introduces overhead -- 2 method calls instead of one >> >>>> * Redundancy (two methods doing the same thing) >> >> >> >> that's my point! this extra way is not relevant, this one adds no new feature >> >> >> >>>> * A few extra tests to write (to ensure __invoke works as expecte) >> >> >> >> my 1 cent ;-) >> >> >> >> -- >> >> Renan de Lima Barbosa >> >> gtalk/msn: [hidden email] >> >> skype: renandelima >> >> +55 61 8166-7755 >> >> renandelima.com >> >> >> >> >> >> >> >> On 31 March 2010 11:38, Dennis Winter <[hidden email]> wrote: >> >>> +1 on this! It's ok to have two methods for the same thing! And the benefit >> >>> of clearer interfaces is overwhelming the disadvantages! >> >>> Just my 2 cents! >> >>> >> >>> Am 31.03.2010 um 16:21 schrieb Matthew Weier O'Phinney <[hidden email]>: >> >>> >> >>>> Greetings, all -- >> >>>> >> >>>> Per the roadmap, one change we're planning on making is making plugin >> >>>> usage consistent across components: plugins will implement __invoke(), >> >>>> which makes them inherently callable. >> >>>> >> >>>> In our refactoring branch, I've done this now with Zend_Filter. However, >> >>>> it required a ton of refactoring on the unit tests (had to either call >> >>>> __invoke() directly, or $filter($value)), and it got me to thinking: >> >>>> >> >>>> Would it make sense to keep the original interfaces (filter, isValid, >> >>>> render, etc.), but also implement __invoke() -- having it proxy to the >> >>>> appropriate interface method? >> >>>> >> >>>> As an example: >> >>>> >> >>>> interface Filter >> >>>> { >> >>>> public function filter($value); >> >>>> public function __invoke($value); >> >>>> } >> >>>> >> >>>> class Digits implements Filter >> >>>> { >> >>>> public function filter($value) >> >>>> { >> >>>> // ... logic... >> >>>> } >> >>>> >> >>>> public function __invoke($value) >> >>>> { >> >>>> return $this->filter($value); >> >>>> } >> >>>> } >> >>>> >> >>>> The benefits: >> >>>> >> >>>> * Fewer changes necessary to existing unit tests >> >>>> * interface method names may be more descriptive of intent >> >>>> >> >>>> Problems: >> >>>> >> >>>> * Using __invoke() introduces overhead -- 2 method calls instead of one >> >>>> * Redundancy (two methods doing the same thing) >> >>>> * A few extra tests to write (to ensure __invoke works as expecte) >> >>>> >> >>>> After discussion with Ralph and Alex, I'm leaning towards having >> >>>> __invoke() as an _additional_ method, instead of replacing existing >> >>>> methods. >> >>>> >> >>>> Any thoughts or comments? >> >>>> >> >>>> -- >> >>>> Matthew Weier O'Phinney >> >>>> Project Lead | [hidden email] >> >>>> Zend Framework | http://framework.zend.com/ >> >>>> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc >> >>> >> > >> > >> > > -- > Matthew Weier O'Phinney > Project Lead | [hidden email] > Zend Framework | http://framework.zend.com/ > PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc > |
|
My understanding is that __invoke is simply the PHP 5.3 function for what we've been calling "direct" or whatever the function name is.
To me, it makes sense to take advantage of features that solve a problem that's already available in the language, then creating your own custom conventions second. Does anything else change with the switch to __invoke, besides the time & effort to bring the unit tests up to speed? I was under the impression it's a behind-the-scenes change. On Mar 31, 2010, at 3:49 PM, Juozas wrote: > I'm starting to question __invoke now... Are initial ideas behind this > somewhere? http://framework.zend.com/wiki/display/ZFDEV2/Plugin+Architectures > found this one, but it hardly explains "why?". It does say that it's > consistent which is as consistent as filter(). You are mentioning that > it makes it consistent between different plugins. But, I like when > method calls are self explanatory, for example if I have: > > $anything->filter($value); > > I know that it's filtering something, or: > > $anything->render($value); > > it renders it somehow. Now basing all calls on __invoke magic method > means that I the only thing what describes what I'm doing now, is > variable name, and they are not always *that* good for doing this, > especially when you try to keep them short (not $a, but > standards-short). > > ..., and that it's simple. The more I look at this, the more I think > that it's not simple at all. For such things as anonymous functions it > does look ok, but for objects... Really think standard methods are > much more straightforward. > > And lets say we go with __invoke as interface method (only). Not sure > why, but it doesn't feel right. > > My 2 British pennies. > > -- > Juozas Kaziukėnas ([hidden email]) > My website - JuoKaz (http://www.juokaz.com) > > > > On Wed, Mar 31, 2010 at 9:37 PM, Matthew Weier O'Phinney > <[hidden email]> wrote: >> -- Juozas <[hidden email]> wrote >> (on Wednesday, 31 March 2010, 08:59 PM +0100): >>> -1 from me too. >>> >>> Looking at the interface it's absolutely not clear what are those two >>> methods intended for. Interfaces should not require looking at the >>> actual implementation to get that some methods are just for the sake >>> of making testing easier (?) and in all implementations just provide a >>> proxy. >> >> Actually, I'm not talking about the implementation being harder, but the >> fact that conversion and migration takes a lot more effort, due to the >> fact that more code needs to change in the unit tests. As an example, I >> had the whole Filter tree migrated in just over an hour yesterday -- but >> the tests took another 5-6 hours to complete, due to the number of >> changes necessary to make them comply with the new signatures. >> >> But if it makes the implementation better, then it's worth it. I'm just >> questioning whether that's the case. >> >>> Personally, I think that once you can do things in more than 1 way, >>> it's confusing and misleading (not in all senses, but in a sense of >>> method call). Now it *might* work if that proxy is provided by >>> abstract class (just to make code more backwards compatible/any other >>> reason), but still since we are talking about 2.0 here, I'd rather >>> have only __invoke. >>> >>> Speaking of tests, I didn't completely get where the problem lies: >>> >>> $filter->filter($value); >>> >>> becomes: >>> >>> $filter($value); >>> >>> is this the refactoring you are mentioning? >> >> Yes -- and then a bit more. >> >> Often setUp() creates the filter: >> >> $this->_filter = new SomeFilter(); >> >> One problem that arises is that PHP is kind of stupid about calling >> __invoke on object members. You'd think this would work: >> >> $this->_filter($value); >> >> But it doesn't -- instead, PHP thinks you're calling the method >> "_filter" on the current object, and then FATALs on you. So you have to >> do this: >> >> $filter = $this->_filter; >> $filtered = $filter($value); >> >> Knowing this limitation, I can see other potential issues down the line >> -- you can't simply call the object as if it were a function; you have >> to think about whether you're operating on an object member or not, and >> then change your methodology accordingly. I see that as a potential >> drawback to using __invoke(). >> >>> On Wed, Mar 31, 2010 at 8:38 PM, Lukas Köll <[hidden email]> wrote: >>>> -1 from me, too - I'm totally agreeing with Renan. >>>> Additionally I don't think design decisions should be based on how much work it >>>> is to write the corresponding unit tests. Don't get me wrong, of course the code >>>> must be testable. But especially for framework code I think that a low overhead >>>> should be preferred against easier testability - infact framework code is >>>> written (or changed) far more seldom than executed. >>>> >>>> If, however you decide to use __invoke as proxy, I would recommend to somehow >>>> prevent the proxied methods (filter, validate) to be called from outside or at least mark them as deprecated >>>> so client code _must_ use invoke and we could then remove the proxies later on if >>>> it should be decided that the tests are rewritten. >>>> >>>> best regards >>>> >>>> Lukas Köll >>>> >>>>> -1 >>>>> >>>>> Talking to my coworkers, a big piece of people take a little more time >>>>> to understand something when there is more than one way to do what >>>>> they want to do. I think that when we start a new project with >>>>> unnecessary duplicated features it's not good. This usually happens >>>>> after years, we should get this situations away. Obviously sometimes >>>>> it's interesting we provide alternative ways to achieve a goal, but >>>>> for those situations (filter, validator...) it's not. >>>>> >>>>>>> The benefits: >>>>>>> >>>>>>> * Fewer changes necessary to existing unit tests >>>>> >>>>> time is the solution ;-) >>>>> >>>>>>> * interface method names may be more descriptive of intent >>>>> >>>>> they should pick a better name for their variables, like $filterFloat, >>>>> $filterAlpha >>>>> >>>>>>> Problems: >>>>>>> >>>>>>> * Using __invoke() introduces overhead -- 2 method calls instead of one >>>>>>> * Redundancy (two methods doing the same thing) >>>>> >>>>> that's my point! this extra way is not relevant, this one adds no new feature >>>>> >>>>>>> * A few extra tests to write (to ensure __invoke works as expecte) >>>>> >>>>> my 1 cent ;-) >>>>> >>>>> -- >>>>> Renan de Lima Barbosa >>>>> gtalk/msn: [hidden email] >>>>> skype: renandelima >>>>> +55 61 8166-7755 >>>>> renandelima.com >>>>> >>>>> >>>>> >>>>> On 31 March 2010 11:38, Dennis Winter <[hidden email]> wrote: >>>>>> +1 on this! It's ok to have two methods for the same thing! And the benefit >>>>>> of clearer interfaces is overwhelming the disadvantages! >>>>>> Just my 2 cents! >>>>>> >>>>>> Am 31.03.2010 um 16:21 schrieb Matthew Weier O'Phinney <[hidden email]>: >>>>>> >>>>>>> Greetings, all -- >>>>>>> >>>>>>> Per the roadmap, one change we're planning on making is making plugin >>>>>>> usage consistent across components: plugins will implement __invoke(), >>>>>>> which makes them inherently callable. >>>>>>> >>>>>>> In our refactoring branch, I've done this now with Zend_Filter. However, >>>>>>> it required a ton of refactoring on the unit tests (had to either call >>>>>>> __invoke() directly, or $filter($value)), and it got me to thinking: >>>>>>> >>>>>>> Would it make sense to keep the original interfaces (filter, isValid, >>>>>>> render, etc.), but also implement __invoke() -- having it proxy to the >>>>>>> appropriate interface method? >>>>>>> >>>>>>> As an example: >>>>>>> >>>>>>> interface Filter >>>>>>> { >>>>>>> public function filter($value); >>>>>>> public function __invoke($value); >>>>>>> } >>>>>>> >>>>>>> class Digits implements Filter >>>>>>> { >>>>>>> public function filter($value) >>>>>>> { >>>>>>> // ... logic... >>>>>>> } >>>>>>> >>>>>>> public function __invoke($value) >>>>>>> { >>>>>>> return $this->filter($value); >>>>>>> } >>>>>>> } >>>>>>> >>>>>>> The benefits: >>>>>>> >>>>>>> * Fewer changes necessary to existing unit tests >>>>>>> * interface method names may be more descriptive of intent >>>>>>> >>>>>>> Problems: >>>>>>> >>>>>>> * Using __invoke() introduces overhead -- 2 method calls instead of one >>>>>>> * Redundancy (two methods doing the same thing) >>>>>>> * A few extra tests to write (to ensure __invoke works as expecte) >>>>>>> >>>>>>> After discussion with Ralph and Alex, I'm leaning towards having >>>>>>> __invoke() as an _additional_ method, instead of replacing existing >>>>>>> methods. >>>>>>> >>>>>>> Any thoughts or comments? >>>>>>> >>>>>>> -- >>>>>>> Matthew Weier O'Phinney >>>>>>> Project Lead | [hidden email] >>>>>>> Zend Framework | http://framework.zend.com/ >>>>>>> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc >>>>>> >>>> >>>> >>> >> >> -- >> Matthew Weier O'Phinney >> Project Lead | [hidden email] >> Zend Framework | http://framework.zend.com/ >> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc >> |
|
In reply to this post by Thomas Weidner
+1
I like it to have the magic method only as an addition because: 1. Some users don't like to use objects as methods and would call them with a clean OOP style method anyhow its a nice feature to call the plugin object directly function like. 2. Additional nobody implement the magic method "__toString" without the real method "toString" Greetings Am 31.03.2010 22:04, schrieb Thomas Weidner: > -1 from me > > I see no benefit of having 2 methods doing the same work > > Additionally forcing __invoke() means that ZF does no longer work > below PHP 5.3. > Even if we limit ZF 2.0 to 5.3 it does not mean that single components > are not able to work below 5.3. > > And working with OOP I see no benefit of using objects by calling them > as function. It's like a step backwards from OOP. > > my 3 cents > > Greetings > Thomas Weidner, I18N Team Leader, Zend Framework > http://www.thomasweidner.com > > ----- Original Message ----- From: "Lukas Köll" <[hidden email]> > To: <[hidden email]> > Sent: Wednesday, March 31, 2010 9:38 PM > Subject: Re: [zf-contributors] Plugin implementations > > > -1 from me, too - I'm totally agreeing with Renan. > Additionally I don't think design decisions should be based on how > much work it > is to write the corresponding unit tests. Don't get me wrong, of > course the code > must be testable. But especially for framework code I think that a low > overhead > should be preferred against easier testability - infact framework code is > written (or changed) far more seldom than executed. > > If, however you decide to use __invoke as proxy, I would recommend to > somehow > prevent the proxied methods (filter, validate) to be called from > outside or at least mark them as deprecated > so client code _must_ use invoke and we could then remove the proxies > later on if > it should be decided that the tests are rewritten. > > best regards > > Lukas Köll > >> -1 >> >> Talking to my coworkers, a big piece of people take a little more time >> to understand something when there is more than one way to do what >> they want to do. I think that when we start a new project with >> unnecessary duplicated features it's not good. This usually happens >> after years, we should get this situations away. Obviously sometimes >> it's interesting we provide alternative ways to achieve a goal, but >> for those situations (filter, validator...) it's not. >> >>>> The benefits: >>>> >>>> * Fewer changes necessary to existing unit tests >> >> time is the solution ;-) >> >>>> * interface method names may be more descriptive of intent >> >> they should pick a better name for their variables, like $filterFloat, >> $filterAlpha >> >>>> Problems: >>>> >>>> * Using __invoke() introduces overhead -- 2 method calls instead of >>>> one >>>> * Redundancy (two methods doing the same thing) >> >> that's my point! this extra way is not relevant, this one adds no new >> feature >> >>>> * A few extra tests to write (to ensure __invoke works as expecte) >> >> my 1 cent ;-) >> >> -- >> Renan de Lima Barbosa >> gtalk/msn: [hidden email] >> skype: renandelima >> +55 61 8166-7755 >> renandelima.com >> >> >> >> On 31 March 2010 11:38, Dennis Winter >> <[hidden email]> wrote: >>> +1 on this! It's ok to have two methods for the same thing! And the >>> benefit >>> of clearer interfaces is overwhelming the disadvantages! >>> Just my 2 cents! >>> >>> Am 31.03.2010 um 16:21 schrieb Matthew Weier O'Phinney >>> <[hidden email]>: >>> >>>> Greetings, all -- >>>> >>>> Per the roadmap, one change we're planning on making is making plugin >>>> usage consistent across components: plugins will implement __invoke(), >>>> which makes them inherently callable. >>>> >>>> In our refactoring branch, I've done this now with Zend_Filter. >>>> However, >>>> it required a ton of refactoring on the unit tests (had to either call >>>> __invoke() directly, or $filter($value)), and it got me to thinking: >>>> >>>> Would it make sense to keep the original interfaces (filter, isValid, >>>> render, etc.), but also implement __invoke() -- having it proxy to the >>>> appropriate interface method? >>>> >>>> As an example: >>>> >>>> interface Filter >>>> { >>>> public function filter($value); >>>> public function __invoke($value); >>>> } >>>> >>>> class Digits implements Filter >>>> { >>>> public function filter($value) >>>> { >>>> // ... logic... >>>> } >>>> >>>> public function __invoke($value) >>>> { >>>> return $this->filter($value); >>>> } >>>> } >>>> >>>> The benefits: >>>> >>>> * Fewer changes necessary to existing unit tests >>>> * interface method names may be more descriptive of intent >>>> >>>> Problems: >>>> >>>> * Using __invoke() introduces overhead -- 2 method calls instead of >>>> one >>>> * Redundancy (two methods doing the same thing) >>>> * A few extra tests to write (to ensure __invoke works as expecte) >>>> >>>> After discussion with Ralph and Alex, I'm leaning towards having >>>> __invoke() as an _additional_ method, instead of replacing existing >>>> methods. >>>> >>>> Any thoughts or comments? >>>> >>>> -- >>>> Matthew Weier O'Phinney >>>> Project Lead | [hidden email] >>>> Zend Framework | http://framework.zend.com/ >>>> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc >>> > > |
|
Administrator
|
In reply to this post by weierophinney
I've been following this thread as well as discussions on IRC with
interest, as, honestly, I can completely buy into all arguments, which are, to sum things up: * Use only __invoke() * Use a method name specific to the interface * Use a standardized method name, such as "run" or "execute" * Use a combination of __invoke() and either a meaningful or standardized method name. I got to thinking about why I suggested "__invoke()" in the first place, and went back to the plugin architecture page: * http://framework.zend.com/wiki/display/ZFDEV2/Plugin+Architectures The original rationale was for using pubsub/filter chain systems. In such systems, you attach callbacks: $chain->attach($callback); If your objects implement __invoke(), $callback can simply be your object -- you're done. Otherwise, you also have to provide the method: $chain->attach($object, $method); Interestingly, in this document, I also recommended that for existing interfaces, __invoke() be added as an _additional_ method, and that it should proxy to the appropriate interface method. In this case, we'd simply be taking advantage of a language feature in places it makes sense. Now, since the Symfony Live conference, I've also been thinking about something Fabien said about Symfony 2: "less magic." To clarify, the idea is to design components so that they are explicit about what they can do. As an example, instead of using __get() and __set(), using explicit getters and setters; instead of using __call(), explicitly defining methods; instead of having __call() proxy to helpers, having a broker from which you pull them as needed. This has several implications: * Less need for explicit documentation. If you have behaviors relying on the magic methods, developers *must* read the the documentation in order to know how a class/component works. If everything is explicit, the class is self-documenting, and you can get the information you need from API docs, your IDE, etc. * Performance. Magic methods are reportedly less performant than using straight method calls (more on this later). These are important factors, and the test-driven developer in me particularly likes less magic; it's much easier to test when you have explicit behaviors. That said, I decided to run some tests. These were the scenarios: * No Proxying: * Defined "filter" method returns value * Defined "__call" method returns value * Defined "__invoke" method returns value * Proxying: * Defined "filter" method returns value * Defined "__call" method proxies to "filter" * Defined "__invoke" method proxies to "filter" * Inverse proxying * Defined "filter" method calls invoke (as "$this();") * Defined "__invoke" method returns value * call_user_func_array() * Using array($obj, 'filter') callback * Using __invoke() with no proxying * Using __invoke() with proxying The results were quite interesting. Across the board, using __call() is awful -- even if you simply return a value, it's on average 6x slower than a straight method call; proxying to another method, however, doesn't take significantly longer (so long as you're not using call_user_func_array() internally, that is). __invoke(), however, turns out to be quite different. When not proxying, it's actually *faster* than a method call (about 25% on average). Proxing __invoke() to a method call or doing the inverse are roughly the same speed -- about 2.5x slower than a straight method call (which, as you'll note, is still over twice as fast as using __call()). What was *really* interesting, however, was using call_user_func_array(), which is commonly used in filter chains and/or brokers. __invoke(), with or without proxying, *always* was around 30% *faster* than using an explicit array($obj, $method) callback. So, what does this tell me? * AVOID using __call(), particularly for often-used method calls. For helper systems, it will always be better to use a helper broker and command pattern. * For classes meant to be used primarily with filter chains or brokers, DO use __invoke(), even if it simply proxies to another interface method. So, based on the design goals and the performance benchmarking, I'm going to recommend that Filter and Validator (we renamed Validate to Validator) define an __invoke() method in addition to a more explicit interface method, and that helpers also follow this pattern. -- Matthew Weier O'Phinney <[hidden email]> wrote (on Wednesday, 31 March 2010, 10:21 AM -0400): > Greetings, all -- > > Per the roadmap, one change we're planning on making is making plugin > usage consistent across components: plugins will implement __invoke(), > which makes them inherently callable. > > In our refactoring branch, I've done this now with Zend_Filter. However, > it required a ton of refactoring on the unit tests (had to either call > __invoke() directly, or $filter($value)), and it got me to thinking: > > Would it make sense to keep the original interfaces (filter, isValid, > render, etc.), but also implement __invoke() -- having it proxy to the > appropriate interface method? > > As an example: > > interface Filter > { > public function filter($value); > public function __invoke($value); > } > > class Digits implements Filter > { > public function filter($value) > { > // ... logic... > } > > public function __invoke($value) > { > return $this->filter($value); > } > } > > The benefits: > > * Fewer changes necessary to existing unit tests > * interface method names may be more descriptive of intent > > Problems: > > * Using __invoke() introduces overhead -- 2 method calls instead of one > * Redundancy (two methods doing the same thing) > * A few extra tests to write (to ensure __invoke works as expecte) > > After discussion with Ralph and Alex, I'm leaning towards having > __invoke() as an _additional_ method, instead of replacing existing > methods. > > Any thoughts or comments? > > -- > Matthew Weier O'Phinney > Project Lead | [hidden email] > Zend Framework | http://framework.zend.com/ > PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc > -- Matthew Weier O'Phinney Project Lead | [hidden email] Zend Framework | http://framework.zend.com/ PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc |
|
> Now, since the Symfony Live conference, I've also been thinking about
> something Fabien said about Symfony 2: "less magic." To clarify, the > idea is to design components so that they are explicit about what they > can do. As an example, instead of using __get() and __set(), using > explicit getters and setters; instead of using __call(), explicitly > defining methods; instead of having __call() proxy to helpers, having a > broker from which you pull them as needed. Apparently the rumors on twitter were in place; the conference did have a bad influence on you :D More seriously, I think this can in no way be compared with SF's "magic" (does the fact that our Zend_Magic only is imaginative mean ZF does at this moment not contain any magic?). Firstly, if you have a class called foo\Filter which contains an __invoke() method, that method should obviously filter. As long as it does that and only that, there's no magic imho involved. Same goes for foo\Validator (its __invoke() method is supposed to validate), and foo\Bootstrap (guess what: __invoke() should bootstrap here). If we can get it done consistently I'm pretty sure everybody will be used to it within the first 5 seconds they look at a random piece of code. Secondly Symfony's magic is on an entirely different scale of magnitude than the naming of one type of method. Compare $random->method() generating an entire application for you with $object() which exactly does what the name of the object implies. Situations in which you have two methods doing exactly the same are often/usually a result of willing to change, but also struggling with inability to do so due to backwards compatibility constraints. However, now that we're busy on something where potentially all BC may be broken, I really don't think having two methods doing exactly the same would be beneficial to anybody. In fact, it would increase the amount of options to do something with (give or take) N! which is an awful lot, without any added value. My $0.02 (you can keep those, dollar's value sucks anyways) Dolf Freeaqingme On Thu, Apr 1, 2010 at 3:07 PM, Matthew Weier O'Phinney <[hidden email]> wrote: > I've been following this thread as well as discussions on IRC with > interest, as, honestly, I can completely buy into all arguments, which > are, to sum things up: > > * Use only __invoke() > * Use a method name specific to the interface > * Use a standardized method name, such as "run" or "execute" > * Use a combination of __invoke() and either a meaningful or > standardized method name. > > I got to thinking about why I suggested "__invoke()" in the first place, > and went back to the plugin architecture page: > > * http://framework.zend.com/wiki/display/ZFDEV2/Plugin+Architectures > > The original rationale was for using pubsub/filter chain systems. In > such systems, you attach callbacks: > > $chain->attach($callback); > > If your objects implement __invoke(), $callback can simply be your > object -- you're done. Otherwise, you also have to provide the method: > > $chain->attach($object, $method); > > Interestingly, in this document, I also recommended that for existing > interfaces, __invoke() be added as an _additional_ method, and that it > should proxy to the appropriate interface method. In this case, we'd > simply be taking advantage of a language feature in places it makes > sense. > > Now, since the Symfony Live conference, I've also been thinking about > something Fabien said about Symfony 2: "less magic." To clarify, the > idea is to design components so that they are explicit about what they > can do. As an example, instead of using __get() and __set(), using > explicit getters and setters; instead of using __call(), explicitly > defining methods; instead of having __call() proxy to helpers, having a > broker from which you pull them as needed. > > This has several implications: > > * Less need for explicit documentation. If you have behaviors relying > on the magic methods, developers *must* read the the documentation in > order to know how a class/component works. If everything is explicit, > the class is self-documenting, and you can get the information you > need from API docs, your IDE, etc. > > * Performance. Magic methods are reportedly less performant than using > straight method calls (more on this later). > > These are important factors, and the test-driven developer in me > particularly likes less magic; it's much easier to test when you have > explicit behaviors. > > That said, I decided to run some tests. These were the scenarios: > > * No Proxying: > * Defined "filter" method returns value > * Defined "__call" method returns value > * Defined "__invoke" method returns value > * Proxying: > * Defined "filter" method returns value > * Defined "__call" method proxies to "filter" > * Defined "__invoke" method proxies to "filter" > * Inverse proxying > * Defined "filter" method calls invoke (as "$this();") > * Defined "__invoke" method returns value > * call_user_func_array() > * Using array($obj, 'filter') callback > * Using __invoke() with no proxying > * Using __invoke() with proxying > > The results were quite interesting. Across the board, using __call() is > awful -- even if you simply return a value, it's on average 6x slower > than a straight method call; proxying to another method, however, > doesn't take significantly longer (so long as you're not using > call_user_func_array() internally, that is). > > __invoke(), however, turns out to be quite different. When not proxying, > it's actually *faster* than a method call (about 25% on average). > Proxing __invoke() to a method call or doing the inverse are roughly the > same speed -- about 2.5x slower than a straight method call (which, as > you'll note, is still over twice as fast as using __call()). > > What was *really* interesting, however, was using > call_user_func_array(), which is commonly used in filter chains and/or > brokers. __invoke(), with or without proxying, *always* was around 30% > *faster* than using an explicit array($obj, $method) callback. > > So, what does this tell me? > > * AVOID using __call(), particularly for often-used method calls. For > helper systems, it will always be better to use a helper broker and > command pattern. > > * For classes meant to be used primarily with filter chains or brokers, > DO use __invoke(), even if it simply proxies to another interface > method. > > So, based on the design goals and the performance benchmarking, I'm > going to recommend that Filter and Validator (we renamed Validate to > Validator) define an __invoke() method in addition to a more explicit > interface method, and that helpers also follow this pattern. > > -- Matthew Weier O'Phinney <[hidden email]> wrote > (on Wednesday, 31 March 2010, 10:21 AM -0400): >> Greetings, all -- >> >> Per the roadmap, one change we're planning on making is making plugin >> usage consistent across components: plugins will implement __invoke(), >> which makes them inherently callable. >> >> In our refactoring branch, I've done this now with Zend_Filter. However, >> it required a ton of refactoring on the unit tests (had to either call >> __invoke() directly, or $filter($value)), and it got me to thinking: >> >> Would it make sense to keep the original interfaces (filter, isValid, >> render, etc.), but also implement __invoke() -- having it proxy to the >> appropriate interface method? >> >> As an example: >> >> interface Filter >> { >> public function filter($value); >> public function __invoke($value); >> } >> >> class Digits implements Filter >> { >> public function filter($value) >> { >> // ... logic... >> } >> >> public function __invoke($value) >> { >> return $this->filter($value); >> } >> } >> >> The benefits: >> >> * Fewer changes necessary to existing unit tests >> * interface method names may be more descriptive of intent >> >> Problems: >> >> * Using __invoke() introduces overhead -- 2 method calls instead of one >> * Redundancy (two methods doing the same thing) >> * A few extra tests to write (to ensure __invoke works as expecte) >> >> After discussion with Ralph and Alex, I'm leaning towards having >> __invoke() as an _additional_ method, instead of replacing existing >> methods. >> >> Any thoughts or comments? >> >> -- >> Matthew Weier O'Phinney >> Project Lead | [hidden email] >> Zend Framework | http://framework.zend.com/ >> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc >> > > -- > Matthew Weier O'Phinney > Project Lead | [hidden email] > Zend Framework | http://framework.zend.com/ > PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc > |
|
Administrator
|
-- Dolf Schimmel <[hidden email]> wrote
(on Thursday, 01 April 2010, 04:48 PM +0200): > > Now, since the Symfony Live conference, I've also been thinking about > > something Fabien said about Symfony 2: "less magic." To clarify, the > > idea is to design components so that they are explicit about what they > > can do. As an example, instead of using __get() and __set(), using > > explicit getters and setters; instead of using __call(), explicitly > > defining methods; instead of having __call() proxy to helpers, having a > > broker from which you pull them as needed. > > Apparently the rumors on twitter were in place; the conference did > have a bad influence on you :D Umm... there are good ideas everywhere, and bad ones as well. Neither ZF nor SF have a monopoloy on either. > More seriously, I think this can in no way be compared with SF's > "magic" (does the fact that our Zend_Magic only is imaginative mean ZF > does at this moment not contain any magic?). I wasn't trying to explicitly link the two. In fact, the examples I gave were examples of applying Fabien's ideas to ZF. > Firstly, if you have a class called foo\Filter which contains an > __invoke() method, > that method should obviously filter. As long as it does that and only > that, there's no magic imho involved. I can see this POV. For somebody who doesn't know what the interface's purpose is, however, it's less clear. Let me point that out with your next examples: > Same goes for foo\Validator (its > __invoke() method is supposed to validate), and foo\Bootstrap (guess > what: __invoke() should bootstrap here). Validator defines two method types: validation, and error message retrieval. In the original interface, this is pretty clear: isValid() and getMessages(). If we change to __invoke() and getMessages(), the latter method is clear, but the former is not -- what exactly is happening here? Looking at the bootstrap, this is also a bit more unclear: right now, we have a bootstrap() method and a run() method: which one does __invoke() define? Resources might be a bit more clear: the init() method might make sense to instead map to __invoke() -- but the intent is less clear. init() at least implies that we're initializing something; what does __invoke() do? > If we can get it done consistently I'm pretty sure everybody will be > used to it within the first 5 seconds they look at a random piece of > code. I *do* want to see consistency. I just don't want to sacrifice end-user comprehension of the code. Our current usage of __call(), __get(), and __set() throughout the framework *does* make the code less easy to understand. I've had plenty of people indicate that they have had trouble understanding view helpers because they can't find the methods in the view object; I don't want the same thing happening with usage of __invoke(). > Secondly Symfony's magic is on an entirely different scale of > magnitude than the naming of one type of method. Compare > $random->method() generating an entire application for you with > $object() which exactly does what the name of the object implies. Please see my notes above. I wasn't talking about this type of magic. > Situations in which you have two methods doing exactly the same are > often/usually a result of willing to change, but also struggling with > inability to do so due to backwards compatibility constraints. > However, now that we're busy on something where potentially all BC may > be broken, I really don't think having two methods doing exactly the > same would be beneficial to anybody. In fact, it would increase the > amount of options to do something with (give or take) N! which is an > awful lot, without any added value. This is the primary argument I can see against having both an explicit method name defined and __invoke(), and it hits home with other complaints about the current state of ZF: understanding how ZF is difficult when there's more than one way to do things. I want to be clear that the primary reason for using __invoke() in the first place is for dynamic calls using call_user_func() and call_user_func_array(). While using __invoke() with these is faster than a traditional callback, it's still way slower than calling the method directly -- around 3 times slower. __invoke() simply provides a slight optimization in a pattern that we're likely to use many, many times throughout a given request. For single usage, though, would you rather do this: $value = $plugin($data); or: $value = $plugin->filter($data); I realize it's a bit contrived -- you might name the object to better describe its purpose. However, the latter more clearly explains what you're doing to the data: you're filtering it. Let's look at a validator: $test = $plugin($value); or: $test = $plugin->isValid($value); I actually think this one benefits a ton -- even with a well-named variable: $test = $validator($value); What is "$validator"? and what is it doing here? When we call the method, we know we're checking for validity; when we call it via __invoke(), we could be filtering it, validating it, pushing in a value... who knows? In my earlier post, I probably should have made it more clear: I see __invoke() as a way to more easily and efficiently define and execute *callbacks*, and hence my rationale that it makes sense to define both an explicit method as well as __invoke() for those class types that will be used with filter chains and/or brokers. > On Thu, Apr 1, 2010 at 3:07 PM, Matthew Weier O'Phinney > <[hidden email]> wrote: > > I've been following this thread as well as discussions on IRC with > > interest, as, honestly, I can completely buy into all arguments, which > > are, to sum things up: > > > > * Use only __invoke() > > * Use a method name specific to the interface > > * Use a standardized method name, such as "run" or "execute" > > * Use a combination of __invoke() and either a meaningful or > > standardized method name. > > > > I got to thinking about why I suggested "__invoke()" in the first place, > > and went back to the plugin architecture page: > > > > * http://framework.zend.com/wiki/display/ZFDEV2/Plugin+Architectures > > > > The original rationale was for using pubsub/filter chain systems. In > > such systems, you attach callbacks: > > > > $chain->attach($callback); > > > > If your objects implement __invoke(), $callback can simply be your > > object -- you're done. Otherwise, you also have to provide the method: > > > > $chain->attach($object, $method); > > > > Interestingly, in this document, I also recommended that for existing > > interfaces, __invoke() be added as an _additional_ method, and that it > > should proxy to the appropriate interface method. In this case, we'd > > simply be taking advantage of a language feature in places it makes > > sense. > > > > Now, since the Symfony Live conference, I've also been thinking about > > something Fabien said about Symfony 2: "less magic." To clarify, the > > idea is to design components so that they are explicit about what they > > can do. As an example, instead of using __get() and __set(), using > > explicit getters and setters; instead of using __call(), explicitly > > defining methods; instead of having __call() proxy to helpers, having a > > broker from which you pull them as needed. > > > > This has several implications: > > > > * Less need for explicit documentation. If you have behaviors relying > > on the magic methods, developers *must* read the the documentation in > > order to know how a class/component works. If everything is explicit, > > the class is self-documenting, and you can get the information you > > need from API docs, your IDE, etc. > > > > * Performance. Magic methods are reportedly less performant than using > > straight method calls (more on this later). > > > > These are important factors, and the test-driven developer in me > > particularly likes less magic; it's much easier to test when you have > > explicit behaviors. > > > > That said, I decided to run some tests. These were the scenarios: > > > > * No Proxying: > > * Defined "filter" method returns value > > * Defined "__call" method returns value > > * Defined "__invoke" method returns value > > * Proxying: > > * Defined "filter" method returns value > > * Defined "__call" method proxies to "filter" > > * Defined "__invoke" method proxies to "filter" > > * Inverse proxying > > * Defined "filter" method calls invoke (as "$this();") > > * Defined "__invoke" method returns value > > * call_user_func_array() > > * Using array($obj, 'filter') callback > > * Using __invoke() with no proxying > > * Using __invoke() with proxying > > > > The results were quite interesting. Across the board, using __call() is > > awful -- even if you simply return a value, it's on average 6x slower > > than a straight method call; proxying to another method, however, > > doesn't take significantly longer (so long as you're not using > > call_user_func_array() internally, that is). > > > > __invoke(), however, turns out to be quite different. When not proxying, > > it's actually *faster* than a method call (about 25% on average). > > Proxing __invoke() to a method call or doing the inverse are roughly the > > same speed -- about 2.5x slower than a straight method call (which, as > > you'll note, is still over twice as fast as using __call()). > > > > What was *really* interesting, however, was using > > call_user_func_array(), which is commonly used in filter chains and/or > > brokers. __invoke(), with or without proxying, *always* was around 30% > > *faster* than using an explicit array($obj, $method) callback. > > > > So, what does this tell me? > > > > * AVOID using __call(), particularly for often-used method calls. For > > helper systems, it will always be better to use a helper broker and > > command pattern. > > > > * For classes meant to be used primarily with filter chains or brokers, > > DO use __invoke(), even if it simply proxies to another interface > > method. > > > > So, based on the design goals and the performance benchmarking, I'm > > going to recommend that Filter and Validator (we renamed Validate to > > Validator) define an __invoke() method in addition to a more explicit > > interface method, and that helpers also follow this pattern. > > > > -- Matthew Weier O'Phinney <[hidden email]> wrote > > (on Wednesday, 31 March 2010, 10:21 AM -0400): > >> Greetings, all -- > >> > >> Per the roadmap, one change we're planning on making is making plugin > >> usage consistent across components: plugins will implement __invoke(), > >> which makes them inherently callable. > >> > >> In our refactoring branch, I've done this now with Zend_Filter. However, > >> it required a ton of refactoring on the unit tests (had to either call > >> __invoke() directly, or $filter($value)), and it got me to thinking: > >> > >> Would it make sense to keep the original interfaces (filter, isValid, > >> render, etc.), but also implement __invoke() -- having it proxy to the > >> appropriate interface method? > >> > >> As an example: > >> > >> interface Filter > >> { > >> public function filter($value); > >> public function __invoke($value); > >> } > >> > >> class Digits implements Filter > >> { > >> public function filter($value) > >> { > >> // ... logic... > >> } > >> > >> public function __invoke($value) > >> { > >> return $this->filter($value); > >> } > >> } > >> > >> The benefits: > >> > >> * Fewer changes necessary to existing unit tests > >> * interface method names may be more descriptive of intent > >> > >> Problems: > >> > >> * Using __invoke() introduces overhead -- 2 method calls instead of one > >> * Redundancy (two methods doing the same thing) > >> * A few extra tests to write (to ensure __invoke works as expecte) > >> > >> After discussion with Ralph and Alex, I'm leaning towards having > >> __invoke() as an _additional_ method, instead of replacing existing > >> methods. > >> > >> Any thoughts or comments? > >> > >> -- > >> Matthew Weier O'Phinney > >> Project Lead | [hidden email] > >> Zend Framework | http://framework.zend.com/ > >> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc > >> > > > > -- > > Matthew Weier O'Phinney > > Project Lead | [hidden email] > > Zend Framework | http://framework.zend.com/ > > PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc > > > -- Matthew Weier O'Phinney Project Lead | [hidden email] Zend Framework | http://framework.zend.com/ PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc |
|
Administrator
|
Ralph and I were just talking about this, and had a realization.
is_callable() returns true for objects that implement __invoke(). What this means is: even for filter chains and the like, we simply test to see if what was passed is callable. If not, we throw an exception or use another strategy. (In Phly_PubSub, if I get a non-callable first argument, I check for a second argument, which would be the method to call on the first argument.) As such, he and I are leaning towards no definition of __invoke() in the interface, but defining it in the abstract classes and/or concrete classes that implement the interface. This solves the issue of ensuring clear interfaces, while also solving the issue of using __invoke() as a general-purpose, consistent callback paradigm within the framework. Thoughts? -- Matthew Weier O'Phinney <[hidden email]> wrote (on Thursday, 01 April 2010, 11:43 AM -0400): > -- Dolf Schimmel <[hidden email]> wrote > (on Thursday, 01 April 2010, 04:48 PM +0200): > > > Now, since the Symfony Live conference, I've also been thinking about > > > something Fabien said about Symfony 2: "less magic." To clarify, the > > > idea is to design components so that they are explicit about what they > > > can do. As an example, instead of using __get() and __set(), using > > > explicit getters and setters; instead of using __call(), explicitly > > > defining methods; instead of having __call() proxy to helpers, having a > > > broker from which you pull them as needed. > > > > Apparently the rumors on twitter were in place; the conference did > > have a bad influence on you :D > > Umm... there are good ideas everywhere, and bad ones as well. Neither ZF > nor SF have a monopoloy on either. > > > More seriously, I think this can in no way be compared with SF's > > "magic" (does the fact that our Zend_Magic only is imaginative mean ZF > > does at this moment not contain any magic?). > > I wasn't trying to explicitly link the two. In fact, the examples I gave > were examples of applying Fabien's ideas to ZF. > > > Firstly, if you have a class called foo\Filter which contains an > > __invoke() method, > > that method should obviously filter. As long as it does that and only > > that, there's no magic imho involved. > > I can see this POV. For somebody who doesn't know what the interface's > purpose is, however, it's less clear. Let me point that out with your > next examples: > > > Same goes for foo\Validator (its > > __invoke() method is supposed to validate), and foo\Bootstrap (guess > > what: __invoke() should bootstrap here). > > Validator defines two method types: validation, and error message > retrieval. In the original interface, this is pretty clear: isValid() > and getMessages(). If we change to __invoke() and getMessages(), the > latter method is clear, but the former is not -- what exactly is > happening here? > > Looking at the bootstrap, this is also a bit more unclear: right now, we > have a bootstrap() method and a run() method: which one does __invoke() > define? Resources might be a bit more clear: the init() method might > make sense to instead map to __invoke() -- but the intent is less clear. > init() at least implies that we're initializing something; what does > __invoke() do? > > > If we can get it done consistently I'm pretty sure everybody will be > > used to it within the first 5 seconds they look at a random piece of > > code. > > I *do* want to see consistency. I just don't want to sacrifice end-user > comprehension of the code. Our current usage of __call(), __get(), and > __set() throughout the framework *does* make the code less easy to > understand. I've had plenty of people indicate that they have had > trouble understanding view helpers because they can't find the methods > in the view object; I don't want the same thing happening with usage of > __invoke(). > > > Secondly Symfony's magic is on an entirely different scale of > > magnitude than the naming of one type of method. Compare > > $random->method() generating an entire application for you with > > $object() which exactly does what the name of the object implies. > > Please see my notes above. I wasn't talking about this type of magic. > > > Situations in which you have two methods doing exactly the same are > > often/usually a result of willing to change, but also struggling with > > inability to do so due to backwards compatibility constraints. > > However, now that we're busy on something where potentially all BC may > > be broken, I really don't think having two methods doing exactly the > > same would be beneficial to anybody. In fact, it would increase the > > amount of options to do something with (give or take) N! which is an > > awful lot, without any added value. > > This is the primary argument I can see against having both an explicit > method name defined and __invoke(), and it hits home with other > complaints about the current state of ZF: understanding how ZF is > difficult when there's more than one way to do things. > > I want to be clear that the primary reason for using __invoke() in the > first place is for dynamic calls using call_user_func() and > call_user_func_array(). While using __invoke() with these is faster than > a traditional callback, it's still way slower than calling the method > directly -- around 3 times slower. __invoke() simply provides a slight > optimization in a pattern that we're likely to use many, many times > throughout a given request. > > For single usage, though, would you rather do this: > > $value = $plugin($data); > > or: > > $value = $plugin->filter($data); > > I realize it's a bit contrived -- you might name the object to better > describe its purpose. However, the latter more clearly explains what > you're doing to the data: you're filtering it. Let's look at a > validator: > > $test = $plugin($value); > > or: > > $test = $plugin->isValid($value); > > I actually think this one benefits a ton -- even with a well-named > variable: > > $test = $validator($value); > > What is "$validator"? and what is it doing here? When we call the > method, we know we're checking for validity; when we call it via > __invoke(), we could be filtering it, validating it, pushing in a > value... who knows? > > In my earlier post, I probably should have made it more clear: I see > __invoke() as a way to more easily and efficiently define and execute > *callbacks*, and hence my rationale that it makes sense to define both > an explicit method as well as __invoke() for those class types that will > be used with filter chains and/or brokers. > > > > On Thu, Apr 1, 2010 at 3:07 PM, Matthew Weier O'Phinney > > <[hidden email]> wrote: > > > I've been following this thread as well as discussions on IRC with > > > interest, as, honestly, I can completely buy into all arguments, which > > > are, to sum things up: > > > > > > * Use only __invoke() > > > * Use a method name specific to the interface > > > * Use a standardized method name, such as "run" or "execute" > > > * Use a combination of __invoke() and either a meaningful or > > > standardized method name. > > > > > > I got to thinking about why I suggested "__invoke()" in the first place, > > > and went back to the plugin architecture page: > > > > > > * http://framework.zend.com/wiki/display/ZFDEV2/Plugin+Architectures > > > > > > The original rationale was for using pubsub/filter chain systems. In > > > such systems, you attach callbacks: > > > > > > $chain->attach($callback); > > > > > > If your objects implement __invoke(), $callback can simply be your > > > object -- you're done. Otherwise, you also have to provide the method: > > > > > > $chain->attach($object, $method); > > > > > > Interestingly, in this document, I also recommended that for existing > > > interfaces, __invoke() be added as an _additional_ method, and that it > > > should proxy to the appropriate interface method. In this case, we'd > > > simply be taking advantage of a language feature in places it makes > > > sense. > > > > > > Now, since the Symfony Live conference, I've also been thinking about > > > something Fabien said about Symfony 2: "less magic." To clarify, the > > > idea is to design components so that they are explicit about what they > > > can do. As an example, instead of using __get() and __set(), using > > > explicit getters and setters; instead of using __call(), explicitly > > > defining methods; instead of having __call() proxy to helpers, having a > > > broker from which you pull them as needed. > > > > > > This has several implications: > > > > > > * Less need for explicit documentation. If you have behaviors relying > > > on the magic methods, developers *must* read the the documentation in > > > order to know how a class/component works. If everything is explicit, > > > the class is self-documenting, and you can get the information you > > > need from API docs, your IDE, etc. > > > > > > * Performance. Magic methods are reportedly less performant than using > > > straight method calls (more on this later). > > > > > > These are important factors, and the test-driven developer in me > > > particularly likes less magic; it's much easier to test when you have > > > explicit behaviors. > > > > > > That said, I decided to run some tests. These were the scenarios: > > > > > > * No Proxying: > > > * Defined "filter" method returns value > > > * Defined "__call" method returns value > > > * Defined "__invoke" method returns value > > > * Proxying: > > > * Defined "filter" method returns value > > > * Defined "__call" method proxies to "filter" > > > * Defined "__invoke" method proxies to "filter" > > > * Inverse proxying > > > * Defined "filter" method calls invoke (as "$this();") > > > * Defined "__invoke" method returns value > > > * call_user_func_array() > > > * Using array($obj, 'filter') callback > > > * Using __invoke() with no proxying > > > * Using __invoke() with proxying > > > > > > The results were quite interesting. Across the board, using __call() is > > > awful -- even if you simply return a value, it's on average 6x slower > > > than a straight method call; proxying to another method, however, > > > doesn't take significantly longer (so long as you're not using > > > call_user_func_array() internally, that is). > > > > > > __invoke(), however, turns out to be quite different. When not proxying, > > > it's actually *faster* than a method call (about 25% on average). > > > Proxing __invoke() to a method call or doing the inverse are roughly the > > > same speed -- about 2.5x slower than a straight method call (which, as > > > you'll note, is still over twice as fast as using __call()). > > > > > > What was *really* interesting, however, was using > > > call_user_func_array(), which is commonly used in filter chains and/or > > > brokers. __invoke(), with or without proxying, *always* was around 30% > > > *faster* than using an explicit array($obj, $method) callback. > > > > > > So, what does this tell me? > > > > > > * AVOID using __call(), particularly for often-used method calls. For > > > helper systems, it will always be better to use a helper broker and > > > command pattern. > > > > > > * For classes meant to be used primarily with filter chains or brokers, > > > DO use __invoke(), even if it simply proxies to another interface > > > method. > > > > > > So, based on the design goals and the performance benchmarking, I'm > > > going to recommend that Filter and Validator (we renamed Validate to > > > Validator) define an __invoke() method in addition to a more explicit > > > interface method, and that helpers also follow this pattern. > > > > > > -- Matthew Weier O'Phinney <[hidden email]> wrote > > > (on Wednesday, 31 March 2010, 10:21 AM -0400): > > >> Greetings, all -- > > >> > > >> Per the roadmap, one change we're planning on making is making plugin > > >> usage consistent across components: plugins will implement __invoke(), > > >> which makes them inherently callable. > > >> > > >> In our refactoring branch, I've done this now with Zend_Filter. However, > > >> it required a ton of refactoring on the unit tests (had to either call > > >> __invoke() directly, or $filter($value)), and it got me to thinking: > > >> > > >> Would it make sense to keep the original interfaces (filter, isValid, > > >> render, etc.), but also implement __invoke() -- having it proxy to the > > >> appropriate interface method? > > >> > > >> As an example: > > >> > > >> interface Filter > > >> { > > >> public function filter($value); > > >> public function __invoke($value); > > >> } > > >> > > >> class Digits implements Filter > > >> { > > >> public function filter($value) > > >> { > > >> // ... logic... > > >> } > > >> > > >> public function __invoke($value) > > >> { > > >> return $this->filter($value); > > >> } > > >> } > > >> > > >> The benefits: > > >> > > >> * Fewer changes necessary to existing unit tests > > >> * interface method names may be more descriptive of intent > > >> > > >> Problems: > > >> > > >> * Using __invoke() introduces overhead -- 2 method calls instead of one > > >> * Redundancy (two methods doing the same thing) > > >> * A few extra tests to write (to ensure __invoke works as expecte) > > >> > > >> After discussion with Ralph and Alex, I'm leaning towards having > > >> __invoke() as an _additional_ method, instead of replacing existing > > >> methods. > > >> > > >> Any thoughts or comments? > > >> > > >> -- > > >> Matthew Weier O'Phinney > > >> Project Lead | [hidden email] > > >> Zend Framework | http://framework.zend.com/ > > >> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc > > >> > > > > > > -- > > > Matthew Weier O'Phinney > > > Project Lead | [hidden email] > > > Zend Framework | http://framework.zend.com/ > > > PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc > > > > > > > -- > Matthew Weier O'Phinney > Project Lead | [hidden email] > Zend Framework | http://framework.zend.com/ > PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc > -- Matthew Weier O'Phinney Project Lead | [hidden email] Zend Framework | http://framework.zend.com/ PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc |
|
I probably only have about 1/2 cent worth to give - but, I think that is a very reasonable solution that should satisfy good programming practices. I really like the idea of leaving the interfaces __invoke() free... :-)
Thanks, Tommy Smith
On Thu, Apr 1, 2010 at 11:19 AM, Matthew Weier O'Phinney <[hidden email]> wrote: Ralph and I were just talking about this, and had a realization. |
|
In reply to this post by weierophinney
Now +1 from me, exactly as I would like to have it.
Just to mention - __invoke is very nice for array_map, array_filter and all those other functions accepting callables. So now you don't need to use array_umap, but can just pass $filter object. Same with validator: you can use array_filter to get all array entries which are valid/invalid... A lot of nice use cases.
But, as we (almost) agreed here - __invoke is better off in the implementation side, rather than in the interface. Just makes interface actually mean something. -- Juozas Kaziukėnas ([hidden email]) My website - JuoKaz (http://www.juokaz.com) On Thu, Apr 1, 2010 at 6:19 PM, Matthew Weier O'Phinney <[hidden email]> wrote: Ralph and I were just talking about this, and had a realization. |
|
>From a design point of view we can make an example:
<?php interface CapableOfSomething { public function doSomething(MyClass $value); public function __invoke(MyClass $value); } When there is a type hint on the parameter, it is clearer that there is a duplication in place: the methods accomplish the same thing, and are coupled to change together. I agree that removing the analogue to doSomething() makes the code less readable when using a single object, so probably __invoke() has to go. Placing an __invoke() method only on implementations it's useful to simplify the chaining, but there is no way to enforce its implementation (maybe a separate interface Chainable?) -- Giorgio Sironi Piccolo Principe & Web Engineer http://giorgiosironi.blogspot.com http://twitter.com/giorgiosironi |
| Powered by Nabble | Edit this page |
