Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit dd68442

Browse filesBrowse files
[EventDispatcher] Handle laziness internally instead of relying on ClosureProxyArgument
1 parent 7a9875c commit dd68442
Copy full SHA for dd68442

File tree

5 files changed

+130
-17
lines changed
Filter options

5 files changed

+130
-17
lines changed

‎src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public function removeListener($eventName, $listener)
116116
public function hasListeners($eventName = null)
117117
{
118118
if (null === $eventName) {
119-
return (bool) count($this->listenerIds) || (bool) count($this->listeners);
119+
return count($this->listenerIds) || count($this->listeners) || parent::hasListeners();
120120
}
121121

122122
if (isset($this->listenerIds[$eventName])) {

‎src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php
+4-3Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111

1212
namespace Symfony\Component\EventDispatcher\DependencyInjection;
1313

14-
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
14+
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1515
use Symfony\Component\DependencyInjection\ContainerBuilder;
1616
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1717
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
18+
use Symfony\Component\DependencyInjection\Reference;
1819
use Symfony\Component\EventDispatcher\EventDispatcher;
1920
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
2021

@@ -78,7 +79,7 @@ public function process(ContainerBuilder $container)
7879
$event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
7980
}
8081

81-
$definition->addMethodCall('addListener', array($event['event'], new ClosureProxyArgument($id, $event['method']), $priority));
82+
$definition->addMethodCall('addListener', array($event['event'], array(new ServiceClosureArgument(new Reference($id)), $event['method']), $priority));
8283
}
8384
}
8485

@@ -103,7 +104,7 @@ public function process(ContainerBuilder $container)
103104
ExtractingEventDispatcher::$subscriber = $class;
104105
$extractingDispatcher->addSubscriber($extractingDispatcher);
105106
foreach ($extractingDispatcher->listeners as $args) {
106-
$args[1] = new ClosureProxyArgument($id, $args[1]);
107+
$args[1] = array(new ServiceClosureArgument(new Reference($id)), $args[1]);
107108
$definition->addMethodCall('addListener', $args);
108109
}
109110
$extractingDispatcher->listeners = array();

‎src/Symfony/Component/EventDispatcher/EventDispatcher.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/EventDispatcher/EventDispatcher.php
+57-9Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* @author Fabien Potencier <fabien@symfony.com>
2525
* @author Jordi Boggiano <j.boggiano@seld.be>
2626
* @author Jordan Alliot <jordan.alliot@gmail.com>
27+
* @author Nicolas Grekas <p@tchwork.com>
2728
*/
2829
class EventDispatcher implements EventDispatcherInterface
2930
{
@@ -52,7 +53,7 @@ public function dispatch($eventName, Event $event = null)
5253
public function getListeners($eventName = null)
5354
{
5455
if (null !== $eventName) {
55-
if (!isset($this->listeners[$eventName])) {
56+
if (empty($this->listeners[$eventName])) {
5657
return array();
5758
}
5859

@@ -77,13 +78,23 @@ public function getListeners($eventName = null)
7778
*/
7879
public function getListenerPriority($eventName, $listener)
7980
{
80-
if (!isset($this->listeners[$eventName])) {
81+
if (empty($this->listeners[$eventName])) {
8182
return;
8283
}
8384

85+
if (is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
86+
$listener[0] = $listener[0]();
87+
}
88+
8489
foreach ($this->listeners[$eventName] as $priority => $listeners) {
85-
if (false !== in_array($listener, $listeners, true)) {
86-
return $priority;
90+
foreach ($listeners as $k => $v) {
91+
if ($v !== $listener && is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) {
92+
$v[0] = $v[0]();
93+
$this->listeners[$eventName][$priority][$k] = $v;
94+
}
95+
if ($v === $listener) {
96+
return $priority;
97+
}
8798
}
8899
}
89100
}
@@ -93,7 +104,17 @@ public function getListenerPriority($eventName, $listener)
93104
*/
94105
public function hasListeners($eventName = null)
95106
{
96-
return (bool) $this->getListeners($eventName);
107+
if (null !== $eventName) {
108+
return !empty($this->listeners[$eventName]);
109+
}
110+
111+
foreach ($this->listeners as $eventListeners) {
112+
if ($eventListeners) {
113+
return true;
114+
}
115+
}
116+
117+
return false;
97118
}
98119

99120
/**
@@ -110,13 +131,30 @@ public function addListener($eventName, $listener, $priority = 0)
110131
*/
111132
public function removeListener($eventName, $listener)
112133
{
113-
if (!isset($this->listeners[$eventName])) {
134+
if (empty($this->listeners[$eventName])) {
114135
return;
115136
}
116137

138+
if (is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
139+
$listener[0] = $listener[0]();
140+
}
141+
117142
foreach ($this->listeners[$eventName] as $priority => $listeners) {
118-
if (false !== ($key = array_search($listener, $listeners, true))) {
119-
unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]);
143+
foreach ($listeners as $k => $v) {
144+
if ($v !== $listener && is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) {
145+
$v[0] = $v[0]();
146+
}
147+
if ($v === $listener) {
148+
unset($listeners[$k], $this->sorted[$eventName]);
149+
} else {
150+
$listeners[$k] = $v;
151+
}
152+
}
153+
154+
if ($listeners) {
155+
$this->listeners[$eventName][$priority] = $listeners;
156+
} else {
157+
unset($this->listeners[$eventName][$priority]);
120158
}
121159
}
122160
}
@@ -183,6 +221,16 @@ protected function doDispatch($listeners, $eventName, Event $event)
183221
private function sortListeners($eventName)
184222
{
185223
krsort($this->listeners[$eventName]);
186-
$this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]);
224+
$this->sorted[$eventName] = array();
225+
226+
foreach ($this->listeners[$eventName] as $priority => $listeners) {
227+
foreach ($listeners as $k => $listener) {
228+
if (is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
229+
$listener[0] = $listener[0]();
230+
$this->listeners[$eventName][$priority][$k] = $listener;
231+
}
232+
$this->sorted[$eventName][] = $listener;
233+
}
234+
}
187235
}
188236
}

‎src/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php
+63Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,69 @@ public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEv
302302
$this->assertFalse($this->dispatcher->hasListeners('foo'));
303303
$this->assertFalse($this->dispatcher->hasListeners());
304304
}
305+
306+
public function testHasListenersIsLazy()
307+
{
308+
$called = 0;
309+
$listener = array(function () use (&$called) { ++$called; }, 'onFoo');
310+
$this->dispatcher->addListener('foo', $listener);
311+
$this->assertTrue($this->dispatcher->hasListeners());
312+
$this->assertTrue($this->dispatcher->hasListeners('foo'));
313+
$this->assertSame(0, $called);
314+
}
315+
316+
public function testDispatchLazyListener()
317+
{
318+
$called = 0;
319+
$listener = array(function () use (&$called) { ++$called; return new TestWithDispatcher(); }, 'foo');
320+
$this->dispatcher->addListener('foo', $listener);
321+
$this->assertSame(0, $called);
322+
$this->dispatcher->dispatch('foo', new Event());
323+
$this->dispatcher->dispatch('foo', new Event());
324+
$this->assertSame(1, $called);
325+
}
326+
327+
public function testRemoveFindsLazyListeners()
328+
{
329+
$test = new TestWithDispatcher();
330+
$factory = function () use ($test) { return $test; };
331+
332+
$this->dispatcher->addListener('foo', array($factory, 'foo'));
333+
$this->assertTrue($this->dispatcher->hasListeners('foo'));
334+
$this->dispatcher->removeListener('foo', array($test, 'foo'));
335+
$this->assertFalse($this->dispatcher->hasListeners('foo'));
336+
337+
$this->dispatcher->addListener('foo', array($test, 'foo'));
338+
$this->assertTrue($this->dispatcher->hasListeners('foo'));
339+
$this->dispatcher->removeListener('foo', array($factory, 'foo'));
340+
$this->assertFalse($this->dispatcher->hasListeners('foo'));
341+
}
342+
343+
public function testPriorityFindsLazyListeners()
344+
{
345+
$test = new TestWithDispatcher();
346+
$factory = function () use ($test) { return $test; };
347+
348+
$this->dispatcher->addListener('foo', array($factory, 'foo'), 3);
349+
$this->assertSame(3, $this->dispatcher->getListenerPriority('foo', array($test, 'foo')));
350+
$this->dispatcher->removeListener('foo', array($factory, 'foo'));
351+
352+
$this->dispatcher->addListener('foo', array($test, 'foo'), 5);
353+
$this->assertSame(5, $this->dispatcher->getListenerPriority('foo', array($factory, 'foo')));
354+
}
355+
356+
public function testGetLazyListeners()
357+
{
358+
$test = new TestWithDispatcher();
359+
$factory = function () use ($test) { return $test; };
360+
361+
$this->dispatcher->addListener('foo', array($factory, 'foo'), 3);
362+
$this->assertSame(array(array($test, 'foo')), $this->dispatcher->getListeners('foo'));
363+
364+
$this->dispatcher->removeListener('foo', array($test, 'foo'));
365+
$this->dispatcher->addListener('bar', array($factory, 'foo'), 3);
366+
$this->assertSame(array('bar' => array(array($test, 'foo'))), $this->dispatcher->getListeners());
367+
}
305368
}
306369

307370
class CallableClass

‎src/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php
+5-4Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
namespace Symfony\Component\EventDispatcher\Tests\DependencyInjection;
1313

1414
use PHPUnit\Framework\TestCase;
15-
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
15+
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1616
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\Reference;
1718
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
1819

1920
class RegisterListenersPassTest extends TestCase
@@ -127,17 +128,17 @@ public function testEventSubscriberResolvableClassName()
127128
$registerListenersPass->process($container);
128129

129130
$definition = $container->getDefinition('event_dispatcher');
130-
$expected_calls = array(
131+
$expectedCalls = array(
131132
array(
132133
'addListener',
133134
array(
134135
'event',
135-
new ClosureProxyArgument('foo', 'onEvent'),
136+
array(new ServiceClosureArgument(new Reference('foo')), 'onEvent'),
136137
0,
137138
),
138139
),
139140
);
140-
$this->assertEquals($expected_calls, $definition->getMethodCalls());
141+
$this->assertEquals($expectedCalls, $definition->getMethodCalls());
141142
}
142143

143144
/**

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.