Skip to content

Commit ded0bb4

Browse files
[EventDispatcher] Handle lazyness internally instead of relying on ClosureProxyArgument
1 parent 7a9875c commit ded0bb4

File tree

3 files changed

+89
-9
lines changed

3 files changed

+89
-9
lines changed

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

Lines 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

Lines changed: 80 additions & 2 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
{
@@ -86,14 +87,47 @@ public function getListenerPriority($eventName, $listener)
8687
return $priority;
8788
}
8889
}
90+
91+
if (is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
92+
$listener[0] = $listener[0]();
93+
foreach ($this->listeners[$eventName] as $priority => $listeners) {
94+
if (false !== in_array($listener, $listeners, true)) {
95+
return $priority;
96+
}
97+
}
98+
} elseif (!isset($this->sorted[$eventName])) {
99+
foreach ($this->listeners[$eventName] as $priority => $listeners) {
100+
foreach ($listeners as $k => $v) {
101+
if (!is_array($v) || !isset($v[0]) || !$v[0] instanceof \Closure) {
102+
continue;
103+
}
104+
$v[0] = $v[0]();
105+
$this->listeners[$eventName][$priority][$k] = $v;
106+
107+
if ($v === $listener) {
108+
return $priority;
109+
}
110+
}
111+
}
112+
}
89113
}
90114

91115
/**
92116
* {@inheritdoc}
93117
*/
94118
public function hasListeners($eventName = null)
95119
{
96-
return (bool) $this->getListeners($eventName);
120+
if (null !== $eventName) {
121+
return isset($this->listeners[$eventName]) && !empty(array_filter($this->listeners[$eventName]));
122+
}
123+
124+
foreach ($this->listeners as $eventListeners) {
125+
if (!empty(array_filter($eventListeners))) {
126+
return true;
127+
}
128+
}
129+
130+
return false;
97131
}
98132

99133
/**
@@ -113,10 +147,44 @@ public function removeListener($eventName, $listener)
113147
if (!isset($this->listeners[$eventName])) {
114148
return;
115149
}
150+
$found = false;
116151

117152
foreach ($this->listeners[$eventName] as $priority => $listeners) {
118153
if (false !== ($key = array_search($listener, $listeners, true))) {
119154
unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]);
155+
$found = true;
156+
}
157+
}
158+
159+
if (is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
160+
$listener[0] = $listener[0]();
161+
foreach ($this->listeners[$eventName] as $priority => $listeners) {
162+
if (false !== ($key = array_search($listener, $listeners, true))) {
163+
unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]);
164+
$found = true;
165+
}
166+
}
167+
}
168+
169+
if ($found || isset($this->sorted[$eventName])) {
170+
// There might be more matches encapsed in closures,
171+
// but we don't want to instanciate the world
172+
// to remove a listener that already matched.
173+
return;
174+
}
175+
176+
foreach ($this->listeners[$eventName] as $priority => $listeners) {
177+
foreach ($listeners as $k => $v) {
178+
if (!is_array($v) || !isset($v[0]) || !$v[0] instanceof \Closure) {
179+
continue;
180+
}
181+
$v[0] = $v[0]();
182+
183+
if ($v === $listener) {
184+
unset($this->listeners[$eventName][$priority][$k], $this->sorted[$eventName]);
185+
} else {
186+
$this->listeners[$eventName][$priority][$k] = $v;
187+
}
120188
}
121189
}
122190
}
@@ -183,6 +251,16 @@ protected function doDispatch($listeners, $eventName, Event $event)
183251
private function sortListeners($eventName)
184252
{
185253
krsort($this->listeners[$eventName]);
186-
$this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]);
254+
$this->sorted[$eventName] = array();
255+
256+
foreach ($this->listeners[$eventName] as $priority => $listeners) {
257+
foreach ($listeners as $k => $listener) {
258+
if (is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
259+
$listener[0] = $listener[0]();
260+
$this->listeners[$eventName][$priority][$k] = $listener;
261+
}
262+
$this->sorted[$eventName][] = $listener;
263+
}
264+
}
187265
}
188266
}

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

Lines 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)