Skip to content

Commit 74289aa

Browse files
javernicolas-grekas
authored andcommitted
[EventDispatcher] Fix removing listeners when using first-class callable syntax
1 parent 0e9cfc3 commit 74289aa

File tree

5 files changed

+66
-3
lines changed

5 files changed

+66
-3
lines changed

src/Symfony/Component/EventDispatcher/EventDispatcher.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public function getListenerPriority(string $eventName, callable|array $listener)
108108
$v[0] = $v[0]();
109109
$v[1] ??= '__invoke';
110110
}
111-
if ($v === $listener) {
111+
if ($v === $listener || ($listener instanceof \Closure && $v == $listener)) {
112112
return $priority;
113113
}
114114
}
@@ -164,7 +164,7 @@ public function removeListener(string $eventName, callable|array $listener)
164164
$v[0] = $v[0]();
165165
$v[1] ??= '__invoke';
166166
}
167-
if ($v === $listener) {
167+
if ($v === $listener || ($listener instanceof \Closure && $v == $listener)) {
168168
unset($listeners[$k], $this->sorted[$eventName], $this->optimized[$eventName]);
169169
}
170170
}

src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,32 @@ public function testMutatingWhilePropagationIsStopped()
405405

406406
$this->assertTrue($testLoaded);
407407
}
408+
409+
public function testNamedClosures()
410+
{
411+
$listener = new TestEventListener();
412+
413+
$callback1 = $listener(...);
414+
$callback2 = $listener(...);
415+
$callback3 = (new TestEventListener())(...);
416+
417+
$this->assertNotSame($callback1, $callback2);
418+
$this->assertNotSame($callback1, $callback3);
419+
$this->assertNotSame($callback2, $callback3);
420+
$this->assertTrue($callback1 == $callback2);
421+
$this->assertFalse($callback1 == $callback3);
422+
423+
$this->dispatcher->addListener('foo', $callback1, 1);
424+
$this->dispatcher->addListener('foo', $callback2, 2);
425+
$this->dispatcher->addListener('foo', $callback3, 3);
426+
427+
$this->assertSame(1, $this->dispatcher->getListenerPriority('foo', $callback1));
428+
$this->assertSame(1, $this->dispatcher->getListenerPriority('foo', $callback2));
429+
430+
$this->dispatcher->removeListener('foo', $callback1);
431+
432+
$this->assertSame(['foo' => [$callback3]], $this->dispatcher->getListeners());
433+
}
408434
}
409435

410436
class CallableClass

src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,30 @@ public function testSessionIsNotReported()
322322
$listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST));
323323
}
324324

325+
public function testOnKernelResponseRemoveListener()
326+
{
327+
$tokenStorage = new TokenStorage();
328+
$tokenStorage->setToken(new UsernamePasswordToken(new InMemoryUser('test1', 'pass1'), 'phpunit', ['ROLE_USER']));
329+
330+
$request = new Request();
331+
$request->attributes->set('_security_firewall_run', '_security_session');
332+
333+
$session = new Session(new MockArraySessionStorage());
334+
$request->setSession($session);
335+
336+
$dispatcher = new EventDispatcher();
337+
$httpKernel = $this->createMock(HttpKernelInterface::class);
338+
339+
$listener = new ContextListener($tokenStorage, [], 'session', null, $dispatcher, null, $tokenStorage->getToken(...));
340+
$this->assertEmpty($dispatcher->getListeners());
341+
342+
$listener(new RequestEvent($httpKernel, $request, HttpKernelInterface::MAIN_REQUEST));
343+
$this->assertNotEmpty($dispatcher->getListeners());
344+
345+
$listener->onKernelResponse(new ResponseEvent($httpKernel, $request, HttpKernelInterface::MAIN_REQUEST, new Response()));
346+
$this->assertEmpty($dispatcher->getListeners());
347+
}
348+
325349
protected function runSessionOnKernelResponse($newToken, $original = null)
326350
{
327351
$session = new Session(new MockArraySessionStorage());

src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Security\Http\Tests\Firewall;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\EventDispatcher\EventDispatcher;
1516
use Symfony\Component\HttpFoundation\Request;
1617
use Symfony\Component\HttpFoundation\Response;
1718
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
@@ -179,6 +180,18 @@ public function testLogoutException()
179180
$this->assertEquals(403, $event->getThrowable()->getStatusCode());
180181
}
181182

183+
public function testUnregister()
184+
{
185+
$listener = $this->createExceptionListener();
186+
$dispatcher = new EventDispatcher();
187+
188+
$listener->register($dispatcher);
189+
$this->assertNotEmpty($dispatcher->getListeners());
190+
191+
$listener->unregister($dispatcher);
192+
$this->assertEmpty($dispatcher->getListeners());
193+
}
194+
182195
public function getAccessDeniedExceptionProvider()
183196
{
184197
return [

src/Symfony/Component/Security/Http/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"psr/log": "^1|^2|^3"
3333
},
3434
"conflict": {
35-
"symfony/event-dispatcher": "<5.4",
35+
"symfony/event-dispatcher": "<6.1",
3636
"symfony/security-bundle": "<5.4",
3737
"symfony/security-csrf": "<5.4"
3838
},

0 commit comments

Comments
 (0)