Hi everyone,
PS: Everyone is free to fork my branch and submit PR to it!
Motivation
Event manager has always been, with service manager, the slowest parts of ZF2, but the most widely used one internally. It was therefore highly important to make them faster.
Also, the event manager was a complex piece of software with a lot of obscure features, a strange shared manager concept and complex signatures.
Breaking changes
Massive performance improvements
I copy-paste some of the benchmarks I've made (the PHP7 build was an old one, so expect at least 10-15% improvments on PHP7):
ZF2 EVM
PHP 5.6
Bench\EventManagerBenchmark
Method Name Iterations Average Time Ops/second
------------------ ------------ -------------- -------------
attach20Listeners : [1,000 ] [0.0005693485737] [1,756.39326]
trigger20Listeners: [1,000 ] [0.0010452888012] [956.67341]
PHP 7
Bench\EventManagerBenchmark
Method Name Iterations Average Time Ops/second
------------------ ------------ -------------- -------------
attach20Listeners : [1,000 ] [0.0000875046253] [11,427.96734]
trigger20Listeners: [1,000 ] [0.0001518340111] [6,586.13965]
ZF3 EVM
PHP 5.6
Bench\EventManagerBenchmark
Method Name Iterations Average Time Ops/second
------------------ ------------ -------------- -------------
attach20Listeners : [1,000 ] [0.0001252155304] [7,986.22980]
trigger20Listeners: [1,000 ] [0.0002773256302] [3,605.86939]
PHP 7
Bench\EventManagerBenchmark
Method Name Iterations Average Time Ops/second
------------------ ------------ -------------- -------------
attach20Listeners : [1,000 ] [0.0000128707886] [77,695.31713]
trigger20Listeners: [1,000 ] [0.0000429670811] [23,273.63123]
No more shared event manager
I've explain myself why I think the shared event manager was a bad idea (although @ocramius tried to show me the contrary, I think the event manager should stay simple as introduced in this PR).
As I used EVM in my app, I realized I ONLY used the Shared event manager. But the SEM is SLOW AS HELL and complex (because of identifiers, expensive merging...). I think that in most use cases, people should have a global EVM used throughout their applications, and have more precise event name.
Simplifications
Previously, trigger and triggerUntil were very complex. Depending on the order and types of parameters, you could use it in a LOT of different ways. Now everything has been enforced in interfaces, so there is one possible use case, which make things clearer and more efficient (less checks).
Event no longer has name
I've simplified the EventInterface
so that it no longer has a getName
method. I'm not against reverting it, but to me, we should trigger event like this:
$evm->trigger('event.name', $eventObject);
As an event object could be used for various event names, I think it makes more sense to be used like this (and avoid to re-add logic to check if the first parameter is a string or object... that kind of things).
propagationIsStopped
has been renamed to isPropagationStopped
.
Lazy listeners
One main issue with ZF2 was the inability to have lazy listeners. This means taht when you attach listeners, they are created, with all their dependencies, on every requests, even if you don't need them.
From my app, this can add a non-negligible overhead, and solutions like proxies are annoying to setup.
Some example usage:
<?php
$eventManager = new EventManager();
$eventManager->attach('foo', function() {}, $priority);
$eventManager->trigger('foo', new Event());
$eventManager->triggerUntil('foo', new Event(), function() {});
$eventManager->attach('*', function() {});
$listener = $eventManager->attach('foo', function() {});
$eventManager->detach('foo', $listener);
$eventManager->detach('foo');
class MyListenerAggregate implements ListenerAggregateInterface
{
public static function attachAggregate(EventManager $evm)
{
$evm->attach('foo', [static::class, 'myMethod']);
}
public function myMethod(Event $event)
{
}
}
$evm = $serviceLocator->get(EventManager::class);
MyListenerAggregate::attachAggregate($evm);
?>