Quantcast

Plugin implementations

classic Classic list List threaded Threaded
29 messages Options
12
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Plugin implementations

weierophinney
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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

denniswinter
+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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Dmitry Dulepov
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/

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Benjamin Eberlei-2
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?

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Renan de Lima
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
>

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Lukas Köll
-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
>>

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Juozas
-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
>>>
>
>

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Thomas Weidner
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
>>

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

weierophinney
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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

weierophinney
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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Juozas
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
>

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Eric Clemmons
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
>>

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Marc Bennewitz (private)
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
>>>
>
>

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

weierophinney
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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Dolf Schimmel
> 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
>

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

weierophinney
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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

weierophinney
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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Tommy Smith
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.

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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Juozas
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.

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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Plugin implementations

Giorgio Sironi
>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

12
Loading...