Mercurial > hg > isophonics-drupal-site
comparison core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 1fec387a4317 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\Core\EventSubscriber; | |
4 | |
5 use Drupal\Core\Routing\RedirectDestinationInterface; | |
6 use Drupal\Core\Utility\Error; | |
7 use Psr\Log\LoggerInterface; | |
8 use Symfony\Component\HttpFoundation\Response; | |
9 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; | |
10 use Symfony\Component\HttpKernel\HttpKernelInterface; | |
11 use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; | |
12 use Symfony\Component\Routing\Matcher\UrlMatcherInterface; | |
13 | |
14 /** | |
15 * Exception subscriber for handling core default HTML error pages. | |
16 */ | |
17 class DefaultExceptionHtmlSubscriber extends HttpExceptionSubscriberBase { | |
18 | |
19 /** | |
20 * The HTTP kernel. | |
21 * | |
22 * @var \Symfony\Component\HttpKernel\HttpKernelInterface | |
23 */ | |
24 protected $httpKernel; | |
25 | |
26 /** | |
27 * The logger instance. | |
28 * | |
29 * @var \Psr\Log\LoggerInterface | |
30 */ | |
31 protected $logger; | |
32 | |
33 /** | |
34 * The redirect destination service. | |
35 * | |
36 * @var \Drupal\Core\Routing\RedirectDestinationInterface | |
37 */ | |
38 protected $redirectDestination; | |
39 | |
40 /** | |
41 * A router implementation which does not check access. | |
42 * | |
43 * @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface | |
44 */ | |
45 protected $accessUnawareRouter; | |
46 | |
47 /** | |
48 * Constructs a new DefaultExceptionHtmlSubscriber. | |
49 * | |
50 * @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel | |
51 * The HTTP kernel. | |
52 * @param \Psr\Log\LoggerInterface $logger | |
53 * The logger service. | |
54 * @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination | |
55 * The redirect destination service. | |
56 * @param \Symfony\Component\Routing\Matcher\UrlMatcherInterface $access_unaware_router | |
57 * A router implementation which does not check access. | |
58 */ | |
59 public function __construct(HttpKernelInterface $http_kernel, LoggerInterface $logger, RedirectDestinationInterface $redirect_destination, UrlMatcherInterface $access_unaware_router) { | |
60 $this->httpKernel = $http_kernel; | |
61 $this->logger = $logger; | |
62 $this->redirectDestination = $redirect_destination; | |
63 $this->accessUnawareRouter = $access_unaware_router; | |
64 } | |
65 | |
66 /** | |
67 * {@inheritdoc} | |
68 */ | |
69 protected static function getPriority() { | |
70 // A very low priority so that custom handlers are almost certain to fire | |
71 // before it, even if someone forgets to set a priority. | |
72 return -128; | |
73 } | |
74 | |
75 /** | |
76 * {@inheritdoc} | |
77 */ | |
78 protected function getHandledFormats() { | |
79 return ['html']; | |
80 } | |
81 | |
82 /** | |
83 * Handles a 4xx error for HTML. | |
84 * | |
85 * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event | |
86 * The event to process. | |
87 */ | |
88 public function on4xx(GetResponseForExceptionEvent $event) { | |
89 if (($exception = $event->getException()) && $exception instanceof HttpExceptionInterface) { | |
90 $this->makeSubrequest($event, '/system/4xx', $exception->getStatusCode()); | |
91 } | |
92 } | |
93 | |
94 /** | |
95 * Handles a 401 error for HTML. | |
96 * | |
97 * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event | |
98 * The event to process. | |
99 */ | |
100 public function on401(GetResponseForExceptionEvent $event) { | |
101 $this->makeSubrequest($event, '/system/401', Response::HTTP_UNAUTHORIZED); | |
102 } | |
103 | |
104 /** | |
105 * Handles a 403 error for HTML. | |
106 * | |
107 * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event | |
108 * The event to process. | |
109 */ | |
110 public function on403(GetResponseForExceptionEvent $event) { | |
111 $this->makeSubrequest($event, '/system/403', Response::HTTP_FORBIDDEN); | |
112 } | |
113 | |
114 /** | |
115 * Handles a 404 error for HTML. | |
116 * | |
117 * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event | |
118 * The event to process. | |
119 */ | |
120 public function on404(GetResponseForExceptionEvent $event) { | |
121 $this->makeSubrequest($event, '/system/404', Response::HTTP_NOT_FOUND); | |
122 } | |
123 | |
124 /** | |
125 * Makes a subrequest to retrieve the default error page. | |
126 * | |
127 * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event | |
128 * The event to process. | |
129 * @param string $url | |
130 * The path/url to which to make a subrequest for this error message. | |
131 * @param int $status_code | |
132 * The status code for the error being handled. | |
133 */ | |
134 protected function makeSubrequest(GetResponseForExceptionEvent $event, $url, $status_code) { | |
135 $request = $event->getRequest(); | |
136 $exception = $event->getException(); | |
137 | |
138 try { | |
139 // Reuse the exact same request (so keep the same URL, keep the access | |
140 // result, the exception, et cetera) but override the routing information. | |
141 // This means that aside from routing, this is identical to the master | |
142 // request. This allows us to generate a response that is executed on | |
143 // behalf of the master request, i.e. for the original URL. This is what | |
144 // allows us to e.g. generate a 404 response for the original URL; if we | |
145 // would execute a subrequest with the 404 route's URL, then it'd be | |
146 // generated for *that* URL, not the *original* URL. | |
147 $sub_request = clone $request; | |
148 | |
149 // The routing to the 404 page should be done as GET request because it is | |
150 // restricted to GET and POST requests only. Otherwise a DELETE request | |
151 // would for example trigger a method not allowed exception. | |
152 $request_context = clone ($this->accessUnawareRouter->getContext()); | |
153 $request_context->setMethod('GET'); | |
154 $this->accessUnawareRouter->setContext($request_context); | |
155 | |
156 $sub_request->attributes->add($this->accessUnawareRouter->match($url)); | |
157 | |
158 // Add to query (GET) or request (POST) parameters: | |
159 // - 'destination' (to ensure e.g. the login form in a 403 response | |
160 // redirects to the original URL) | |
161 // - '_exception_statuscode' | |
162 $parameters = $sub_request->isMethod('GET') ? $sub_request->query : $sub_request->request; | |
163 $parameters->add($this->redirectDestination->getAsArray() + ['_exception_statuscode' => $status_code]); | |
164 | |
165 $response = $this->httpKernel->handle($sub_request, HttpKernelInterface::SUB_REQUEST); | |
166 // Only 2xx responses should have their status code overridden; any | |
167 // other status code should be passed on: redirects (3xx), error (5xx)… | |
168 // @see https://www.drupal.org/node/2603788#comment-10504916 | |
169 if ($response->isSuccessful()) { | |
170 $response->setStatusCode($status_code); | |
171 } | |
172 | |
173 // Persist any special HTTP headers that were set on the exception. | |
174 if ($exception instanceof HttpExceptionInterface) { | |
175 $response->headers->add($exception->getHeaders()); | |
176 } | |
177 | |
178 $event->setResponse($response); | |
179 } | |
180 catch (\Exception $e) { | |
181 // If an error happened in the subrequest we can't do much else. Instead, | |
182 // just log it. The DefaultExceptionSubscriber will catch the original | |
183 // exception and handle it normally. | |
184 $error = Error::decodeException($e); | |
185 $this->logger->log($error['severity_level'], '%type: @message in %function (line %line of %file).', $error); | |
186 } | |
187 } | |
188 | |
189 } |