Mercurial > hg > isophonics-drupal-site
comparison core/lib/Drupal/Core/EventSubscriber/FinalExceptionSubscriber.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 129ea1e6d783 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\Core\EventSubscriber; | |
4 | |
5 use Drupal\Component\Utility\SafeMarkup; | |
6 use Drupal\Core\Config\ConfigFactoryInterface; | |
7 use Drupal\Core\StringTranslation\StringTranslationTrait; | |
8 use Drupal\Core\Utility\Error; | |
9 use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |
10 use Symfony\Component\HttpFoundation\Response; | |
11 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; | |
12 use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; | |
13 use Symfony\Component\HttpKernel\KernelEvents; | |
14 | |
15 /** | |
16 * Last-chance handler for exceptions: the final exception subscriber. | |
17 * | |
18 * This handler will catch any exceptions not caught elsewhere and report | |
19 * them as an error page. | |
20 * | |
21 * Each format has its own way of handling exceptions: | |
22 * - html: exception.default_html, exception.custom_page_html and | |
23 * exception.fast_404_html | |
24 * - json: exception.default_json | |
25 * | |
26 * And when the serialization module is installed, all serialization formats are | |
27 * handled by a single exception subscriber:: serialization.exception.default. | |
28 * | |
29 * This exception subscriber runs after all the above (it has a lower priority), | |
30 * which makes it the last-chance exception handler. It always sends a plain | |
31 * text response. If it's a displayable error and the error level is configured | |
32 * to be verbose, then a helpful backtrace is also printed. | |
33 */ | |
34 class FinalExceptionSubscriber implements EventSubscriberInterface { | |
35 use StringTranslationTrait; | |
36 | |
37 /** | |
38 * @var string | |
39 * | |
40 * One of the error level constants defined in bootstrap.inc. | |
41 */ | |
42 protected $errorLevel; | |
43 | |
44 /** | |
45 * The config factory. | |
46 * | |
47 * @var \Drupal\Core\Config\ConfigFactoryInterface | |
48 */ | |
49 protected $configFactory; | |
50 | |
51 /** | |
52 * Constructs a new FinalExceptionSubscriber. | |
53 * | |
54 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory | |
55 * The configuration factory. | |
56 */ | |
57 public function __construct(ConfigFactoryInterface $config_factory) { | |
58 $this->configFactory = $config_factory; | |
59 } | |
60 | |
61 /** | |
62 * Gets the configured error level. | |
63 * | |
64 * @return string | |
65 */ | |
66 protected function getErrorLevel() { | |
67 if (!isset($this->errorLevel)) { | |
68 $this->errorLevel = $this->configFactory->get('system.logging')->get('error_level'); | |
69 } | |
70 return $this->errorLevel; | |
71 } | |
72 | |
73 /** | |
74 * Handles exceptions for this subscriber. | |
75 * | |
76 * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event | |
77 * The event to process. | |
78 */ | |
79 public function onException(GetResponseForExceptionEvent $event) { | |
80 $exception = $event->getException(); | |
81 $error = Error::decodeException($exception); | |
82 | |
83 // Display the message if the current error reporting level allows this type | |
84 // of message to be displayed, and unconditionally in update.php. | |
85 $message = ''; | |
86 if ($this->isErrorDisplayable($error)) { | |
87 // If error type is 'User notice' then treat it as debug information | |
88 // instead of an error message. | |
89 // @see debug() | |
90 if ($error['%type'] == 'User notice') { | |
91 $error['%type'] = 'Debug'; | |
92 } | |
93 | |
94 $error = $this->simplifyFileInError($error); | |
95 | |
96 unset($error['backtrace']); | |
97 | |
98 if (!$this->isErrorLevelVerbose()) { | |
99 // Without verbose logging, use a simple message. | |
100 | |
101 // We call SafeMarkup::format directly here, rather than use t() since | |
102 // we are in the middle of error handling, and we don't want t() to | |
103 // cause further errors. | |
104 $message = SafeMarkup::format('%type: @message in %function (line %line of %file).', $error); | |
105 } | |
106 else { | |
107 // With verbose logging, we will also include a backtrace. | |
108 | |
109 $backtrace_exception = $exception; | |
110 while ($backtrace_exception->getPrevious()) { | |
111 $backtrace_exception = $backtrace_exception->getPrevious(); | |
112 } | |
113 $backtrace = $backtrace_exception->getTrace(); | |
114 // First trace is the error itself, already contained in the message. | |
115 // While the second trace is the error source and also contained in the | |
116 // message, the message doesn't contain argument values, so we output it | |
117 // once more in the backtrace. | |
118 array_shift($backtrace); | |
119 | |
120 // Generate a backtrace containing only scalar argument values. | |
121 $error['@backtrace'] = Error::formatBacktrace($backtrace); | |
122 $message = SafeMarkup::format('%type: @message in %function (line %line of %file). <pre class="backtrace">@backtrace</pre>', $error); | |
123 } | |
124 } | |
125 | |
126 $content = $this->t('The website encountered an unexpected error. Please try again later.'); | |
127 $content .= $message ? '</br></br>' . $message : ''; | |
128 $response = new Response($content, 500, ['Content-Type' => 'text/plain']); | |
129 | |
130 if ($exception instanceof HttpExceptionInterface) { | |
131 $response->setStatusCode($exception->getStatusCode()); | |
132 $response->headers->add($exception->getHeaders()); | |
133 } | |
134 else { | |
135 $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR, '500 Service unavailable (with message)'); | |
136 } | |
137 | |
138 $event->setResponse($response); | |
139 } | |
140 | |
141 /** | |
142 * {@inheritdoc} | |
143 */ | |
144 public static function getSubscribedEvents() { | |
145 // Run as the final (very late) KernelEvents::EXCEPTION subscriber. | |
146 $events[KernelEvents::EXCEPTION][] = ['onException', -256]; | |
147 return $events; | |
148 } | |
149 | |
150 /** | |
151 * Checks whether the error level is verbose or not. | |
152 * | |
153 * @return bool | |
154 */ | |
155 protected function isErrorLevelVerbose() { | |
156 return $this->getErrorLevel() === ERROR_REPORTING_DISPLAY_VERBOSE; | |
157 } | |
158 | |
159 /** | |
160 * Wrapper for error_displayable(). | |
161 * | |
162 * @param $error | |
163 * Optional error to examine for ERROR_REPORTING_DISPLAY_SOME. | |
164 * | |
165 * @return bool | |
166 * | |
167 * @see \error_displayable | |
168 */ | |
169 protected function isErrorDisplayable($error) { | |
170 return error_displayable($error); | |
171 } | |
172 | |
173 /** | |
174 * Attempts to reduce error verbosity in the error message's file path. | |
175 * | |
176 * Attempts to reduce verbosity by removing DRUPAL_ROOT from the file path in | |
177 * the message. This does not happen for (false) security. | |
178 * | |
179 * @param $error | |
180 * Optional error to examine for ERROR_REPORTING_DISPLAY_SOME. | |
181 * | |
182 * @return | |
183 * The updated $error. | |
184 */ | |
185 protected function simplifyFileInError($error) { | |
186 // Attempt to reduce verbosity by removing DRUPAL_ROOT from the file path | |
187 // in the message. This does not happen for (false) security. | |
188 $root_length = strlen(DRUPAL_ROOT); | |
189 if (substr($error['%file'], 0, $root_length) == DRUPAL_ROOT) { | |
190 $error['%file'] = substr($error['%file'], $root_length + 1); | |
191 } | |
192 return $error; | |
193 } | |
194 | |
195 } |