class DefaultExceptionHtmlSubscriber
Same name in other branches
- 9 core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php \Drupal\Core\EventSubscriber\DefaultExceptionHtmlSubscriber
- 8.9.x core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php \Drupal\Core\EventSubscriber\DefaultExceptionHtmlSubscriber
- 11.x core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php \Drupal\Core\EventSubscriber\DefaultExceptionHtmlSubscriber
Exception subscriber for handling core default HTML error pages.
Hierarchy
- class \Drupal\Core\EventSubscriber\HttpExceptionSubscriberBase implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
- class \Drupal\Core\EventSubscriber\DefaultExceptionHtmlSubscriber extends \Drupal\Core\EventSubscriber\HttpExceptionSubscriberBase
Expanded class hierarchy of DefaultExceptionHtmlSubscriber
1 string reference to 'DefaultExceptionHtmlSubscriber'
- core.services.yml in core/
core.services.yml - core/core.services.yml
1 service uses DefaultExceptionHtmlSubscriber
File
-
core/
lib/ Drupal/ Core/ EventSubscriber/ DefaultExceptionHtmlSubscriber.php, line 18
Namespace
Drupal\Core\EventSubscriberView source
class DefaultExceptionHtmlSubscriber extends HttpExceptionSubscriberBase {
/**
* The HTTP kernel.
*
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
*/
protected $httpKernel;
/**
* The logger instance.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* The redirect destination service.
*
* @var \Drupal\Core\Routing\RedirectDestinationInterface
*/
protected $redirectDestination;
/**
* A router implementation which does not check access.
*
* @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface
*/
protected $accessUnawareRouter;
/**
* Constructs a new DefaultExceptionHtmlSubscriber.
*
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
* The HTTP kernel.
* @param \Psr\Log\LoggerInterface $logger
* The logger service.
* @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
* The redirect destination service.
* @param \Symfony\Component\Routing\Matcher\UrlMatcherInterface $access_unaware_router
* A router implementation which does not check access.
*/
public function __construct(HttpKernelInterface $http_kernel, LoggerInterface $logger, RedirectDestinationInterface $redirect_destination, UrlMatcherInterface $access_unaware_router) {
$this->httpKernel = $http_kernel;
$this->logger = $logger;
$this->redirectDestination = $redirect_destination;
$this->accessUnawareRouter = $access_unaware_router;
}
/**
* {@inheritdoc}
*/
protected static function getPriority() {
// A very low priority so that custom handlers are almost certain to fire
// before it, even if someone forgets to set a priority.
return -128;
}
/**
* {@inheritdoc}
*/
protected function getHandledFormats() {
return [
'html',
];
}
/**
* Handles a 4xx error for HTML.
*
* @param \Symfony\Component\HttpKernel\Event\ExceptionEvent $event
* The event to process.
*/
public function on4xx(ExceptionEvent $event) {
if (($exception = $event->getThrowable()) && $exception instanceof HttpExceptionInterface) {
$this->makeSubrequest($event, '/system/4xx', $exception->getStatusCode());
}
}
/**
* Handles a 400 error for HTML.
*
* @param \Symfony\Component\HttpKernel\Event\ExceptionEvent $event
* The event to process.
*/
public function on400(ExceptionEvent $event) : void {
throw $event->getThrowable();
}
/**
* Handles a 401 error for HTML.
*
* @param \Symfony\Component\HttpKernel\Event\ExceptionEvent $event
* The event to process.
*/
public function on401(ExceptionEvent $event) {
$this->makeSubrequest($event, '/system/401', Response::HTTP_UNAUTHORIZED);
}
/**
* Handles a 403 error for HTML.
*
* @param \Symfony\Component\HttpKernel\Event\ExceptionEvent $event
* The event to process.
*/
public function on403(ExceptionEvent $event) {
$this->makeSubrequest($event, '/system/403', Response::HTTP_FORBIDDEN);
}
/**
* Handles a 404 error for HTML.
*
* @param \Symfony\Component\HttpKernel\Event\ExceptionEvent $event
* The event to process.
*/
public function on404(ExceptionEvent $event) {
$this->makeSubrequest($event, '/system/404', Response::HTTP_NOT_FOUND);
}
/**
* Makes a subrequest to retrieve the default error page.
*
* @param \Symfony\Component\HttpKernel\Event\ExceptionEvent $event
* The event to process.
* @param string $url
* The path/url to which to make a subrequest for this error message.
* @param int $status_code
* The status code for the error being handled.
*/
protected function makeSubrequest(ExceptionEvent $event, $url, $status_code) {
$request = $event->getRequest();
$exception = $event->getThrowable();
try {
// Reuse the exact same request (so keep the same URL, keep the access
// result, the exception, et cetera) but override the routing information.
// This means that aside from routing, this is identical to the master
// request. This allows us to generate a response that is executed on
// behalf of the master request, i.e. for the original URL. This is what
// allows us to e.g. generate a 404 response for the original URL; if we
// would execute a subrequest with the 404 route's URL, then it'd be
// generated for *that* URL, not the *original* URL.
$sub_request = clone $request;
// The routing to the 404 page should be done as GET request because it is
// restricted to GET and POST requests only. Otherwise a DELETE request
// would for example trigger a method not allowed exception.
$request_context = clone $this->accessUnawareRouter
->getContext();
$request_context->setMethod('GET');
$this->accessUnawareRouter
->setContext($request_context);
$sub_request->attributes
->add($this->accessUnawareRouter
->match($url));
// Add to query (GET) or request (POST) parameters:
// - 'destination' (to ensure e.g. the login form in a 403 response
// redirects to the original URL)
// - '_exception_statuscode'
$parameters = $sub_request->isMethod('GET') ? $sub_request->query : $sub_request->request;
$parameters->add($this->redirectDestination
->getAsArray() + [
'_exception_statuscode' => $status_code,
]);
$response = $this->httpKernel
->handle($sub_request, HttpKernelInterface::SUB_REQUEST);
// Only 2xx responses should have their status code overridden; any
// other status code should be passed on: redirects (3xx), error (5xx)…
// @see https://www.drupal.org/node/2603788#comment-10504916
if ($response->isSuccessful()) {
$response->setStatusCode($status_code);
}
// Persist the exception's cacheability metadata, if any. If the exception
// itself isn't cacheable, then this will make the response uncacheable:
// max-age=0 will be set.
if ($response instanceof CacheableResponseInterface) {
$response->addCacheableDependency($exception);
}
// Persist any special HTTP headers that were set on the exception.
if ($exception instanceof HttpExceptionInterface) {
$response->headers
->add($exception->getHeaders());
}
$event->setResponse($response);
} catch (\Exception $e) {
// If an error happened in the subrequest we can't do much else. Instead,
// just log it. The DefaultExceptionSubscriber will catch the original
// exception and handle it normally.
$error = Error::decodeException($e);
$this->logger
->log($error['severity_level'], Error::DEFAULT_ERROR_MESSAGE, $error);
}
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title | Overrides |
---|---|---|---|---|---|
DefaultExceptionHtmlSubscriber::$accessUnawareRouter | protected | property | A router implementation which does not check access. | ||
DefaultExceptionHtmlSubscriber::$httpKernel | protected | property | The HTTP kernel. | ||
DefaultExceptionHtmlSubscriber::$logger | protected | property | The logger instance. | ||
DefaultExceptionHtmlSubscriber::$redirectDestination | protected | property | The redirect destination service. | ||
DefaultExceptionHtmlSubscriber::getHandledFormats | protected | function | Specifies the request formats this subscriber will respond to. | Overrides HttpExceptionSubscriberBase::getHandledFormats | |
DefaultExceptionHtmlSubscriber::getPriority | protected static | function | Specifies the priority of all listeners in this class. | Overrides HttpExceptionSubscriberBase::getPriority | 1 |
DefaultExceptionHtmlSubscriber::makeSubrequest | protected | function | Makes a subrequest to retrieve the default error page. | ||
DefaultExceptionHtmlSubscriber::on400 | public | function | Handles a 400 error for HTML. | ||
DefaultExceptionHtmlSubscriber::on401 | public | function | Handles a 401 error for HTML. | ||
DefaultExceptionHtmlSubscriber::on403 | public | function | Handles a 403 error for HTML. | 1 | |
DefaultExceptionHtmlSubscriber::on404 | public | function | Handles a 404 error for HTML. | 1 | |
DefaultExceptionHtmlSubscriber::on4xx | public | function | Handles a 4xx error for HTML. | ||
DefaultExceptionHtmlSubscriber::__construct | public | function | Constructs a new DefaultExceptionHtmlSubscriber. | 1 | |
HttpExceptionSubscriberBase::getSubscribedEvents | public static | function | Registers the methods in this class that should be listeners. | 1 | |
HttpExceptionSubscriberBase::onException | public | function | Handles errors for this subscriber. | 1 |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.