Events RFC

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

Events RFC

basz
Hello,


I ran into a situation where perhaps the event managers could simplify things a little more.

I need an event to be send to an object that implements LocatorAware.

$events->attach('Application\Controller\PaypalController', 'transaction.completed', array($locator->get('Application\Service\StoreService'), 'processTransaction'), 100);

This works (as the StoreService is created by the DI Locator), a locator is available within the StoreService instance.

However this has one drawback; The StoreService instance is created when the events is attached.

So I came up with the following...

$events->attach('Application\Controller\PaypalController', 'transaction.completed', function($e) use ($locator) {
            $service = $locator->get('Application\Service\StoreService');
            $service->processTransaction($e);
        }, 100);

The effect here is that the instanciation is delayed until the event is actually triggered...

I was wondering if the $events->attach method could be modified to handle this somehow automatically. It could check to see if an class signature is available within the locator and if so get it from there.


However I am not sure if this is a good idea at all?

Not sure what the API should look like either... Does not feel right to mess with the PHP callback signature of array(obj, method)

perhaps... this would leave room for other locator-like services. (No idea which or what)
$events->attach('Application\Controller\PaypalController', 'transaction.completed', array('locator', 'Application\Service\StoreService', 'processTransaction'), 100);

so RFC

thanks,
Bas
Reply | Threaded
Open this post in threaded view
|

Re: Events RFC

Wil Moore III
$events->attach('Application\Controller\PaypalController', 'transaction.completed', function($e) use ($locator) {
           $service = $locator->get('Application\Service\StoreService');
           $service->processTransaction($e);
       }, 100);

The effect here is that the instanciation is delayed until the event is actually triggered...

I was wondering if the $events->attach method could be modified to handle this somehow automatically. It could check to see if an class signature is available within the locator and if so get it from there.

What you are asking for works OOTB. Your callback is wrapped as a CallbackHandler object:

Your callback is only invoked after the CallbackHandler instance is pulled from the events array:


--
Wil Moore III

Best Practices for Working with Open-Source Developers
http://www.faqs.org/docs/artu/ch19s02.html

Why is Bottom-posting better than Top-posting:
http://www.caliburn.nl/topposting.html

DO NOT TOP-POST and DO trim your replies:
http://linux.sgms-centre.com/misc/netiquette.php#toppost
Reply | Threaded
Open this post in threaded view
|

Re: Events RFC

basz
On 10 apr. 2012, at 19:08, Wil Moore III wrote:

$events->attach('Application\Controller\PaypalController', 'transaction.completed', function($e) use ($locator) {
           $service = $locator->get('Application\Service\StoreService');
           $service->processTransaction($e);
       }, 100);

The effect here is that the instanciation is delayed until the event is actually triggered...

I was wondering if the $events->attach method could be modified to handle this somehow automatically. It could check to see if an class signature is available within the locator and if so get it from there.

What you are asking for works OOTB. Your callback is wrapped as a CallbackHandler object:

Your callback is only invoked after the CallbackHandler instance is pulled from the events array:


Are you sure?

What I coded in the example is working yes... The StoreService class is instantiated 'lazy' when the event is triggered (not at attach time!, which is good). The point is  I coded the closure... Wouldn't we want that anonymous function abstracted away into the event handling class.

I would like to see my event-'binding' setup to be as clean as possible, concentrating on "when event X comes by pass it to Y". Additionally I would like to see Y being instantiated when events are actually passed. The DI Locator is very useful for that.

What I am suggesting rephrased :

The attach method takes an PHP-callback, and as such any instances fed into the array notation must be created before one binds event listeners to them (static calls not taken into account here). 

Wouldn't it be useful if the event class is able to attach event to listeners stored in the locator?

The DI locator is a central piece to ZF2 and that might validate complicating the events attaching code a little? Perhaps, somehow, via a modified PHP callback notation?

again just looking for opinions... 




bushbaby
bushbaby multimedia 
ontwerp & uitvoering
[hidden email] | +31(0)6 2897 7426 | kvk 34125878

Reply | Threaded
Open this post in threaded view
|

Re: Events RFC

Wil Moore III
What I coded in the example is working yes... The StoreService class is instantiated 'lazy' when the event is triggered (not at attach time!, which is good). The point is  I coded the closure... Wouldn't we want that anonymous function abstracted away into the event handling class.

So, the actual code you took issue with is the following:
$locator->get('Application\Service\StoreService')

I would like to see my event-'binding' setup to be as clean as possible, concentrating on "when event X comes by pass it to Y". Additionally I would like to see Y being instantiated when events are actually passed. The DI Locator is very useful for that.

The way you wrote your original post made it look like you had an issue with the event manager. In reality it looks like your request is more toward the service locator (instance manager).

I have to believe that the way it works makes sense. Writing a closure to signify that you want lazy loading seems fair since it is explicit (no magic).

I think the current implementation is sound; the real gripe is that PHP doesn't have Ruby block syntax :)
--
Wil Moore III

Best Practices for Working with Open-Source Developers
http://www.faqs.org/docs/artu/ch19s02.html

Why is Bottom-posting better than Top-posting:
http://www.caliburn.nl/topposting.html

DO NOT TOP-POST and DO trim your replies:
http://linux.sgms-centre.com/misc/netiquette.php#toppost
Reply | Threaded
Open this post in threaded view
|

Re: Events RFC

basz

On 10 apr. 2012, at 23:07, Wil Moore III wrote:

> What I coded in the example is working yes... The StoreService class is instantiated 'lazy' when the event is triggered (not at attach time!, which is good). The point is  I coded the closure... Wouldn't we want that anonymous function abstracted away into the event handling class.
>
> So, the actual code you took issue with is the following:
> $locator->get('Application\Service\StoreService')
>
> I would like to see my event-'binding' setup to be as clean as possible, concentrating on "when event X comes by pass it to Y". Additionally I would like to see Y being instantiated when events are actually passed. The DI Locator is very useful for that.
>
> The way you wrote your original post made it look like you had an issue with the event manager. In reality it looks like your request is more toward the service locator (instance manager).
> I have to believe that the way it works makes sense.

jip, events work great and as expected (so far ;-) )

> Writing a closure to signify that you want lazy loading seems fair since it is explicit (no magic).

good point.

still; Wouldn't it be a useful addition if the event class is able to attach events to listeners stored in the locator?

> I think the current implementation is sound; the real gripe is that PHP doesn't have Ruby block syntax :)

Luckily I don't do Ruby...



bushbaby multimedia ontwerp & uitvoering
[hidden email] | +31(0)6 2897 7426 | kvk 34125878

Reply | Threaded
Open this post in threaded view
|

Re: Events RFC

Wil Moore III
Luckily I don't do Ruby...

Haha…now you're just being mean :)
--
Wil Moore III

Best Practices for Working with Open-Source Developers
http://www.faqs.org/docs/artu/ch19s02.html

Why is Bottom-posting better than Top-posting:
http://www.caliburn.nl/topposting.html

DO NOT TOP-POST and DO trim your replies:
http://linux.sgms-centre.com/misc/netiquette.php#toppost
Reply | Threaded
Open this post in threaded view
|

Re: Events RFC

weierophinney
Administrator
In reply to this post by basz
-- Bas Kamer <[hidden email]> wrote
(on Tuesday, 10 April 2012, 10:24 PM +0200):

> On 10 apr. 2012, at 19:08, Wil Moore III wrote:
>
>
>         $events->attach('Application\Controller\PaypalController',
>         'transaction.completed', function($e) use ($locator) {
>                    $service = $locator->get('Application\Service\
>         StoreService');
>                    $service->processTransaction($e);
>                }, 100);
>
>         The effect here is that the instanciation is delayed until the event is
>         actually triggered...
>
>         I was wondering if the $events->attach method could be modified to
>         handle this somehow automatically. It could check to see if an class
>         signature is available within the locator and if so get it from there.
>
>
>     What you are asking for works OOTB. Your callback is wrapped as a
>     CallbackHandler object:
>     https://github.com/zendframework/zf2/blob/master/library/Zend/EventManager/
>     EventManager.php#L292
>
>     Your callback is only invoked after the CallbackHandler instance is pulled
>     from the events array:
>     https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/
>     CallbackHandler.php#L162
>
>
>
> Are you sure?
>
> What I coded in the example is working yes... The StoreService class is
> instantiated 'lazy' when the event is triggered (not at attach time!, which is
> good). The point is  I coded the closure... Wouldn't we want that anonymous
> function abstracted away into the event handling class.

No. By insisting that anything attaching to an event is callable
already, we (a) simplify usage (only one pattern to learn), (b) simplify
the code itself (the callback handler is relatively trivial at this
time), and (c) allow usage of the full breadth of PHP's callbacks
(closures are only _one_ type of callback).

> I would like to see my event-'binding' setup to be as clean as possible,
> concentrating on "when event X comes by pass it to Y".

This is what it does already.

> Additionally I would like to see Y being instantiated when events are
> actually passed.

I originally had code in the CallbackHandler to make this possible. In
doing some profiling, I discovered that it was providing a _lot_ of
overhead -- basically, we had to do reflection anytime there was an
array callback to see if (a) we had an object for the first item, and (b)
if not, if the call was a static call, or a dynamic call. Considering
that you may have many dozens or hundreds of listeners over the course
of your request, this was a bottleneck. Additionally, it volated the
principles I outlined above -- it made usage more complex (you could
rely on dynamic instantiation vs explicit instantiation).

If you want dynamic instantiation, then the appropriate approach is
exactly what you were doing: pass a closure, and instantiate your
"listener" object within it.

> The DI Locator is very useful for that.

Absolutely not. The EM should _not_ have ties to the DI system, or the
InstanceManager (if we end up approving that). It should be as
lightweight as possible, with as few dependencies as possible.
Using a locator introduces complexity and additional usage patterns that
will only confuse people.


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

Re: Events RFC

basz
Okay,

Thanks all for the feedback! It's really helpful in a number of ways as I am starting with ZF2. This pingponging of arguments is really useful to get a feeling for the system as a whole and improves coding skills... Let me know when I ask to much questions or annoy the h*ell out of anyone.

For now I surrender...



On 11 apr. 2012, at 19:26, Matthew Weier O'Phinney wrote:

> -- Bas Kamer <[hidden email]> wrote
> (on Tuesday, 10 April 2012, 10:24 PM +0200):
>> On 10 apr. 2012, at 19:08, Wil Moore III wrote:
>>
>>
>>        $events->attach('Application\Controller\PaypalController',
>>        'transaction.completed', function($e) use ($locator) {
>>                   $service = $locator->get('Application\Service\
>>        StoreService');
>>                   $service->processTransaction($e);
>>               }, 100);
>>
>>        The effect here is that the instanciation is delayed until the event is
>>        actually triggered...
>>
>>        I was wondering if the $events->attach method could be modified to
>>        handle this somehow automatically. It could check to see if an class
>>        signature is available within the locator and if so get it from there.
>>
>>
>>    What you are asking for works OOTB. Your callback is wrapped as a
>>    CallbackHandler object:
>>    https://github.com/zendframework/zf2/blob/master/library/Zend/EventManager/
>>    EventManager.php#L292
>>
>>    Your callback is only invoked after the CallbackHandler instance is pulled
>>    from the events array:
>>    https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/
>>    CallbackHandler.php#L162
>>
>>
>>
>> Are you sure?
>>
>> What I coded in the example is working yes... The StoreService class is
>> instantiated 'lazy' when the event is triggered (not at attach time!, which is
>> good). The point is  I coded the closure... Wouldn't we want that anonymous
>> function abstracted away into the event handling class.
>
> No. By insisting that anything attaching to an event is callable
> already, we (a) simplify usage (only one pattern to learn), (b) simplify
> the code itself (the callback handler is relatively trivial at this
> time), and (c) allow usage of the full breadth of PHP's callbacks
> (closures are only _one_ type of callback).
>
>> I would like to see my event-'binding' setup to be as clean as possible,
>> concentrating on "when event X comes by pass it to Y".
>
> This is what it does already.
>
>> Additionally I would like to see Y being instantiated when events are
>> actually passed.
>
> I originally had code in the CallbackHandler to make this possible. In
> doing some profiling, I discovered that it was providing a _lot_ of
> overhead -- basically, we had to do reflection anytime there was an
> array callback to see if (a) we had an object for the first item, and (b)
> if not, if the call was a static call, or a dynamic call. Considering
> that you may have many dozens or hundreds of listeners over the course
> of your request, this was a bottleneck. Additionally, it volated the
> principles I outlined above -- it made usage more complex (you could
> rely on dynamic instantiation vs explicit instantiation).
>
> If you want dynamic instantiation, then the appropriate approach is
> exactly what you were doing: pass a closure, and instantiate your
> "listener" object within it.
>
>> The DI Locator is very useful for that.
>
> Absolutely not. The EM should _not_ have ties to the DI system, or the
> InstanceManager (if we end up approving that). It should be as
> lightweight as possible, with as few dependencies as possible.
> Using a locator introduces complexity and additional usage patterns that
> will only confuse people.
>
>
> --
> 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
|

Re: Events RFC

Wil Moore III
Thanks all for the feedback! It's really helpful in a number of ways as I am starting with ZF2.
 
Actually, by asking questions like these, you potentially help people coming by later to find answers to more esoteric questions. It really helps people that haven't followed the development of a component closely to understand some of the design decisions. Makes everyone better…slowly but surely :)

--
Wil Moore III

Best Practices for Working with Open-Source Developers
http://www.faqs.org/docs/artu/ch19s02.html

Why is Bottom-posting better than Top-posting:
http://www.caliburn.nl/topposting.html

DO NOT TOP-POST and DO trim your replies:
http://linux.sgms-centre.com/misc/netiquette.php#toppost
Reply | Threaded
Open this post in threaded view
|

Re: Events RFC

Jurian Sluiman
In reply to this post by basz
CONTENTS DELETED
The author has deleted this message.
Reply | Threaded
Open this post in threaded view
|

Re: Events RFC

Wil Moore III
closures all over the place...

For an example of "closures" all over the place, see a Sinatra application file:

get '/hi' do
  "Hello World!"
end

Granted, blocks aren't really closures in the purest sense: http://innig.net/software/ruby/closures-in-ruby. That being said, here is what the equivalent would look like in PHP just to get an idea:

get('/hi', function(){
  return "Hello World!"
});

To be honest, the above is generous. Most likely, you won't want "get" to be in the global namespace so you'd likely call get as an instance method:

$app->get('/hi', function(){
  return "Hello World!"
});

Configuration via closures or blocks is quite natural. If you aren't used to seeing this, it takes a little time to get used to, but it isn't a bad thing. The only downside with PHP is that it is much more verbose and looks a little dirty but if you squint a bit you can almost forget about it. Oh well, it's what we are using, so yeah, there's that…

At the end of the day, the lambda/closure syntax is the right solution for the problem given the language facilities provided.
--
Wil Moore III

Best Practices for Working with Open-Source Developers
http://www.faqs.org/docs/artu/ch19s02.html

Why is Bottom-posting better than Top-posting:
http://www.caliburn.nl/topposting.html

DO NOT TOP-POST and DO trim your replies:
http://linux.sgms-centre.com/misc/netiquette.php#toppost
Reply | Threaded
Open this post in threaded view
|

Re: Events RFC

weierophinney
Administrator
In reply to this post by Jurian Sluiman
-- Jurian Sluiman <[hidden email]> wrote
(on Wednesday, 11 April 2012, 11:17 PM +0200):

> 2012/4/11 Bas Kamer <[hidden email]>
>
>     Okay,
>
>     Thanks all for the feedback! It's really helpful in a number of ways as I
>     am starting with ZF2. This pingponging of arguments is really useful to get
>     a feeling for the system as a whole and improves coding skills... Let me
>     know when I ask to much questions or annoy the h*ell out of anyone.
>
>     For now I surrender...
>
>
> I agree here with Matthew and Will when it comes down to explicitness and
> simpler framework code. However, a framework is built to solve common and
> generic problems. In case of an event manager, a common case *will be* the lazy
> loading of listeners as not all events are triggered during the process. I
> think especially in application code this will happen (and not framework code
> per se).
>
> At this time the case is "closed" by two statements: 1.It creates magic,
> difficult to manage code with performance problems (+1 for me) and 2. a
> coupling of EVM with DI is undesired (+1 for me too). I think it is interesting
> to look at this problem without thinking about implementation details yet.
> So: Is it useful to provide framework code for lazy loading of listeners? When
> this might become a common pattern, closures all over the place "just" to
> prevent listeners for lazy loading increases boilerplate code with the only
> reason to mimic lazy loading.

One thing I do is consider when I might actually _need_ a listener. As
an example, if a particular listener should only ever respond if code
from a certain module is executed, I only register it when I know that
the module is active (see Rob Allen's excellent post on that subject:
http://akrabat.com/zend-framework-2/module-specific-bootstrapping-in-zf2/).

Using approaches like these allow you to get both the benefits of
lazy-loading as well as slimming down the execution runtime to exactly
what's needed for any given request.

Now, that said: if folks want a locator-enabled EventManager, it's
pretty easy to create one. With a change I have in the Pull Request
queue, it'll even be easy to substitute it for the default EventManager
when desired. However, it can be done _separately_ from the default
EventManager; it doesn't have to be built _into_ it.

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