bug #29411 [EventDispatcher] Revers event tracing order (ro0NL)
This PR was merged into the 3.4 branch.
Discussion
----------
[EventDispatcher] Revers event tracing order
| Q | A
| ------------- | ---
| Branch? | 3.4
| Bug fix? | yes
| New feature? | no
| BC breaks? | no <!-- see https://symfony.com/bc -->
| Deprecations? | no
| Tests pass? | yes <!-- please add some, will be required by reviewers -->
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
| License | MIT
| Doc PR | symfony/symfony-docs#... <!-- required for new features -->
Split from #29312 for 3.4
This traces events in dispatch order.
Before:
![image](https://user-images.githubusercontent.com/1047696/49330956-5f71e780-f596-11e8-8701-80458e715213.png)
After:
![image](https://user-images.githubusercontent.com/1047696/49330963-79abc580-f596-11e8-8666-5535afd59b31.png)
Here we see it also collects "terminate" events (both kernel & console for not-called listeners). In case of exception page the fix is even better: https://github.com/symfony/symfony/pull/29312#pullrequestreview-178076750
Which now correctly shows the kernel.exception event of the main request is dispatched _before_ the kernel.request event of the sub-request.
Moreover, it de-duplicates events. So we actually see the sub-request events 🎉
_Havent looked at tests yet._
Commits
-------
2570d6f877
[EventDispatcher] Revers event tracing order
This commit is contained in:
commit
7fa13be610
@ -29,7 +29,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
protected $logger;
|
||||
protected $stopwatch;
|
||||
|
||||
private $called;
|
||||
private $callStack;
|
||||
private $dispatcher;
|
||||
private $wrappedListeners;
|
||||
|
||||
@ -38,7 +38,6 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->stopwatch = $stopwatch;
|
||||
$this->logger = $logger;
|
||||
$this->called = array();
|
||||
$this->wrappedListeners = array();
|
||||
}
|
||||
|
||||
@ -123,6 +122,10 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
*/
|
||||
public function dispatch($eventName, Event $event = null)
|
||||
{
|
||||
if (null === $this->callStack) {
|
||||
$this->callStack = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
if (null === $event) {
|
||||
$event = new Event();
|
||||
}
|
||||
@ -158,11 +161,15 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
*/
|
||||
public function getCalledListeners()
|
||||
{
|
||||
$called = array();
|
||||
foreach ($this->called as $eventName => $listeners) {
|
||||
foreach ($listeners as $listener) {
|
||||
$called[$eventName.'.'.$listener->getPretty()] = $listener->getInfo($eventName);
|
||||
if (null === $this->callStack) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$called = array();
|
||||
foreach ($this->callStack as $listener) {
|
||||
list($eventName) = $this->callStack->getInfo();
|
||||
|
||||
$called[] = $listener->getInfo($eventName);
|
||||
}
|
||||
|
||||
return $called;
|
||||
@ -188,9 +195,9 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
foreach ($allListeners as $eventName => $listeners) {
|
||||
foreach ($listeners as $listener) {
|
||||
$called = false;
|
||||
if (isset($this->called[$eventName])) {
|
||||
foreach ($this->called[$eventName] as $l) {
|
||||
if ($l->getWrappedListener() === $listener) {
|
||||
if (null !== $this->callStack) {
|
||||
foreach ($this->callStack as $calledListener) {
|
||||
if ($calledListener->getWrappedListener() === $listener) {
|
||||
$called = true;
|
||||
|
||||
break;
|
||||
@ -202,19 +209,19 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
if (!$listener instanceof WrappedListener) {
|
||||
$listener = new WrappedListener($listener, null, $this->stopwatch, $this);
|
||||
}
|
||||
$notCalled[$eventName.'.'.$listener->getPretty()] = $listener->getInfo($eventName);
|
||||
$notCalled[] = $listener->getInfo($eventName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uasort($notCalled, array($this, 'sortListenersByPriority'));
|
||||
uasort($notCalled, array($this, 'sortNotCalledListeners'));
|
||||
|
||||
return $notCalled;
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->called = array();
|
||||
$this->callStack = array();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -258,6 +265,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
$this->wrappedListeners[$eventName][] = $wrappedListener;
|
||||
$this->dispatcher->removeListener($eventName, $listener);
|
||||
$this->dispatcher->addListener($eventName, $wrappedListener, $priority);
|
||||
$this->callStack->attach($wrappedListener, array($eventName));
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,8 +294,8 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
if (!isset($this->called[$eventName])) {
|
||||
$this->called[$eventName] = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
$this->called[$eventName]->attach($listener);
|
||||
} else {
|
||||
$this->callStack->detach($listener);
|
||||
}
|
||||
|
||||
if (null !== $this->logger && $skipped) {
|
||||
@ -304,8 +312,12 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function sortListenersByPriority($a, $b)
|
||||
private function sortNotCalledListeners(array $a, array $b)
|
||||
{
|
||||
if (0 !== $cmp = strcmp($a['event'], $b['event'])) {
|
||||
return $cmp;
|
||||
}
|
||||
|
||||
if (\is_int($a['priority']) && !\is_int($b['priority'])) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -110,17 +110,17 @@ class TraceableEventDispatcherTest extends TestCase
|
||||
$tdispatcher->addListener('foo', function () {}, 5);
|
||||
|
||||
$listeners = $tdispatcher->getNotCalledListeners();
|
||||
$this->assertArrayHasKey('stub', $listeners['foo.closure']);
|
||||
unset($listeners['foo.closure']['stub']);
|
||||
$this->assertArrayHasKey('stub', $listeners[0]);
|
||||
unset($listeners[0]['stub']);
|
||||
$this->assertEquals(array(), $tdispatcher->getCalledListeners());
|
||||
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'pretty' => 'closure', 'priority' => 5)), $listeners);
|
||||
$this->assertEquals(array(array('event' => 'foo', 'pretty' => 'closure', 'priority' => 5)), $listeners);
|
||||
|
||||
$tdispatcher->dispatch('foo');
|
||||
|
||||
$listeners = $tdispatcher->getCalledListeners();
|
||||
$this->assertArrayHasKey('stub', $listeners['foo.closure']);
|
||||
unset($listeners['foo.closure']['stub']);
|
||||
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'pretty' => 'closure', 'priority' => 5)), $listeners);
|
||||
$this->assertArrayHasKey('stub', $listeners[0]);
|
||||
unset($listeners[0]['stub']);
|
||||
$this->assertEquals(array(array('event' => 'foo', 'pretty' => 'closure', 'priority' => 5)), $listeners);
|
||||
$this->assertEquals(array(), $tdispatcher->getNotCalledListeners());
|
||||
}
|
||||
|
||||
@ -133,10 +133,10 @@ class TraceableEventDispatcherTest extends TestCase
|
||||
$tdispatcher->reset();
|
||||
|
||||
$listeners = $tdispatcher->getNotCalledListeners();
|
||||
$this->assertArrayHasKey('stub', $listeners['foo.closure']);
|
||||
unset($listeners['foo.closure']['stub']);
|
||||
$this->assertArrayHasKey('stub', $listeners[0]);
|
||||
unset($listeners[0]['stub']);
|
||||
$this->assertEquals(array(), $tdispatcher->getCalledListeners());
|
||||
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'pretty' => 'closure', 'priority' => 5)), $listeners);
|
||||
$this->assertEquals(array(array('event' => 'foo', 'pretty' => 'closure', 'priority' => 5)), $listeners);
|
||||
}
|
||||
|
||||
public function testGetCalledListenersNested()
|
||||
|
Reference in New Issue
Block a user