3 namespace Drupal\Core\EventSubscriber;
5 use Drupal\Core\Access\AccessManagerInterface;
6 use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
7 use Drupal\Core\Config\ConfigFactoryInterface;
8 use Drupal\Core\Routing\AccessAwareRouterInterface;
9 use Drupal\Core\Routing\RedirectDestinationInterface;
11 use Psr\Log\LoggerInterface;
12 use Symfony\Component\HttpFoundation\Response;
13 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
14 use Symfony\Component\HttpKernel\HttpKernelInterface;
15 use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
18 * Exception subscriber for handling core custom HTML error pages.
20 class CustomPageExceptionHtmlSubscriber extends DefaultExceptionHtmlSubscriber {
23 * The configuration factory.
25 * @var \Drupal\Core\Config\ConfigFactoryInterface
27 protected $configFactory;
32 * @var \Drupal\Core\Access\AccessManagerInterface
34 protected $accessManager;
37 * Constructs a new CustomPageExceptionHtmlSubscriber.
39 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
40 * The configuration factory.
41 * @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
42 * The HTTP Kernel service.
43 * @param \Psr\Log\LoggerInterface $logger
45 * @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
46 * The redirect destination service.
47 * @param \Symfony\Component\Routing\Matcher\UrlMatcherInterface $access_unaware_router
48 * A router implementation which does not check access.
49 * @param \Drupal\Core\Access\AccessManagerInterface $access_manager
52 public function __construct(ConfigFactoryInterface $config_factory, HttpKernelInterface $http_kernel, LoggerInterface $logger, RedirectDestinationInterface $redirect_destination, UrlMatcherInterface $access_unaware_router, AccessManagerInterface $access_manager) {
53 parent::__construct($http_kernel, $logger, $redirect_destination, $access_unaware_router);
54 $this->configFactory = $config_factory;
55 $this->accessManager = $access_manager;
61 protected static function getPriority() {
68 public function on403(GetResponseForExceptionEvent $event) {
69 $custom_403_path = $this->configFactory->get('system.site')->get('page.403');
70 if (!empty($custom_403_path)) {
71 $this->makeSubrequestToCustomPath($event, $custom_403_path, Response::HTTP_FORBIDDEN);
78 public function on404(GetResponseForExceptionEvent $event) {
79 $custom_404_path = $this->configFactory->get('system.site')->get('page.404');
80 if (!empty($custom_404_path)) {
81 $this->makeSubrequestToCustomPath($event, $custom_404_path, Response::HTTP_NOT_FOUND);
86 * Makes a subrequest to retrieve the custom error page.
88 * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
89 * The event to process.
90 * @param string $custom_path
91 * The custom path to which to make a subrequest for this error message.
92 * @param int $status_code
93 * The status code for the error being handled.
95 protected function makeSubrequestToCustomPath(GetResponseForExceptionEvent $event, $custom_path, $status_code) {
96 $url = Url::fromUserInput($custom_path);
97 if ($url->isRouted()) {
98 $access_result = $this->accessManager->checkNamedRoute($url->getRouteName(), $url->getRouteParameters(), NULL, TRUE);
99 $request = $event->getRequest();
101 // Merge the custom path's route's access result's cacheability metadata
102 // with the existing one (from the master request), otherwise create it.
103 if (!$request->attributes->has(AccessAwareRouterInterface::ACCESS_RESULT)) {
104 $request->attributes->set(AccessAwareRouterInterface::ACCESS_RESULT, $access_result);
107 $existing_access_result = $request->attributes->get(AccessAwareRouterInterface::ACCESS_RESULT);
108 if ($existing_access_result instanceof RefinableCacheableDependencyInterface) {
109 $existing_access_result->addCacheableDependency($access_result);
113 // Only perform the subrequest if the custom path is actually accessible.
114 if (!$access_result->isAllowed()) {
119 $this->makeSubrequest($event, $custom_path, $status_code);