feature #35635 [HttpKernel] Make ErrorListener unaware of the event dispatcher (derrabus)

This PR was merged into the 5.1-dev branch.

Discussion
----------

[HttpKernel] Make ErrorListener unaware of the event dispatcher

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | no
| Deprecations? | no
| Tickets       | N/A
| License       | MIT
| Doc PR        | N/A

Under certain circumstances, HttpKernel's `ErrorListener` class might dynamically register and unregister a listener with the dispatcher. If our goal is to make the dispatcher immutable, that specific behavior would be in our way. Also, #34988 would break this workflow.

This PR provides an alternative. The listener is always registered, but I'm using the request to piggyback a flag that activates/deactivates the listener.

Commits
-------

a9d1dede44 [HttpKernel] Make ErrorListener unaware of the event dispatcher.
This commit is contained in:
Fabien Potencier 2020-02-08 08:02:19 +01:00
commit 4253251278
2 changed files with 13 additions and 10 deletions

View File

@ -14,11 +14,11 @@ namespace Symfony\Component\HttpKernel\EventListener;
use Psr\Log\LoggerInterface;
use Symfony\Component\Debug\Exception\FlattenException as LegacyFlattenException;
use Symfony\Component\ErrorHandler\Exception\FlattenException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
@ -33,7 +33,7 @@ class ErrorListener implements EventSubscriberInterface
protected $logger;
protected $debug;
public function __construct($controller, LoggerInterface $logger = null, $debug = false)
public function __construct($controller, LoggerInterface $logger = null, bool $debug = false)
{
$this->controller = $controller;
$this->logger = $logger;
@ -47,7 +47,7 @@ class ErrorListener implements EventSubscriberInterface
$this->logException($event->getThrowable(), sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', $e->getClass(), $e->getMessage(), $e->getFile(), $e->getLine()));
}
public function onKernelException(ExceptionEvent $event, string $eventName = null, EventDispatcherInterface $eventDispatcher = null)
public function onKernelException(ExceptionEvent $event)
{
if (null === $this->controller) {
return;
@ -79,12 +79,15 @@ class ErrorListener implements EventSubscriberInterface
$event->setResponse($response);
if ($this->debug && $eventDispatcher instanceof EventDispatcherInterface) {
$cspRemovalListener = function ($event) use (&$cspRemovalListener, $eventDispatcher) {
$event->getResponse()->headers->remove('Content-Security-Policy');
$eventDispatcher->removeListener(KernelEvents::RESPONSE, $cspRemovalListener);
};
$eventDispatcher->addListener(KernelEvents::RESPONSE, $cspRemovalListener, -128);
if ($this->debug) {
$event->getRequest()->attributes->set('_remove_csp_headers', true);
}
}
public function removeCspHeader(ResponseEvent $event): void
{
if ($this->debug && $event->getRequest()->attributes->get('_remove_csp_headers', false)) {
$event->getResponse()->headers->remove('Content-Security-Policy');
}
}
@ -114,6 +117,7 @@ class ErrorListener implements EventSubscriberInterface
['logKernelException', 0],
['onKernelException', -128],
],
KernelEvents::RESPONSE => ['removeCspHeader', -128],
];
}

View File

@ -155,7 +155,6 @@ class ErrorListenerTest extends TestCase
$dispatcher->dispatch($event, KernelEvents::RESPONSE);
$this->assertFalse($response->headers->has('content-security-policy'), 'CSP header has been removed');
$this->assertFalse($dispatcher->hasListeners(KernelEvents::RESPONSE), 'CSP removal listener has been removed');
}
/**