zendframework/zf2 — pull request #5491 by devosc (2013-11-17 07:53:43+00:00)
Title: [ZF3] EventManager

The primary goal of this changeset is to have a unified interface between the SharedEventManager and EventManager, which can be achieved by having single argument API methods. And consequently would allow replacing the SharedEventManager with an EventManager instead.
```
$em = new Zend\EventManager\EventManager();

$em2 = new Zend\EventManager\EventManager();

$em->attach($em2)
```
Now that the EventManager is itself a Listener and implements the EventManagerInterface; when an EM is attached to another EM, instead of immediately pulling in all of the listeners from the EM it is instead internally assigned to a list of shared listeners. Conceptually this means multiple EMs can be assigned to a single EM; and when getListeners() is called on that EM it will also pull in the listeners from the EMs in its shared listeners list.

I also tried to keep the current notion of being able to assign listeners to an EM without specifying targets, and decided that if no target was specified then it should default to the wildcard target name '\*'. In either case, if the EM has been set with targets these are used regardless of what targets the listener may of been configured with. Here, the term *target* replaces *identifiers*; I switched to it because it seemed easier to grasp and also I wanted to check that object comparisons could be achieved (which may of already been possible).

All listeners implement the ListenerInterface, which contains, the event name(s), priority, target(s) and an invoke method. The CallbackListenerInterface extends the ListenerInterface by allowing a callback method to be assigned which the invoke method would call.

Since the EventManager is also a listener (basically we're only interested in the target(s) and invoke method), the EventManager's invoke method is a proxy to its trigger method. The trigger method then iterates through it listeners invoking the Event object with each listener
```
foreach($listeners as $listener) {
if ($event($listener)) {
break;
}
}
```
The invoke method of the event object actually then invokes the listener
```
public function __invoke($listener)
{
$response = $listener($this);

//$this->eventResponses[] = $response;

if ($this->stopPropagation) {
return true;
}

if ($this->callback) {
return (bool) call_user_func($this->callback, $this, $listener, $response);
}

return false;
}
```

This allows the event object to determine how to handle responses from the listener and how to determine if its propagation should stop. At which point the Event object is becoming more than just a container, it can also manage its own propagation (e.g how should it handle its responses).

Its my impression that having single argument API methods allows it to be a lot more flexible, in the sense it doesn't care how the single argument objects are created as long as they implement the respective interfaces. It also allows for various strategies to be used in controlling the propagation of an event, e.g. should both isPropagationStopped and the callback be true?

In this initial changeset, I've (hopefully) only been concerned with the core API methods and functionality. Other methods and functionality (e.g. get listeners by identifier) were taken out, but can be (selectively) put back.

I used the bench-evm.php by @bakura10 to get a rough comparison as to any performance improvements gained.
```
------
ZF2
------
Attach 100 listeners: time=0.001995, mem=456320
Triggers 1000 events: time=0.056950, mem=226176
Attach 100 shared listeners: time=0.001253, mem=448840
Triggers 1000 events: time=0.207061, mem=240120

Total: time=0.267477, mem=1572864

---------------------
Proposed ZF3
---------------------
Attach 100 listeners: time=0.001786, mem=253608
Triggers 1000 events: time=0.002872, mem=212536
Attach 100 shared listeners: time=0.002086, mem=211360
Triggers 1000 events: time=0.200215, mem=136984

Total: time=0.207832, mem=524288
```