Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

[ZF3] Clean event manager #5399

Closed
wants to merge 62 commits into from
@bakura10

Hi everyone,

This is a preliminary work for a clean-up EVM for ZF3. Event manager was pretty well done so this is mostly cleanup, optimizations, merging some interfaces, removing deprecated code.

I'd like your feedback on some choices I've made.

  • The StaticEventManager was removed as it was deprecated just after ZF2 release in favour of SharedEventManager.
  • The GlobalEventManager has been removed (I'm not sure about it, this class looks a bit like a StaticEventManager but has not been marked as deprecated... If needed I can make it back).
  • A lot of PHPDoc fixing, and some code were updated to respect interface contract.
  • Methods have been reordered to group more logically methods.
  • Use callable type hint where possible.
  • unsetSharedEventManager has been removed in favor of setSharedEventManager accepting null. The problem with unsetSharedEventManager is that it set it as "false" (and was considering it different from null in some tests, making it hard to follow).
  • propagationIsStopped has been renamed to isPropagationStopped (and all the code that rely on it has been updated).
  • trigger and triggerUntil are complicated methods. They accept a lot of parameters and the behavior changes depending on the order parameters are passed. To add to the complexity, trigger accepted a last parameter to emulate triggerUntil. To simplify the signature, no callback can be added to trigger now. You should use triggerUntil to that. It makes the code more explicit and makes the implementation simpler.
  • For the same idea, the "attach" method no longer accepts a ListenerAggregate. You must use the attachAggregate method for that. This allow to avoid to test instance of against ListenerAggregate whenever we attach something. We can now also type hint for callable in the attach method which avoids another test.

All tests have been updated.

TODO:

  • Backport the "Filter" feature (is this needed or was it something from ZF2 that was never used? I can't find any reference about it in the doc, and it's not used anywhere in the framework). EDIT: after some discussion with @weierophinney , he told me it was something that was initially planed to be used inside the framework, but finally failed to what it was done. So I'll don't back port it until someone tell me to do so.
  • Write proper documentation for event manager component
  • Feedback ?
library/Zend/EventManager/Event.php
@@ -103,7 +125,24 @@ public function setParams($params)
}
$this->params = $params;
- return $this;
+ }
+
+ /**
+ * Set an individual parameter to a value
+ *
+ * @param string|int $name
+ * @param mixed $value
+ * @return Event

no return here ? or use @return void instead ? or @return self instead @return CurrentClassName

@bakura10
bakura10 added a note

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Thinkscape Thinkscape commented on the diff
library/Zend/EventManager/Event.php
@@ -64,9 +64,15 @@ public function __construct($name = null, $target = null, $params = null)
}
/**
- * Get event name
- *
- * @return string
+ * {@inheritDoc}

@inheritDoc makes code less readable. There's nothing wrong with having full signature description, even if it's copy-pasted from interface of parent class.

@Ocramius Collaborator

Copy pasted = more stuff to maintain.

Why is {@inheritDoc} even used here? You only ever use this annotation when you're extending a parent docblock. If you're not adding or modifying the parent description, there's no need to include {@inheritDoc} - it's naturally assumed you're inheriting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Thinkscape

I love the idea of finally having callable everywhere... leave it up to php to type-check and validate callables, while having clear code method signatures and less probing/casting/converting :+1: Luckily the callable is unified and covers lambdas, strings and 2-element arrays pointing to methods.

We can also play with __invoke() method and instanceof if we want to cover aggregates.

I've created a simple experiment: http://3v4l.org/UCsG3

@bakura10

Unfortunatley after having benchmark it a bit, it's still as slow as in ZF2 :(.

I'm not sure about the attach thing. In this cleaning I've separated attach and attachAggregate, as it allows us to have cleaner type hint. Attach only accepts callable, while attachAggregate only accepts ListenerAggregateInterface.

But yeah, Callable typehnt simplifies a lot of things and will make ZF code much cleaner.

@Thinkscape

Unfortunatley after having benchmark it a bit, it's still as slow as in ZF2 :(.

It's not possible :-) Less sanitization and branching has to make it faster. Dumping CallbackHandle must also have helped. I've also seen that you haven't touched trigger*(). It currently supports many different calling styles, different arguments and has some branching inside. Depending on the type of benchmarks you make (i.e. lot of listeners vs. lot of triggers vs. lot of nesting) you'd get different results.

Anyways, @weierophinney wrote a handsome component and lack of monumental speed increase proves it's well thought out :-)

@Thinkscape

For benchmarks I'd advise the following:

  • large number of listeners (tests attach* methods and event loop)
  • large number of trigger calls w/ shortcircuits (tests trigger speed and loop)
  • some nesting - listeners triggering events
@bakura10

"It currently supports many different calling styles, different arguments and has some branching inside"

Exactly. I'd like to simplify this (to be honest, whenever I trigger a listener I need to go to this method to see the order), but I'm not sure to what extent.

In the benchmark I've made (attach 10 listeners and 10 listeners to the shared manager, and trigger the event 1000 times), it took nearly 1 second. Most of the time is spent on fetching the listeners from the shared event manager. In fact, shared event manager is what makes it slow.

@bakura10

But at the same time, the SharedEventManager is the awesome part of the thing, but I really do not know how to optimize it further :/.

@bakura10

@Thinkscape , we cannot remove CallbackHandler. I tried yesterday but I failed, mostly because we also store some metadata with the CallbackHandler (like the event name and the priority). To remove the CallbakcHandler, we will likely need some refactoring on the PriortiyQueue impl to allow merging.

@marc-mabe

@bakura10 :+1:

As @Thinkscape already noted you did'nt touched the trigger* methods. I think they should be more clear to have a fixed semantic.

You need to check callable of Class::staticMethod on the callable of triggerUntil/triggerListeners as you did within the CallackHandler

@bakura10

@marc-mabe : yes, I know about the trigger. I just didn't want to mess it up yet. We'll need a discussion about that :).

About the callable thing, @weierophinney noted an interesting thing. In fact, it works even with "class::staticMethod" ONLY if you use the call_user_func function. But does not using $callable(...). :php_palmface:.

We may need some benchmark (using CallbackHandler imply create one more object, more function calls and more conditions).

@marc-mabe

@bakura10 You could do the same as within CallbackHandler. if string containing :: then explode into an array and the array can be called directly.

@devosc
@bakura10

@devosc : here is the place I have not been able to remove it: https://github.com/bakura10/zf2/blob/2499d5d3b74803d87f5280a721369966a3da6ba3/library/Zend/EventManager/EventManager.php#L435

I think it could be solved by adding a "merge" method to the custom implementation of SplPriorityQueue that ZF2 provides. Thoughts @weierophinney ?

@weierophinney

@bakura10 That sounds reasonable -- give it a try.

@bakura10

Hi,

Previous commit is a first baby-step towards simplifying the EventManager and hopefully allowing more optimizations. The simplifications were based on my own usage of the EVM (understanding: where I always needed to dig into the source code to understand how it works).

  1. The EventInterface has been simplified by removing the "getName" and "setName". I never found it makes a lot of sense for an event object to be aware of its own name, and made thing really confusing where an event object is used in for multiple events. The standard Event class has a lot of checks to allow passing an object as param and trying to see if it's an ArrayAccess to extract data. Is this really useful?
  2. trigger and triggerUntil were drastically reduced to remove all the conditionals and (at last!!) make the API completely logic. Now, it can only work like this:
$event = new MyEvent();
$evm->trigger('foo', $event);

// or

$evm->triggerUntil('foo', $event, function() {});

This should already speed things a bit in cases where you trigger a lot. As a consequence of this, some more simplifications can be made to the code :). The idea is that as an essential piece of the framework, we should make it as minimal as possible. Just my thought.

What do you think ? ;-)

@bakura10

Hej everyone (ping @weierophinney @macnibblet @ocramius),

I've made some progress with the idea I've had and I think I had a good idea. Because of the way SplPriorityQueue works (need cloning, merging...), I realized it may be faster to simply use arrays in a smart way. Furthermore, I've also realized that a lot of overhead was added to the SharedEventManager: it had an instance of EventManager for each identifier. While architecturally more beautiful, I think we should favor performance on such critical component.

So here are my results:

My test was done with attaching 50 events and 50 events to the shared manager, and then triggering the event 1 000 times.

ZF2 :

  • Time: 3.13 s
  • Memory consumption: 761080 bytes

ZF3 :

  • Time: 0.56 s
  • Memory consumption: 463632 bytes

So a rather 6x performance bump in triggering, and nearly halve the memory consumption.

However, some operations are now much more slower (like detach) because of the removing of the CallbackHandler and the way the new architecture is.

I'll try to see tomorrow if I can optimize even more :).

NOTE: I'm not sure this refactor can handle all the use cases.

@bakura10

I managed to have a 20% speedup over previous ZF3 commit by removing some local variables (now it executes in around 0,40s).

demos/index.php
@@ -0,0 +1,28 @@
+<?php
+
+require_once __DIR__ . '/../vendor/autoload.php';
@Ocramius Collaborator

@weierophinney do you think it'd be appropriate to include a real performance test suite in ZF3? People continuously call for it...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
demos/index.php
((3 lines not shown))
+require_once __DIR__ . '/../vendor/autoload.php';
+
+$nb_occur=1000;
+
+$time_start = microtime(true);
+
+$eventManager = new \Zend\EventManager\EventManager(array('myid'));
+$sharedManager = new \Zend\EventManager\SharedEventManager();
+
+$eventManager->setSharedManager($sharedManager);
+
+for ($i = 0 ; $i != 50 ; ++$i) {
+ $eventManager->attach('event', function() {}, $i);
+ $sharedManager->attach('myid', 'event', function() {});
+}
+
@Ocramius Collaborator

You should also profile attach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/Event.php
((5 lines not shown))
}
/**
- * Get all parameters
- *
- * @return array|object|ArrayAccess
+ * {@inheritDoc}
+ */
+ public function setParam($name, $value)
+ {
+ if (is_array($this->params) || $this->params instanceof ArrayAccess) {
@Ocramius Collaborator

You can probably swap the check here to avoid the method call when not needed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/Event.php
((5 lines not shown))
}
/**
- * Get all parameters
- *
- * @return array|object|ArrayAccess
+ * {@inheritDoc}
+ */
+ public function setParam($name, $value)
+ {
+ if (is_array($this->params) || $this->params instanceof ArrayAccess) {
+ // Arrays or objects implementing array access
+ $this->params[$name] = $value;
+ } else {
+ // Objects
+ $this->params->{$name} = $value;
@Ocramius Collaborator

No real need for {}

it's better readable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Ocramius Ocramius commented on the diff
library/Zend/EventManager/Event.php
@@ -20,11 +20,6 @@
class Event implements EventInterface
{
/**
- * @var string Event name
- */
- protected $name;
@Ocramius Collaborator

Why is the name removed? I think it's intrinsically part of an event...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Ocramius Ocramius commented on the diff
library/Zend/EventManager/Event.php
((8 lines not shown))
*/
- public function propagationIsStopped()
+ public function isPropagationStopped()
@Ocramius Collaborator

This is a useless break... Plus I liked the previous naming better :-)

Are you sure this is correct english ? :/

@zf2timo
zf2timo added a note

I prefer the naming suggestion by @bakura10, so we can write code like this:

if ($this->isPropagationStopped()) {
    // Do some stuff ...
} 

I think its much more readable.

To be honest i would have has chosen hasPropagationStopped()

@netiul
netiul added a note

I'm not a native english speaker, but I'm pretty sure it should be propagationIsStopped or propagationHasStopped.

@netiul
netiul added a note

Well, it's not really a question: "If propagation has stopped, do stuff.."

@Ocramius Collaborator

I'm yoda. Your argument is invalid :P

Stopped is past tense, so has is correct here. I think hasPropagationStopped or propagationHasStopped sounds more natural. The latter makes conditions more semantic:

if ($this->propagationHasStopped()) {

}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Ocramius Ocramius commented on the diff
library/Zend/EventManager/EventInterface.php
((10 lines not shown))
*/
- public function getName();
@Ocramius Collaborator

getName should stay. In my opinion, the name is the most important part of an event

@netiul
netiul added a note

@Ocramius Agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((71 lines not shown))
{
- // "false" means "I do not want a shared manager; don't try and fetch one"
- if (false === $this->sharedManager
- || $this->sharedManager instanceof SharedEventManagerInterface
- ) {
- return $this->sharedManager;
- }
+ //$hash = spl_object_hash($listener);
@Ocramius Collaborator

Dead code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((39 lines not shown))
{
- $this->sharedManager = false;
+ return $this->sharedManager;
@Ocramius Collaborator

Shouldn't this be getOrCreate (pseudo)

Create an empty shared manager makes no sense. It's either you inject the global one or nothing. Plus if you are using something performance critical you can not inject an shared event manager so less work is done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((85 lines not shown))
- $this->sharedManager = StaticEventManager::getInstance();
- return $this->sharedManager;
+ return $listener;
@Ocramius Collaborator

You can make this a single line with 108

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((106 lines not shown))
{
- return $this->identifiers;
+ return $aggregate->attach($this, $priority);
@Ocramius Collaborator

Wondering if this kind of logic still makes sense. Do we need attachAggregate on this side? Can't we just have attach on the aggregate and that's it?

Not sure. I really find the $evm->attachAggregate($object) more logical than $object->attach($evm).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((127 lines not shown))
- } elseif ($identifiers !== null) {
- $this->identifiers = array($identifiers);
- }
- return $this;
+ //$hash = spl_object_hash($listener);
+
+ /*foreach ($this->events as &$event) {
+ if (isset($event[$hash])) {
+ unset($event[$hash]);
+ unset($this->listeners[$hash]);
+
+ return true;
+ }
+ }*/
+
+ return false;
@Ocramius Collaborator

Not implemented?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((159 lines not shown))
{
- if (is_array($identifiers) || $identifiers instanceof Traversable) {
- $this->identifiers = array_unique(array_merge($this->identifiers, (array) $identifiers));
- } elseif ($identifiers !== null) {
- $this->identifiers = array_unique(array_merge($this->identifiers, array($identifiers)));
- }
- return $this;
+ return $aggregate->detach($this);
@Ocramius Collaborator

Same as above - I think there's no need to couple the EventManager with ListenerAggregateInterface. It already works the other way around.

So this means that $evm->attachAggregate($aggraget) is no longer supported and replaced by $aggregate->attach($evm) ?

@Ocramius Collaborator

Yep, I think that removes some clutter

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((13 lines not shown))
*/
- public function triggerUntil($event, $target, $argv = null, $callback = null)
+ public function triggerUntil($eventName, EventInterface $event = null, callable $callback = null)
@Ocramius Collaborator

I think this method can go...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((209 lines not shown))
// Initial value of stop propagation flag should be false
- $e->stopPropagation(false);
+ $event = $event ?: new Event();
@Ocramius Collaborator

Missing event name, as stated above

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((188 lines not shown))
{
- return array_keys($this->events);
+ if (is_array($identifiers) || $identifiers instanceof Traversable) {
@Ocramius Collaborator

Swap the conditional

@devosc
devosc added a note

This bit looks like it could be redone so as to not repeat initializing the ResponseCollection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((206 lines not shown))
{
- if (!array_key_exists($event, $this->events)) {
- return new PriorityQueue();
+ if (is_array($identifiers) || $identifiers instanceof Traversable) {
@Ocramius Collaborator

Swap the conditional

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((206 lines not shown))
{
- if (!array_key_exists($event, $this->events)) {
- return new PriorityQueue();
+ if (is_array($identifiers) || $identifiers instanceof Traversable) {
+ $this->identifiers = array_unique(array_merge($this->identifiers, (array) $identifiers));
@Ocramius Collaborator

You said + was better than array_merge?

Yeah, didn't touch this part yet. And + cannot be used everywhere (especially with numerical index). I'll check if it works here.

I remember swapping (#4022) + for the array_merge() some time in the past to fix a bug (#3685). (Just thought it was worth mentioning...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((26 lines not shown))
- // Shared listeners on this specific event
- $this->insertListeners($listeners, $sharedListeners);
+ // + operator is faster than array_merge and in this case there are no drawbacks using it
+ $listeners = $this->getListeners($eventName)
@Ocramius Collaborator

This part could need some denormalization, since the API is protected anyway

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((42 lines not shown))
foreach ($listeners as $listener) {
- $listenerCallback = $listener->getCallback();
-
- // Trigger the listener's callback, and push its result onto the
- // response collection
- $responses->push(call_user_func($listenerCallback, $e));
-
- // If the event was asked to stop propagating, do so
- if ($e->propagationIsStopped()) {
- $responses->setStopped(true);
- break;
- }
+ // Using direct de-referencing instead of using "reset" provides a 10% speedup
+ // on performance
+ $responses->push($listener[0]($event));
@Ocramius Collaborator

Can probably build a response collection directly, without ->push() at every iteration

Taht's what I wanted (building the responses in an array, and then creating the response collection from array, but it's not possible). :/

@Ocramius Collaborator

@bakura10 what's the limitation? Can't you use an accumulator?

Do I miss something here or do you only call the first listener per priority even if there are more than one?

Do we really need the full ResponseCollection? Within Zend\Cache\Storage\Adapter\AbstractAdapter I'm using only the last result ever - all other will be discarded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Ocramius Ocramius commented on the diff
library/Zend/EventManager/EventManager.php
((26 lines not shown))
- foreach ($identifiers as $id) {
- if (!$listeners = $sharedManager->getListeners($id, $event)) {
- continue;
- }
-
- if (!is_array($listeners) && !($listeners instanceof Traversable)) {
- continue;
- }
-
- foreach ($listeners as $listener) {
- if (!$listener instanceof CallbackHandler) {
- continue;
- }
@Ocramius Collaborator

Would be nice to have the shared eventmanager allowing $identifiers being an array for this call - that avoids some iterations here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManagerAwareTrait.php
((6 lines not shown))
trait EventManagerAwareTrait
{
- use ProvidesEvents;
+ /**
+ * @var EventManagerInterface
+ */
+ protected $eventManager;
+
+ /**
+ * @var array|string|object|null
+ */
+ protected $eventIdentifier = null;
@Ocramius Collaborator

Make it an array all the time and that's it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManagerAwareTrait.php
((15 lines not shown))
+ /**
+ * @var array|string|object|null
+ */
+ protected $eventIdentifier = null;
+
+ /**
+ * Set the event manager instance used by this context
+ *
+ * @param EventManagerInterface $eventManager
+ * @return void
+ */
+ public function setEventManager(EventManagerInterface $eventManager)
+ {
+ $identifiers = array(__CLASS__, get_class($this));
+
+ if ((is_string($this->eventIdentifier))
@Ocramius Collaborator

Can be always considered a Traversable - no need for this custom logic imo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManagerAwareTrait.php
((40 lines not shown))
+
+ $eventManager->setIdentifiers($identifiers);
+
+ $this->eventManager = $eventManager;
+ }
+
+ /**
+ * Retrieve the event manager
+ *
+ * Lazy-loads an EventManager instance if none registered.
+ *
+ * @return EventManagerInterface
+ */
+ public function getEventManager()
+ {
+ if (!$this->eventManager instanceof EventManagerInterface) {
@Ocramius Collaborator

null === $this->eventManager is enough. You don't need to deal with broken state

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManagerInterface.php
((46 lines not shown))
*/
- public function triggerUntil($event, $target, $argv = null, $callback = null);
+ public function attachAggregate(ListenerAggregateInterface $aggregate, $priority = 1);
@Ocramius Collaborator

As of above, I'd get rid of this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManagerInterface.php
((71 lines not shown))
* @return bool
*/
- public function detach($listener);
@Ocramius Collaborator

Same here. YAGNI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManagerInterface.php
((98 lines not shown))
*/
- public function getListeners($event);
@Ocramius Collaborator

I think this is exactly like trigger

What do you mean?

@Ocramius Collaborator

I think github is messing with the comments - did you just rebase? The comment I wrote makes no sense at line 87...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/Event.php
((5 lines not shown))
}
/**
- * Get all parameters
- *
- * @return array|object|ArrayAccess
+ * {@inheritDoc}
+ */
+ public function setParam($name, $value)
+ {
+ if (is_array($this->params) || $this->params instanceof ArrayAccess) {
+ // Arrays or objects implementing array access

use the real name of the interface ArrayAccess

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((129 lines not shown))
+ if (!empty($eventName)) {
+ foreach ($this->events[$eventName] as $priority => $listeners) {
+ foreach ($listeners as $key => $listenerToTest) {
+ if ($listener === $listenerToTest) {
+ unset($this->events[$eventName][$priority][$key]);
+ return true;
+ }
+ }
+ }
+ }
+
+ foreach ($this->events as &$event) {
+ foreach ($event as $priority => $listeners) {
+ foreach ($listeners as $key => $listenerToTest) {
+ if ($listener === $listenerToTest) {
+ unset($event[$priority][$key]);

The variable $listeners of the second foreach can be set by reference too - and then unset($listeners[$key]); (The variable $priority is no longer needed)

The third foreach can be replaced by if (($key = array_search($listener, $listeners, true)) !== false)

Thanks for those optimizations!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((124 lines not shown))
{
- if (is_array($identifiers) || $identifiers instanceof Traversable) {
- $this->identifiers = array_unique((array) $identifiers);
- } elseif ($identifiers !== null) {
- $this->identifiers = array($identifiers);
+ if (!empty($eventName)) {
+ foreach ($this->events[$eventName] as $priority => $listeners) {
+ foreach ($listeners as $key => $listenerToTest) {
+ if ($listener === $listenerToTest) {
+ unset($this->events[$eventName][$priority][$key]);

The variable $listeners of the foreach can be set by reference - and then unset($listeners[$key]); (The variable $priority is no longer needed)
The second foreach can be replaced by if (($key = array_search($listener, $listeners, true)) !== false)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((69 lines not shown))
*/
- public function attach($event, $callback = null, $priority = 1)
+ public function getEvents()

Shouldn't this be getEventNames() ?

Exactly!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((10 lines not shown))
* @return ResponseCollection
*/
- protected function triggerListeners($event, EventInterface $e, $callback = null)
+ protected function triggerListeners($eventName, EventInterface $event, callable $callback = null)

Is this internal method really needed? The public trigger and triggerUntil and this method can be merged together into one single method trigger.

Yes, now that triggerUntil is gone, both methods can be merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((13 lines not shown))
return array();
}
- $identifiers = $this->getIdentifiers();
- //Add wildcard id to the search, if not already added
+ $identifiers = $this->getIdentifiers();
+ $listeners = array();
+
+ // Add wildcard id to the search, if not already added
if (!in_array('*', $identifiers)) {

Use the strict argument of in_array here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@bakura10

Unfortunately, I'm not sure my approach works.

The problem comes from the way PHP merges array. With the union operator (+), the keys are preserved, but [0 => "foo"] + [0 => "bar"] gives 0 => "bar". With array_merge, the problem is that index keys (that are used for sorting by priority) are reinitialized... So we're stuck u_u. I'll try to see if I can find a workaround but I'm a bit depressed u_u

@bakura10

In fact, I've been able to "trick" the array_merge by using the following trick: instead of setting the saving the listener using the priority as key: $this->listeners[$eventName][(int) $priority][] = $listener, I converted the priority to a string by appending '.0': $this->listeners[$eventName][(int) $priority . '.0'][] = $listener

Now the array_merge method considers this as a string and no longer reorder the keys.

I saw a very little drop in performance by around 2-3%, but still lot faster than ZF2 approach.

@bakura10

Ok, I clean everything up. I removed the triggerUntil, remove some function calls by concatenating triggerListeners inside trigger method. As per discussion with @Ocramius, I also removed edge-case features that introduced a lot of conditionals like supporting object in event object through ArrayAccess. I think people can use their own Event if they need such use-cases.

It's nearly ready. Feel free to review!

To @weierophinney , there is a behavior that I'm not sure was documented, but we should handle this case: what happen if the event manager have a listener to event "myEvent" with priority 1, and a shared event manager have another listener to "myEvent" at priority 1. Which should be executed first? In fact, the idea is how should we order those lines:

$listeners = array_merge_recursive(
            $this->getListeners($eventName),
            $this->getListeners('*'),
            $this->getSharedListeners($eventName),
            $this->getSharedListeners('*')
        );

afaik it has never been properly documented.

@netiul

I would like to see getName and setName still being part of in the EventInterface. They can be of great use to identify events.

library/Zend/EventManager/EventManager.php
((86 lines not shown))
- if (!$listeners = $sharedManager->getListeners($id, $event)) {
- continue;
- }
-
- if (!is_array($listeners) && !($listeners instanceof Traversable)) {
- continue;
- }
-
- foreach ($listeners as $listener) {
- if (!$listener instanceof CallbackHandler) {
- continue;
- }
- $sharedListeners[] = $listener;
- }
+ // Add wildcard id to the search, if not already added
+ if (!in_array('*', $identifiers, true)) {
@Ocramius Collaborator

What if you push * twice instead of checking?

If I push twice, the listeners will be executed twice.

@Ocramius Collaborator

Can't you array_unique on the other side?

Yes. But are you sure that:

$identifiers[] = '*';
$identifiers = array_unique($identifiers);

is faster than doing the conditionals ?

@Ocramius Collaborator

Just verified - is indeed slower: https://gist.github.com/Ocramius/7453564

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManagerAwareTrait.php
((12 lines not shown))
+ protected $eventManager;
+
+ /**
+ * @var array
+ */
+ protected $eventIdentifiers = array();
+
+ /**
+ * Set the event manager instance used by this context
+ *
+ * @param EventManagerInterface $eventManager
+ * @return void
+ */
+ public function setEventManager(EventManagerInterface $eventManager)
+ {
+ $identifiers = array_unique(array_merge(array(__CLASS__, get_class($this)), $this->eventIdentifiers));
@Ocramius Collaborator

You don't need to define variable $identifiers. Just pass the result to $eventManager->setIdentifiers()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/SharedEventManager.php
((38 lines not shown))
{
- $ids = (array) $id;
- $listeners = array();
- foreach ($ids as $id) {
- if (!array_key_exists($id, $this->identifiers)) {
- $this->identifiers[$id] = new EventManager($id);
- }
- $listeners[] = $this->identifiers[$id]->attach($event, $callback, $priority);
- }
- if (count($listeners) > 1) {
- return $listeners;
+ $identifiers = (array) $identifiers;
@Ocramius Collaborator

Just define it in the loop:

foreach (((array) $identifiers) as $identifier) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/SharedEventManager.php
((14 lines not shown))
{
- if (!array_key_exists($id, $this->identifiers)) {
- //Check if there are any id wildcards listeners
- if ('*' != $id && array_key_exists('*', $this->identifiers)) {
- return $this->identifiers['*']->getEvents();
+ $identifiers = (array) $identifiers;
@Ocramius Collaborator

foreach (((array) $identifiers) as $identifier) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@bakura10

@devosc , regarding removing the SharedEventManager: in fact the SharedEventManager is aware of identifiers. Event manager are not. Identifiers are what bring all the flexibility to ZF2 EVM, so I'm not sure the two can be merged into a single class.

I'm not sure of the purpose of the Listener class. We already have the ListenerAggregateInterface that brings that flexibility and does not add any overhead. The goal of this refactor is to make the EVM much faster so adding again complexity seems wrong.

@bakura10

Hi,

After some more tests today, my benchmark were wrong. Because of bad merging, half of the listeners were in fact not called. The execution time was not 0,40s but 1,1s (it's still around 3-4 times faster than current version).

I made a lot of tries today, trying new data strcutresu, but couldn't find a better way to do it because of the merging process that make everything harder.

Finally, I changed the ResponseCollection data structure by using a simple array instead of a SplStack, and creating the ResponseCollection only at the end of the iteration. This allows to save a lot of method calls. As a consequence, execution time dropped from 1,1s to 0,80s). Still not as low as 0,40s but still good :).

Tbh I have no other idea about how I could optimize this. If someone want to give it a try, I think the way to go would be to simplify the data structure and having a smart way to have priorities.

@devosc

This came to mind

public function attach($event, callable $listener, $priority = 1)
{
    $identifiers = $listener->getTargetIdentifiers();

    foreach($identifiers as $identifier) {
        $this->events[(string) $event][(int) $priority . '.0'][$identifier][] = $listener;
    }

    return $listener;
}

What to do when a listener has no identifiers would still need to be ironed out; a listener could either be attached to the EventManager triggering the event, or it is a listener for all identifiers ('*').

If the EventManager then had a getTargetEventListeners($id, $event) method to retrieve listeners for the specified event and target identifier(s), then this method would essentially be the same as the current SharedEventManager::getListeners($id, $event) method.

library/Zend/EventManager/EventManager.php
((12 lines not shown))
+ */
+ public function trigger($eventName, EventInterface $event = null, callable $callback = null)
+ {
+ // Initial value of stop propagation flag should be false
+ $event = $event ?: new Event();
+ $event->stopPropagation(false);
+
+ $responses = array();
+
+ // We cannot use union (+) operator as it merges numeric indexed keys
+ $listeners = array_merge_recursive(
+ $this->getListeners($eventName),
+ $this->getListeners('*'),
+ $this->getSharedListeners($eventName),
+ $this->getSharedListeners('*')
+ );

The protected method getSharedListeners will only used here - so the method could be removed completely.
(I know it's not the nice way - but could help us with performance)

if (null === $this->sharedManager) {
    $listeners = array_merge_recursive(
        $this->getListeners($eventName),
        $this->getListeners('*')
    );
} else {
    // merge own listeners together with shared listeners
    $identifiers = $this->identifiers;
    if (!in_array('*', $identifiers, true)) { // Add wildcard id to the search, if not already added
        $identifiers[] = '*';
    }

    $listeners = array_merge_recursive(
        $this->getListeners($eventName),
        $this->getListeners('*'),
        $this->sharedManager->getListeners($identifiers, $eventName),
        $this->sharedManager->getListeners($identifiers, '*'),
    );
}

PS: Be careful - not tested

We could also make sure the * identifier will be ever (or never) part of the internal identifier list - that would move the in_array check into setters. But I'm not sure if it could break evm usage.

This has very little (if none) impact. The part where we can have some gains are finding a better data structure that would allow us to merge listeners and sort them by priority, as well as the iteration.

Now, because I removed the priority queue (for the good!) we end up with two nested foreach. The ksort also operates on string now instead of integers, so I suspect this is a bit slower (but this is the only workaround to prevent array_merge to reordering keys).

In my opinion the method(s) getListeners should return all listeners listening on the given event. So it should return listeners listening on all events * without that argument.

... In cases there is no shared evm merging can be part of getListeners itself allowing other optimizations.

Seems like a logic idea. I'll do that. I'm not sure there is a use case for retrieving listeners for a specific event, without the wild card ones.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@marc-mabe

As already noted (but can't find my comment or responses):
Do we really need the full response collection on trigger() ? In Zend\Cache\Storage\Adapter\AbstractAdapter only the last value will be used and all other will be discarded. If it's required somewhere we could have two methods or an argument to return only the last value.

library/Zend/EventManager/ResponseCollection.php
@@ -74,11 +92,12 @@ public function last()
*/
public function contains($value)
{
- foreach ($this as $response) {
+ foreach ($this->responses as $response) {

return in_array($value, $this->responses, true);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/SharedEventManager.php
((14 lines not shown))
{
- if (!array_key_exists($id, $this->identifiers)) {
- //Check if there are any id wildcards listeners
- if ('*' != $id && array_key_exists('*', $this->identifiers)) {
- return $this->identifiers['*']->getEvents();
+ $listeners = array();
+
+ foreach ((array) $identifiers as $identifier) {
+ if (isset($this->identifiers[$identifier]) && isset($this->identifiers[$identifier][$eventName])) {

The second isset should be enough if (isset($this->identifiers[$identifier][$eventName])) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@marc-mabe marc-mabe commented on the diff
library/Zend/Stdlib/CallbackHandler.php
@@ -85,53 +68,26 @@ public function getCallback()
public function call(array $args = array())
{
$callback = $this->getCallback();
-
- // Minor performance tweak, if the callback gets called more than once
- if (!isset(static::$isPhp54)) {
- static::$isPhp54 = version_compare(PHP_VERSION, '5.4.0rc1', '>=');
- }
-
$argCount = count($args);

If $args is an empty array - a simple if (!$args) { return $callback(); } is enough. The function count will be needed only after this check ;)

EventListener no loger relies on CallbakcHandler . Furthremore I already made a PR for refactoring CallbackHandler to take advantage of PHP 5.4 features :).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@macnibblet

@bakura10 do we really need the AbstractListenerAggregate ?

@bakura10
@bakura10

@devosc : $identifiers = $listener->getTargetIdentifiers();

$listener is a callable. I'm not sure what you were trying to expose in your example.

@bakura10

@marc-mabe yestarday I optimized the ResponseCollection by using plain array instead of SplStack. Now, if we only need the last response, it simplifies things a lot.

@weierophinney , in the MVC dispatch are you actually using the last value of the response collection? Do you actually need the whole stack of responses?

@Ocramius
Collaborator

@bakura10 we still need the results of the various listeners. The EventManager is not designed around the MVC

@devosc

You had mentioned about how to combine the SEM and EM, I was thinking that by adding the identifiers to the $this->events list of the EM would achieve this.

I realize that in the ZF library it is desired to have one EM per component, but if that was not always the case, then there could be less merging involved?

@marc-mabe

@Ocramius Do you have examples were the hole response collection is required? Returning the last response directly isn't enough because we need the information if it was stopped but we don't need to generate a full collection for only one single value.

@Ocramius
Collaborator

@marc-mabe I can think of a chain of responsibility - if the result of an operation is produced in the middle of the chain and then some listener after that does not return anything, we're kinda lost.

@devosc

That means whoever triggered the event would need to implement so that it never relies on the last result (the caller would never know who is listening). And would most likely iterate over the returned response, which makes me think that it should be ok for the response to be anything?

One other purpose of the ResponseCollection is the stopped() method, but at a quick glance, the places where it is used, they could directly query the event object for this, $e->isPropagationStopped() instead. Which sounds like ResponseCollection could be taken out.

@Ocramius
Collaborator

Thinking again of it... any particular reason why rthe response isn't simply an array @weierophinney?

library/Zend/EventManager/EventManager.php
((14 lines not shown))
+ {
+ // Initial value of stop propagation flag should be false
+ $event = $event ?: new Event();
+ $event->stopPropagation(false);
+
+ $responses = array();
+
+ // We cannot use union (+) operator as it merges numeric indexed keys
+ $listeners = array_merge_recursive(
+ $this->getListeners($eventName),
+ $this->getListeners('*'),
+ $this->getSharedListeners($eventName),
+ $this->getSharedListeners('*')
+ );
+
+ krsort($listeners);

Because the keys are strings actually - you have to set SORT_NUMERIC else the key 5.0 higher than 40.0

I just tried with my code and it seems it's not necessary. If I add one event with priority 5 and another with 40, 40 is executed before.

You are right - I was an a patched PHP branch were I disabled auto conversion of numeric strings on comparison. Sorry!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/EventManager.php
((125 lines not shown))
}
- foreach ($listeners as $listener) {
- $listenerCallback = $listener->getCallback();
+ // retrieve listeners
+ if (isset($this->events[$eventName]) && ($listeners = $this->events[$eventName])) {

@marc-mabe , we can in fact remove the three conditionals by taking advantage of the fact that boolean are implciiteyl converted to int, so:

$mergeCount = ((isset($this->events[$eventName]) && ($listeners = $this->events[$eventName]) + ...

What do you think? :) Remvoing any branching is quite beneficial :).

It actually changes nothing (except making the code harder to read). It seems that it optimizes the branching automatically. Forget this idea :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@devosc

I've created a PR with the changes I had in mind #5491

@marc-mabe

I taked \SORT_NUMERIC because PHP shouldn't lookup for the constant within the current namespace. It's a micro-optimization.
(The same could be done for basic functions)

@marc-mabe

@bakura10 @Ocramius In my opinion the identifiers of the shared event manager are class names or interface names the target implements. So the main EM could get listeners of the shared EM by current event target instead of an array containing the identifiers. The shared EM checks the given target using instanceof operator.

$sharedListenersByPriority = $this->sharedEventManager->getListeners($event->getTarget(), $eventName);
// ------------
// SharedEventManager
public function getListeners($target, $eventName) {
    $rsListenersByPriority = [];

    if ($eventName !== '*' && isset($this->listenersByEventName[$eventName])) {
        foreach ($this->listenersByEventName[$eventName] as $identifier => $listenersByPriority) {
            if ($target instanceof $identifier || $identifier === '*') {
                $rsListenersByPriority = array_merge_recursive($rsListenersByPriority, $listenersByPriority);
            }
        }
    }

    if (isset($this->listenersByEventName['*'])) {
        foreach ($this->listenersByEventName['*'] as $identifier => $listenersByPriority) {
            if ($target instanceof $identifier || $identifier === '*') {
                $rsListenersByPriority = array_merge_recursive($rsListenersByPriority, $listenersByPriority);
            }
        }
    }

    return $rsListenersByPriority;
}
@Ocramius
Collaborator

@marc-mabe that makes it quite hard to use identifiers standalone, which is currently possible when triggering with a non-object target

marc-mabe and others added some commits
@marc-mabe marc-mabe fixed SharedEventManager::getListeners() and make SharedEventManager:…
…:getListeners() consistent with SharedEventManager::clearListeners() ... gets an array of identifiers as first argument
e412a6a
@bakura10 bakura10 Merge pull request #3 from marc-mabe/zf3-evm-opt
fixed SharedEventManager::getListeners() and make SharedEventManager::ge...
6b1e804
library/Zend/EventManager/EventManager.php
((253 lines not shown))
*/
- protected function insertListeners($masterListeners, $listeners)
+ public function prepareArgs(array $args)

In my opinion this method can go away.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@marc-mabe marc-mabe commented on the diff
library/Zend/EventManager/EventsCapableInterface.php
@@ -1,25 +0,0 @@
-<?php
-/**
- * Zend Framework (http://framework.zend.com/)
- *
- * @link http://github.com/zendframework/zf2 for the canonical source repository
- * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
- * @license http://framework.zend.com/license/new-bsd New BSD License
- */
-
-namespace Zend\EventManager;
-
-/**
- * Interface providing events that can be attached, detached and triggered.
- */
-interface EventsCapableInterface

This interface is required by Zend\Cache because the internally used EV shouldn't be overwritten ... only a getter exists.
(Having a setter already break things in the past as it was automatically called by DI)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@bakura10

Started to add some tests :).

@weierophinney weierophinney added this to the 3.0.0 milestone
@bakura10

Hi,

As for my hydrator PR, I've included a lot of benchmarks with Atheltic to cover most use cases: usage of EVM alone, usage of EVM with SEM...

It should help people that want to try optimize code.

@bakura10

@marc-mabe , I think you were the author of the various optimizations that related to sorting: bakura10@0879030#diff-da18fdca62f1cf8973a6252c88b9e11cL226

However I removed it as I'm not sure to understand it. You were first sorting listeners, and set the orderedByPriority to true. However, nothing guarantees you that no new listeners was added to the shared manager, so at the end, you nearly always end up there: bakura10@0879030#diff-da18fdca62f1cf8973a6252c88b9e11cL256 resulting in two sortings.

I have been able to optimize a bit the getListeners of SharedManager by removing a conditional inside a loop, which resulted in a 10-15% speedup for this part.

If you want to have fun at trying optimize this ;)...

@bakura10

@mwillbanks > I've added a new implementation, called "FastEventManager". If this idea is accepted, EventManager will actually extend from this one.

The FastEventManager is aimed for performance: it has no knowledge of shared event manager (hence merging of listeners is WAY faster), has no feature for things like widlcard event...

Some people actually used the event manager without sem. And I see another use case where we could use this internally: the event manager that is bound to the MvcEvent.

composer.json
@@ -28,7 +29,8 @@
},
"autoload": {
"psr-0": {
- "Zend\\": "library/"
+ "Zend\\": "library/",
+ "ZendBenchmark\\": "benchmarks/"

this should be part of "autoload-dev" ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/EventManager/FastEventManager.php
((141 lines not shown))
+ {
+ return array_keys($this->events);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getListeners($eventName)
+ {
+ if (isset($this->orderedByPriority[$eventName]) && !$this->orderedByPriority[$eventName]) {
+ krsort($this->events[$eventName], SORT_NUMERIC);
+ $this->orderedByPriority[$eventName] = true;
+ }
+
+ return isset($this->events[$eventName]) ? $this->events[$eventName] : [];
+ }
if (!isset($this->events[$eventName])) {
    return [];
}

// make sure to return listeners ordered by priority
// if `events[$eventName]` exists we are sure that `orderedByPriority[$eventName]` exists, too
if (!$this->orderedByPriority[$eventName]) {
    krsort($this->events[$eventName], SORT_NUMERIC);
    $this->orderedByPriority[$eventName] = true;
}

return $this->events[$eventName];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@marc-mabe

@bakura10 If I remember right the flag orderedByPriority was to have a well preordered list of listeners to first optimize the case there are no shared listeners listening (this part is no lnger valid with your FastEventManager as the EventManager should than be replaced by it) but another part was to have less operations required for the sort function because the list is pre-ordered - means only the shared listeners needs to be ordered into it. But I don#t know if this is big enough without the other part to be faster than calling the sort function twice.

@bakura10

Replaced by #7145

@bakura10 bakura10 closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 27, 2013
  1. @bakura10

    Save old code

    bakura10 authored
  2. @bakura10

    cleanup

    bakura10 authored
  3. @bakura10
  4. @bakura10
Commits on Oct 3, 2013
  1. @bakura10

    Update REDME

    bakura10 authored
Commits on Oct 5, 2013
  1. @bakura10

    Update

    bakura10 authored
  2. @bakura10

    Change autoload

    bakura10 authored
Commits on Nov 1, 2013
  1. @bakura10

    Update autoload

    bakura10 authored
  2. @bakura10

    Rename Callable to callable

    bakura10 authored
  3. @bakura10

    Add key

    bakura10 authored
  4. @bakura10
  5. @bakura10

    Fix doc

    bakura10 authored
  6. @bakura10
  7. @bakura10

    Optimize merging

    bakura10 authored
  8. @bakura10

    Only return PriorityQueue

    bakura10 authored
  9. @bakura10

    Fix

    bakura10 authored
  10. @bakura10

    CS

    bakura10 authored
Commits on Nov 12, 2013
  1. @bakura10

    Simplify API

    bakura10 authored
  2. @bakura10

    Removing things

    bakura10 authored
  3. @bakura10

    Update interface

    bakura10 authored
  4. @bakura10

    Trying something new

    bakura10 authored
  5. @bakura10

    Sme more tests

    bakura10 authored
  6. @bakura10

    Fix

    bakura10 authored
  7. @bakura10

    Optimization

    bakura10 authored
  8. @bakura10

    Optimize more

    bakura10 authored
  9. @bakura10

    Fix shared evm

    bakura10 authored
  10. @bakura10

    No more dependency to stdlib

    bakura10 authored
Commits on Nov 13, 2013
  1. @bakura10
  2. @bakura10

    Fix

    bakura10 authored
  3. @bakura10
  4. @bakura10

    Fix things

    bakura10 authored
  5. @bakura10

    Squizzing every little bit

    bakura10 authored
  6. @bakura10

    Fix benchmark

    bakura10 authored
  7. @bakura10

    Changes

    bakura10 authored
  8. @bakura10

    Optimizations

    bakura10 authored
Commits on Nov 14, 2013
  1. @bakura10

    Remove useless isset

    bakura10 authored
  2. @bakura10

    Fix clearing

    bakura10 authored
  3. @bakura10

    Foo

    bakura10 authored
Commits on Nov 15, 2013
  1. @bakura10

    Sort numerically

    bakura10 authored
  2. @marc-mabe

    Experimental optimizsations

    marc-mabe authored developer committed
  3. @bakura10

    Sort numerically

    bakura10 authored developer committed
  4. @marc-mabe

    EVM optimizations

    marc-mabe authored developer committed
  5. @marc-mabe

    simple EVM benchmark script

    marc-mabe authored developer committed
  6. @marc-mabe

    Merge

    marc-mabe authored developer committed
  7. @bakura10

    fix conflicts

    bakura10 authored
  8. @bakura10

    Fix CS

    bakura10 authored
Commits on Nov 18, 2013
  1. @marc-mabe

    fixed SharedEventManager::getListeners() and make SharedEventManager:…

    marc-mabe authored developer committed
    …:getListeners() consistent with SharedEventManager::clearListeners() ... gets an array of identifiers as first argument
  2. @bakura10

    Merge pull request #3 from marc-mabe/zf3-evm-opt

    bakura10 authored
    fixed SharedEventManager::getListeners() and make SharedEventManager::ge...
Commits on Nov 19, 2013
  1. @bakura10

    Update benchmark

    bakura10 authored
  2. @bakura10

    Add tests

    bakura10 authored
  3. @bakura10
  4. @bakura10

    Add more tests

    bakura10 authored
Commits on May 15, 2014
  1. @bakura10

    Add athletic

    bakura10 authored
  2. @bakura10

    Add benchmarks

    bakura10 authored
  3. @bakura10

    Improve SEM merging by 10%

    bakura10 authored
  4. @bakura10

    Do not need to sort twice

    bakura10 authored
  5. @bakura10

    Add fast event manager

    bakura10 authored
  6. @bakura10

    make sure callback is called

    bakura10 authored
  7. @bakura10
  8. @bakura10

    Clean

    bakura10 authored
Commits on May 16, 2014
  1. @bakura10

    Optimization

    bakura10 authored
Commits on May 17, 2014
  1. @bakura10

    Add tests

    bakura10 authored
Something went wrong with that request. Please try again.