Skip to content

Commit 0dc07cd

Browse files
Merge branch '4.4' into 5.4
* 4.4: [Console] Better required argument check in InputArgument [EventDispatcher] Fix removing listeners when using first-class callable syntax
2 parents d37ecc7 + 4338337 commit 0dc07cd

File tree

7 files changed

+80
-6
lines changed

7 files changed

+80
-6
lines changed

src/Symfony/Component/Console/Input/InputArgument.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public function isArray()
9292
*/
9393
public function setDefault($default = null)
9494
{
95-
if (self::REQUIRED === $this->mode && null !== $default) {
95+
if ($this->isRequired() && null !== $default) {
9696
throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
9797
}
9898

src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ public function testSetDefaultWithRequiredArgument()
8888
$argument->setDefault('default');
8989
}
9090

91+
public function testSetDefaultWithRequiredArrayArgument()
92+
{
93+
$this->expectException(\LogicException::class);
94+
$this->expectExceptionMessage('Cannot set a default value except for InputArgument::OPTIONAL mode.');
95+
$argument = new InputArgument('foo', InputArgument::REQUIRED | InputArgument::IS_ARRAY);
96+
$argument->setDefault([]);
97+
}
98+
9199
public function testSetDefaultWithArrayArgument()
92100
{
93101
$this->expectException(\LogicException::class);

src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public function removeListener(string $eventName, $listener)
7575
{
7676
if (isset($this->wrappedListeners[$eventName])) {
7777
foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
78-
if ($wrappedListener->getWrappedListener() === $listener) {
78+
if ($wrappedListener->getWrappedListener() === $listener || ($listener instanceof \Closure && $wrappedListener->getWrappedListener() == $listener)) {
7979
$listener = $wrappedListener;
8080
unset($this->wrappedListeners[$eventName][$index]);
8181
break;
@@ -110,8 +110,8 @@ public function getListenerPriority(string $eventName, $listener)
110110
// we might have wrapped listeners for the event (if called while dispatching)
111111
// in that case get the priority by wrapper
112112
if (isset($this->wrappedListeners[$eventName])) {
113-
foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
114-
if ($wrappedListener->getWrappedListener() === $listener) {
113+
foreach ($this->wrappedListeners[$eventName] as $wrappedListener) {
114+
if ($wrappedListener->getWrappedListener() === $listener || ($listener instanceof \Closure && $wrappedListener->getWrappedListener() == $listener)) {
115115
return $this->dispatcher->getListenerPriority($eventName, $wrappedListener);
116116
}
117117
}

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, $listener)
108108
$v[0] = $v[0]();
109109
$v[1] = $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, $listener)
164164
$v[0] = $v[0]();
165165
$v[1] = $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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,35 @@ public function testMutatingWhilePropagationIsStopped()
405405

406406
$this->assertTrue($testLoaded);
407407
}
408+
409+
/**
410+
* @requires PHP 8.1
411+
*/
412+
public function testNamedClosures()
413+
{
414+
$listener = new TestEventListener();
415+
416+
$callback1 = \Closure::fromCallable($listener);
417+
$callback2 = \Closure::fromCallable($listener);
418+
$callback3 = \Closure::fromCallable(new TestEventListener());
419+
420+
$this->assertNotSame($callback1, $callback2);
421+
$this->assertNotSame($callback1, $callback3);
422+
$this->assertNotSame($callback2, $callback3);
423+
$this->assertTrue($callback1 == $callback2);
424+
$this->assertFalse($callback1 == $callback3);
425+
426+
$this->dispatcher->addListener('foo', $callback1, 3);
427+
$this->dispatcher->addListener('foo', $callback2, 2);
428+
$this->dispatcher->addListener('foo', $callback3, 1);
429+
430+
$this->assertSame(3, $this->dispatcher->getListenerPriority('foo', $callback1));
431+
$this->assertSame(3, $this->dispatcher->getListenerPriority('foo', $callback2));
432+
433+
$this->dispatcher->removeListener('foo', $callback1);
434+
435+
$this->assertSame(['foo' => [$callback3]], $this->dispatcher->getListeners());
436+
}
408437
}
409438

410439
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
@@ -381,6 +381,30 @@ public function testSessionIsNotReported()
381381
$listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST));
382382
}
383383

384+
public function testOnKernelResponseRemoveListener()
385+
{
386+
$tokenStorage = new TokenStorage();
387+
$tokenStorage->setToken(new UsernamePasswordToken(new InMemoryUser('test1', 'pass1'), 'phpunit', ['ROLE_USER']));
388+
389+
$request = new Request();
390+
$request->attributes->set('_security_firewall_run', '_security_session');
391+
392+
$session = new Session(new MockArraySessionStorage());
393+
$request->setSession($session);
394+
395+
$dispatcher = new EventDispatcher();
396+
$httpKernel = $this->createMock(HttpKernelInterface::class);
397+
398+
$listener = new ContextListener($tokenStorage, [], 'session', null, $dispatcher, null, \Closure::fromCallable([$tokenStorage, 'getToken']));
399+
$this->assertEmpty($dispatcher->getListeners());
400+
401+
$listener(new RequestEvent($httpKernel, $request, HttpKernelInterface::MASTER_REQUEST));
402+
$this->assertNotEmpty($dispatcher->getListeners());
403+
404+
$listener->onKernelResponse(new ResponseEvent($httpKernel, $request, HttpKernelInterface::MASTER_REQUEST, new Response()));
405+
$this->assertEmpty($dispatcher->getListeners());
406+
}
407+
384408
protected function runSessionOnKernelResponse($newToken, $original = null)
385409
{
386410
$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;
@@ -170,6 +171,18 @@ public function testLogoutException()
170171
$this->assertEquals(403, $event->getThrowable()->getStatusCode());
171172
}
172173

174+
public function testUnregister()
175+
{
176+
$listener = $this->createExceptionListener();
177+
$dispatcher = new EventDispatcher();
178+
179+
$listener->register($dispatcher);
180+
$this->assertNotEmpty($dispatcher->getListeners());
181+
182+
$listener->unregister($dispatcher);
183+
$this->assertEmpty($dispatcher->getListeners());
184+
}
185+
173186
public function getAccessDeniedExceptionProvider()
174187
{
175188
return [

0 commit comments

Comments
 (0)