Commit | Line | Data |
---|---|---|
158bd3ca TD |
1 | <?php |
2 | /** | |
3 | * @author Marcel Werk | |
f406809a | 4 | * @copyright 2001-2016 WoltLab GmbH |
158bd3ca TD |
5 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
6 | * @package com.woltlab.wcf | |
0c166126 | 7 | * @category Community Framework |
158bd3ca | 8 | */ |
5bc5a7e6 TD |
9 | namespace { |
10 | use wcf\system\WCF; | |
2b6fbe94 | 11 | |
5bc5a7e6 | 12 | // set exception handler |
d82507dc | 13 | set_exception_handler([WCF::class, 'handleException']); |
5bc5a7e6 | 14 | // set php error handler |
d82507dc | 15 | set_error_handler([WCF::class, 'handleError'], E_ALL); |
5bc5a7e6 | 16 | // set shutdown function |
d82507dc | 17 | register_shutdown_function([WCF::class, 'destruct']); |
5bc5a7e6 | 18 | // set autoload function |
d82507dc | 19 | spl_autoload_register([WCF::class, 'autoload']); |
2b6fbe94 | 20 | |
5bc5a7e6 TD |
21 | // define escape string shortcut |
22 | function escapeString($string) { | |
23 | return WCF::getDB()->escapeString($string); | |
24 | } | |
f406809a AE |
25 | |
26 | /** | |
27 | * Helper method to output debug data for all passed variables, | |
28 | * uses `print_r()` for arrays and objects, `var_dump()` otherwise. | |
29 | */ | |
30 | function wcfDebug() { | |
31 | echo "<pre>"; | |
32 | ||
33 | $args = func_get_args(); | |
34 | $length = count($args); | |
35 | if ($length === 0) { | |
a5cf4a28 | 36 | echo "ERROR: No arguments provided.<hr>"; |
f406809a AE |
37 | } |
38 | else { | |
39 | for ($i = 0; $i < $length; $i++) { | |
40 | $arg = $args[$i]; | |
41 | ||
42 | echo "<h2>Argument {$i} (" . gettype($arg) . ")</h2>"; | |
43 | ||
44 | if (is_array($arg) || is_object($arg)) { | |
45 | print_r($arg); | |
46 | } | |
47 | else { | |
48 | var_dump($arg); | |
49 | } | |
50 | ||
51 | echo "<hr>"; | |
52 | } | |
53 | } | |
54 | ||
55 | $backtrace = debug_backtrace(); | |
56 | ||
57 | // output call location to help finding these debug outputs again | |
58 | echo "wcfDebug() called in {$backtrace[0]['file']} on line {$backtrace[0]['line']}"; | |
59 | ||
60 | echo "</pre>"; | |
61 | ||
62 | exit; | |
63 | } | |
2b6fbe94 | 64 | |
5bc5a7e6 TD |
65 | // define DOCUMENT_ROOT on IIS if not set |
66 | if (PHP_EOL == "\r\n") { | |
67 | if (!isset($_SERVER['DOCUMENT_ROOT']) && isset($_SERVER['SCRIPT_FILENAME'])) { | |
68 | $_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr($_SERVER['SCRIPT_FILENAME'], 0, 0 - strlen($_SERVER['PHP_SELF']))); | |
69 | } | |
70 | if (!isset($_SERVER['DOCUMENT_ROOT']) && isset($_SERVER['PATH_TRANSLATED'])) { | |
71 | $_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr(str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']), 0, 0 - strlen($_SERVER['PHP_SELF']))); | |
72 | } | |
2b6fbe94 | 73 | |
5bc5a7e6 TD |
74 | if (!isset($_SERVER['REQUEST_URI'])) { |
75 | $_SERVER['REQUEST_URI'] = substr($_SERVER['PHP_SELF'], 1); | |
76 | if (isset($_SERVER['QUERY_STRING'])) { | |
77 | $_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING']; | |
78 | } | |
79 | } | |
80 | } | |
dcb3a44c | 81 | } |
322ced57 | 82 | |
fc23fa20 | 83 | // @codingStandardsIgnoreStart |
5bc5a7e6 | 84 | namespace wcf\functions\exception { |
fc23fa20 | 85 | use wcf\system\WCF; |
b8e0376c | 86 | use wcf\system\exception\IExtraInformationException; |
5bc5a7e6 | 87 | use wcf\system\exception\SystemException; |
5bc5a7e6 TD |
88 | use wcf\util\FileUtil; |
89 | use wcf\util\StringUtil; | |
2b6fbe94 | 90 | |
76e90abc | 91 | function logThrowable($e, &$logFile = null) { |
8891d4b9 | 92 | if ($logFile === null) $logFile = WCF_DIR . 'log/' . gmdate('Y-m-d', TIME_NOW) . '.txt'; |
b8149369 | 93 | touch($logFile); |
e4499881 | 94 | |
b8149369 | 95 | // don't forget to update ExceptionLogViewPage, when changing the log file format |
e4499881 | 96 | /** @noinspection PhpUndefinedMethodInspection */ |
b8149369 TD |
97 | $message = gmdate('r', TIME_NOW)."\n". |
98 | 'Message: '.str_replace("\n", ' ', $e->getMessage())."\n". | |
99 | 'PHP version: '.phpversion()."\n". | |
100 | 'WCF version: '.WCF_VERSION."\n". | |
101 | 'Request URI: '.(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '')."\n". | |
102 | 'Referrer: '.(isset($_SERVER['HTTP_REFERER']) ? str_replace("\n", ' ', $_SERVER['HTTP_REFERER']) : '')."\n". | |
103 | 'User Agent: '.(isset($_SERVER['HTTP_USER_AGENT']) ? str_replace("\n", ' ', $_SERVER['HTTP_USER_AGENT']) : '')."\n". | |
104 | 'Peak Memory Usage: '.memory_get_peak_usage().'/'.FileUtil::getMemoryLimit()."\n"; | |
e4499881 | 105 | /** @noinspection PhpUndefinedMethodInspection */ |
b8149369 | 106 | do { |
e4499881 | 107 | /** @noinspection PhpUndefinedMethodInspection */ |
b8149369 TD |
108 | $message .= "======\n". |
109 | 'Error Class: '.get_class($e)."\n". | |
110 | 'Error Message: '.str_replace("\n", ' ', $e->getMessage())."\n". | |
111 | 'Error Code: '.intval($e->getCode())."\n". | |
112 | 'File: '.str_replace("\n", ' ', $e->getFile()).' ('.$e->getLine().')'."\n". | |
113 | 'Extra Information: '.($e instanceof IExtraInformationException ? base64_encode(serialize($e->getExtraInformation())) : '-')."\n". | |
114 | 'Stack Trace: '.base64_encode(serialize(array_map(function ($item) { | |
115 | $item['args'] = array_map(function ($item) { | |
116 | switch (gettype($item)) { | |
117 | case 'object': | |
118 | return get_class($item); | |
119 | case 'array': | |
120 | return array_map(function () { | |
121 | return '[redacted]'; | |
122 | }, $item); | |
123 | default: | |
124 | return $item; | |
125 | } | |
126 | }, $item['args']); | |
e4499881 | 127 | |
b8149369 TD |
128 | return $item; |
129 | }, sanitizeStacktrace($e, true))))."\n"; | |
130 | } | |
131 | while ($e = $e->getPrevious()); | |
e4499881 | 132 | |
b8149369 TD |
133 | // calculate Exception-ID |
134 | $exceptionID = sha1($message); | |
2b6fbe94 | 135 | $entry = "<<<<<<<<".$exceptionID."<<<<\n".$message."<<<<\n\n"; |
e4499881 | 136 | |
b8149369 TD |
137 | file_put_contents($logFile, $entry, FILE_APPEND); |
138 | return $exceptionID; | |
139 | } | |
2b6fbe94 | 140 | |
5bc5a7e6 | 141 | function printThrowable($e) { |
76e90abc | 142 | $exceptionID = logThrowable($e, $logFile); |
614e2466 AE |
143 | |
144 | $exceptionTitle = $exceptionSubtitle = $exceptionExplanation = ''; | |
145 | $logFile = sanitizePath($logFile); | |
146 | try { | |
04edc2ff AE |
147 | if (WCF::getLanguage() !== null) { |
148 | $exceptionTitle = WCF::getLanguage()->get('wcf.global.exception.title', true); | |
149 | $exceptionSubtitle = str_replace('{$exceptionID}', $exceptionID, WCF::getLanguage()->get('wcf.global.exception.subtitle', true)); | |
150 | $exceptionExplanation = str_replace('{$logFile}', $logFile, WCF::getLanguage()->get('wcf.global.exception.explanation', true)); | |
151 | } | |
614e2466 AE |
152 | } |
153 | catch (\Exception $e) { | |
154 | // ignore | |
155 | } | |
156 | catch (\Throwable $e) { | |
157 | // ignore | |
158 | } | |
159 | ||
160 | if (!$exceptionTitle || !$exceptionSubtitle || !$exceptionExplanation) { | |
161 | // one or more failed, fallback to english | |
162 | $exceptionTitle = 'An error has occured'; | |
a7eb5dda | 163 | $exceptionSubtitle = 'Internal error code: <span class="exceptionInlineCodeWrapper"><span class="exceptionInlineCode">'.$exceptionID.'</span></span>'; |
614e2466 AE |
164 | $exceptionExplanation = <<<EXPLANATION |
165 | <p class="exceptionSubtitle">What happened?</p> | |
166 | <p class="exceptionText">An error has occured while trying to handle your request and execution has been terminated. Please forward the above error code to the site administrator.</p> | |
167 | <p class="exceptionText"> </p> <!-- required to ensure spacing after copy & paste --> | |
168 | <p class="exceptionText"> | |
169 | The error code can be used by an administrator to lookup the full error message in the Administration Control Panel via “Logs » Errors”. | |
819d7822 | 170 | In addition the error has been written to the log file located at <span class="exceptionInlineCodeWrapper"><span class="exceptionInlineCode">{$logFile}</span></span> and can be accessed with a FTP program or similar. |
614e2466 AE |
171 | </p> |
172 | <p class="exceptionText"> </p> <!-- required to ensure spacing after copy & paste --> | |
173 | <p class="exceptionText">Notice: The error code was randomly generated and has no use beyond looking up the full message.</p> | |
174 | EXPLANATION; | |
175 | ||
176 | } | |
177 | ||
178 | /* | |
179 | * A notice on the HTML used below: | |
180 | * | |
181 | * It might appear a bit weird to use <p> all over the place where semantically | |
182 | * other elements would fit in way better. The reason behind this is that we avoid | |
183 | * inheriting unwanted styles (e.g. exception displayed in an overlay) and that | |
184 | * the output needs to be properly readable when copied & pasted somewhere. | |
185 | * | |
186 | * Besides the visual appearance, the output was built to provide a maximum of | |
187 | * compatibility and readability when pasted somewhere else, e.g. a WYSIWYG editor | |
188 | * without the potential of messing up the formatting and thus harming the readability. | |
189 | */ | |
5bc5a7e6 TD |
190 | ?><!DOCTYPE html> |
191 | <html> | |
192 | <head> | |
962c9241 | 193 | <?php if (!defined('EXCEPTION_PRIVACY') || EXCEPTION_PRIVACY !== 'private') { ?> |
5bc5a7e6 TD |
194 | <title>Fatal Error: <?php echo StringUtil::encodeHTML($e->getMessage()); ?></title> |
195 | <?php } else { ?> | |
196 | <title>Fatal Error</title> | |
197 | <?php } ?> | |
614e2466 AE |
198 | <meta charset="utf-8"> |
199 | <meta name="viewport" content="width=device-width, initial-scale=1"> | |
5bc5a7e6 | 200 | <style> |
614e2466 AE |
201 | .exceptionBody { |
202 | margin: 0; | |
203 | padding: 0; | |
5bc5a7e6 | 204 | } |
614e2466 AE |
205 | |
206 | .exceptionContainer { | |
207 | box-sizing: border-box; | |
208 | font-family: 'Segoe UI', 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif; | |
209 | font-size: 14px; | |
210 | padding-bottom: 20px; | |
5bc5a7e6 | 211 | } |
614e2466 AE |
212 | |
213 | .exceptionContainer * { | |
214 | box-sizing: inherit; | |
215 | color: #000; | |
216 | line-height: 1.5em; | |
217 | margin: 0; | |
218 | padding: 0; | |
5bc5a7e6 | 219 | } |
614e2466 AE |
220 | |
221 | .exceptionHeader { | |
222 | background-color: rgb(44, 62, 80); | |
89130385 | 223 | padding: 30px 0; |
5bc5a7e6 | 224 | } |
614e2466 AE |
225 | |
226 | .exceptionTitle { | |
227 | color: #fff; | |
228 | font-size: 28px; | |
229 | font-weight: 300; | |
5bc5a7e6 | 230 | } |
614e2466 AE |
231 | |
232 | .exceptionErrorCode { | |
233 | color: #fff; | |
234 | margin-top: .5em; | |
5bc5a7e6 | 235 | } |
614e2466 AE |
236 | |
237 | .exceptionErrorCode .exceptionInlineCode { | |
238 | background-color: rgb(52, 73, 94); | |
239 | border-radius: 3px; | |
240 | color: #fff; | |
241 | font-family: monospace; | |
242 | padding: 3px 10px; | |
243 | white-space: nowrap; | |
5bc5a7e6 | 244 | } |
614e2466 AE |
245 | |
246 | .exceptionSubtitle { | |
247 | border-bottom: 1px solid rgb(238, 238, 238); | |
248 | color: rgb(44, 62, 80); | |
249 | font-size: 24px; | |
250 | font-weight: 300; | |
251 | margin-bottom: 15px; | |
252 | padding-bottom: 10px; | |
5bc5a7e6 | 253 | } |
614e2466 AE |
254 | |
255 | .exceptionContainer > .exceptionBoundary { | |
256 | margin-top: 30px; | |
5bc5a7e6 | 257 | } |
614e2466 AE |
258 | |
259 | .exceptionText .exceptionInlineCodeWrapper { | |
260 | border: 1px solid rgb(169, 169, 169); | |
261 | border-radius: 3px; | |
262 | padding: 2px 5px; | |
5bc5a7e6 | 263 | } |
614e2466 AE |
264 | |
265 | .exceptionText .exceptionInlineCode { | |
266 | font-family: monospace; | |
267 | white-space: nowrap; | |
5bc5a7e6 | 268 | } |
614e2466 AE |
269 | |
270 | .exceptionFieldTitle { | |
271 | color: rgb(59, 109, 169); | |
5bc5a7e6 | 272 | } |
614e2466 AE |
273 | |
274 | .exceptionFieldTitle .exceptionColon { | |
275 | /* hide colon in browser, but will be visible after copy & paste */ | |
276 | opacity: 0; | |
5bc5a7e6 | 277 | } |
614e2466 AE |
278 | |
279 | .exceptionFieldValue { | |
280 | font-size: 18px; | |
3d8a295e | 281 | min-height: 1.5em; |
614e2466 AE |
282 | } |
283 | ||
284 | .exceptionSystemInformation, | |
285 | .exceptionErrorDetails, | |
286 | .exceptionStacktrace { | |
287 | list-style-type: none; | |
288 | } | |
289 | ||
290 | .exceptionSystemInformation > li:not(:first-child), | |
291 | .exceptionErrorDetails > li:not(:first-child) { | |
292 | margin-top: 10px; | |
293 | } | |
294 | ||
295 | .exceptionStacktrace { | |
296 | display: block; | |
297 | margin-top: 5px; | |
298 | overflow: auto; | |
299 | padding-bottom: 20px; | |
300 | } | |
301 | ||
302 | .exceptionStacktraceFile, | |
303 | .exceptionStacktraceFile span, | |
304 | .exceptionStacktraceCall, | |
305 | .exceptionStacktraceCall span { | |
306 | font-family: monospace !important; | |
307 | white-space: nowrap !important; | |
308 | } | |
309 | ||
310 | .exceptionStacktraceCall + .exceptionStacktraceFile { | |
311 | margin-top: 5px; | |
312 | } | |
313 | ||
314 | .exceptionStacktraceCall { | |
315 | padding-left: 40px; | |
316 | } | |
317 | ||
318 | .exceptionStacktraceCall, | |
319 | .exceptionStacktraceCall span { | |
320 | color: rgb(102, 102, 102) !important; | |
321 | font-size: 13px !important; | |
322 | } | |
323 | ||
324 | /* mobile */ | |
325 | @media (max-width: 767px) { | |
326 | .exceptionBoundary { | |
327 | min-width: 320px; | |
328 | padding: 0 10px; | |
dd6a2623 | 329 | } |
614e2466 AE |
330 | |
331 | .exceptionText .exceptionInlineCodeWrapper { | |
332 | display: inline-block; | |
333 | overflow: auto; | |
dd6a2623 | 334 | } |
614e2466 AE |
335 | |
336 | .exceptionErrorCode .exceptionInlineCode { | |
337 | font-size: 13px; | |
338 | padding: 2px 5px; | |
dd6a2623 | 339 | } |
614e2466 AE |
340 | } |
341 | ||
342 | /* desktop */ | |
343 | @media (min-width: 768px) { | |
344 | .exceptionBoundary { | |
345 | margin: 0 auto; | |
346 | max-width: 1400px; | |
347 | min-width: 1200px; | |
348 | padding: 0 10px; | |
349 | } | |
350 | ||
351 | .exceptionSystemInformation { | |
352 | display: flex; | |
353 | flex-wrap: wrap; | |
354 | } | |
355 | ||
356 | .exceptionSystemInformation1, | |
357 | .exceptionSystemInformation3, | |
358 | .exceptionSystemInformation5 { | |
359 | flex: 0 0 200px; | |
360 | margin: 0 0 10px 0 !important; | |
361 | } | |
362 | ||
363 | .exceptionSystemInformation2, | |
364 | .exceptionSystemInformation4, | |
365 | .exceptionSystemInformation6 { | |
366 | flex: 0 0 calc(100% - 210px); | |
367 | margin: 0 0 10px 10px !important; | |
368 | max-width: calc(100% - 210px); | |
369 | } | |
370 | ||
371 | .exceptionSystemInformation1 { order: 1; } | |
372 | .exceptionSystemInformation2 { order: 2; } | |
373 | .exceptionSystemInformation3 { order: 3; } | |
374 | .exceptionSystemInformation4 { order: 4; } | |
375 | .exceptionSystemInformation5 { order: 5; } | |
376 | .exceptionSystemInformation6 { order: 6; } | |
377 | ||
378 | .exceptionSystemInformation .exceptionFieldValue { | |
379 | overflow: hidden; | |
380 | text-overflow: ellipsis; | |
381 | white-space: nowrap; | |
382 | } | |
383 | } | |
384 | </style> | |
385 | </head> | |
386 | <body class="exceptionBody"> | |
387 | <div class="exceptionContainer"> | |
388 | <div class="exceptionHeader"> | |
389 | <div class="exceptionBoundary"> | |
390 | <p class="exceptionTitle"><?php echo $exceptionTitle; ?></p> | |
391 | <p class="exceptionErrorCode"><?php echo str_replace('{$exceptionID}', $exceptionID, $exceptionSubtitle); ?></p> | |
392 | </div> | |
393 | </div> | |
394 | ||
395 | <div class="exceptionBoundary"> | |
396 | <?php echo $exceptionExplanation; ?> | |
5bc5a7e6 | 397 | </div> |
962c9241 | 398 | <?php if (!defined('EXCEPTION_PRIVACY') || EXCEPTION_PRIVACY !== 'private') { ?> |
614e2466 AE |
399 | <div class="exceptionBoundary"> |
400 | <p class="exceptionSubtitle">System Information</p> | |
401 | <ul class="exceptionSystemInformation"> | |
402 | <li class="exceptionSystemInformation1"> | |
403 | <p class="exceptionFieldTitle">PHP Version<span class="exceptionColon">:</span></p> | |
404 | <p class="exceptionFieldValue"><?php echo StringUtil::encodeHTML(phpversion()); ?></p> | |
405 | </li> | |
406 | <li class="exceptionSystemInformation3"> | |
407 | <p class="exceptionFieldTitle">WCF Version<span class="exceptionColon">:</span></p> | |
408 | <p class="exceptionFieldValue"><?php echo StringUtil::encodeHTML(WCF_VERSION); ?></p> | |
409 | </li> | |
410 | <li class="exceptionSystemInformation5"> | |
411 | <p class="exceptionFieldTitle">Peak Memory Usage<span class="exceptionColon">:</span></p> | |
412 | <p class="exceptionFieldValue"><?php echo round(memory_get_peak_usage() / 1024 / 1024, 3); ?>/<?php echo round(FileUtil::getMemoryLimit() / 1024 / 1024, 3); ?> MiB</p> | |
413 | </li> | |
414 | <li class="exceptionSystemInformation2"> | |
415 | <p class="exceptionFieldTitle">Request URI<span class="exceptionColon">:</span></p> | |
416 | <p class="exceptionFieldValue"><?php if (isset($_SERVER['REQUEST_URI'])) echo StringUtil::encodeHTML($_SERVER['REQUEST_URI']); ?></p> | |
417 | </li> | |
418 | <li class="exceptionSystemInformation4"> | |
419 | <p class="exceptionFieldTitle">Referrer<span class="exceptionColon">:</span></p> | |
420 | <p class="exceptionFieldValue"><?php if (isset($_SERVER['HTTP_REFERER'])) echo StringUtil::encodeHTML($_SERVER['HTTP_REFERER']); ?></p> | |
421 | </li> | |
422 | <li class="exceptionSystemInformation6"> | |
423 | <p class="exceptionFieldTitle">User Agent<span class="exceptionColon">:</span></p> | |
424 | <p class="exceptionFieldValue"><?php if (isset($_SERVER['HTTP_USER_AGENT'])) echo StringUtil::encodeHTML($_SERVER['HTTP_USER_AGENT']); ?></p> | |
425 | </li> | |
426 | </ul> | |
5bc5a7e6 | 427 | </div> |
614e2466 | 428 | |
5bc5a7e6 TD |
429 | <?php |
430 | $first = true; | |
431 | do { | |
432 | ?> | |
614e2466 AE |
433 | <div class="exceptionBoundary"> |
434 | <p class="exceptionSubtitle"><?php if (!$e->getPrevious() && !$first) { echo "Original "; } else if ($e->getPrevious() && $first) { echo "Final "; } ?>Error</p> | |
5bc5a7e6 | 435 | <?php if ($e instanceof SystemException && $e->getDescription()) { ?> |
614e2466 | 436 | <p class="exceptionText"><?php echo $e->getDescription(); ?></p> |
5bc5a7e6 | 437 | <?php } ?> |
614e2466 | 438 | <ul class="exceptionErrorDetails"> |
0b46efcf TD |
439 | <li> |
440 | <p class="exceptionFieldTitle">Error Type<span class="exceptionColon">:</span></p> | |
441 | <p class="exceptionFieldValue"><?php echo StringUtil::encodeHTML(get_class($e)); ?></p> | |
442 | </li> | |
614e2466 AE |
443 | <li> |
444 | <p class="exceptionFieldTitle">Error Message<span class="exceptionColon">:</span></p> | |
445 | <p class="exceptionFieldValue"><?php echo StringUtil::encodeHTML($e->getMessage()); ?></p> | |
446 | </li> | |
447 | <?php if ($e->getCode()) { ?> | |
448 | <li> | |
449 | <p class="exceptionFieldTitle">Error Code<span class="exceptionColon">:</span></p> | |
450 | <p class="exceptionFieldValue"><?php echo intval($e->getCode()); ?></p> | |
451 | </li> | |
452 | <?php } ?> | |
453 | <li> | |
454 | <p class="exceptionFieldTitle">File<span class="exceptionColon">:</span></p> | |
455 | <p class="exceptionFieldValue" style="word-break: break-all"><?php echo StringUtil::encodeHTML(sanitizePath($e->getFile())); ?> (<?php echo $e->getLine(); ?>)</p> | |
456 | </li> | |
457 | ||
5bc5a7e6 TD |
458 | <?php |
459 | if ($e instanceof SystemException) { | |
460 | ob_start(); | |
461 | $e->show(); | |
462 | ob_end_clean(); | |
2b6fbe94 | 463 | |
5bc5a7e6 TD |
464 | $reflection = new \ReflectionClass($e); |
465 | $property = $reflection->getProperty('information'); | |
466 | $property->setAccessible(true); | |
467 | if ($property->getValue($e)) { | |
468 | throw new \Exception("Using the 'information' property of SystemException is not supported any more."); | |
469 | } | |
470 | } | |
b8e0376c TD |
471 | if ($e instanceof IExtraInformationException) { |
472 | foreach ($e->getExtraInformation() as list($key, $value)) { | |
614e2466 AE |
473 | ?> |
474 | <li> | |
475 | <p class="exceptionFieldTitle"><?php echo StringUtil::encodeHTML($key); ?><span class="exceptionColon">:</span></p> | |
476 | <p class="exceptionFieldValue"><?php echo StringUtil::encodeHTML($value); ?></p> | |
477 | </li> | |
478 | <?php | |
b8e0376c TD |
479 | } |
480 | } | |
5bc5a7e6 | 481 | ?> |
614e2466 AE |
482 | <li> |
483 | <p class="exceptionFieldTitle">Stack Trace<span class="exceptionColon">:</span></p> | |
484 | <ul class="exceptionStacktrace"> | |
485 | <?php | |
486 | $trace = sanitizeStacktrace($e); | |
614e2466 AE |
487 | for ($i = 0, $max = count($trace); $i < $max; $i++) { |
488 | ?> | |
489 | <li class="exceptionStacktraceFile"><?php echo '#'.$i.' '.StringUtil::encodeHTML($trace[$i]['file']).' ('.$trace[$i]['line'].')'.':'; ?></li> | |
490 | <li class="exceptionStacktraceCall"> | |
491 | <?php | |
492 | echo $trace[$i]['class'].$trace[$i]['type'].$trace[$i]['function'].'('; | |
493 | echo implode(', ', array_map(function ($item) { | |
494 | switch (gettype($item)) { | |
495 | case 'integer': | |
496 | case 'double': | |
497 | return $item; | |
498 | case 'NULL': | |
499 | return 'null'; | |
500 | case 'string': | |
aac60824 | 501 | return "'".addcslashes(StringUtil::encodeHTML($item), "\\'")."'"; |
614e2466 AE |
502 | case 'boolean': |
503 | return $item ? 'true' : 'false'; | |
504 | case 'array': | |
505 | $keys = array_keys($item); | |
506 | if (count($keys) > 5) return "[ ".count($keys)." items ]"; | |
507 | return '[ '.implode(', ', array_map(function ($item) { | |
508 | return $item.' => '; | |
509 | }, $keys)).']'; | |
510 | case 'object': | |
511 | return get_class($item); | |
512 | } | |
513 | }, $trace[$i]['args'])); | |
514 | echo ')</li>'; | |
515 | } | |
516 | ?> | |
517 | </ul> | |
518 | </li> | |
519 | </ul> | |
5bc5a7e6 TD |
520 | </div> |
521 | <?php | |
522 | $first = false; | |
523 | } while ($e = $e->getPrevious()); | |
524 | ?> | |
525 | <?php } ?> | |
526 | </div> | |
527 | </body> | |
528 | </html> | |
529 | <?php | |
322ced57 | 530 | } |
5bc5a7e6 TD |
531 | |
532 | function sanitizeStacktrace($e, $ignorePaths = false) { | |
533 | $trace = $e->getTrace(); | |
2b6fbe94 | 534 | |
5bc5a7e6 | 535 | return array_map(function ($item) use ($ignorePaths) { |
2b6fbe94 TD |
536 | if (!isset($item['file'])) $item['file'] = '[internal function]'; |
537 | if (!isset($item['line'])) $item['line'] = '?'; | |
538 | if (!isset($item['class'])) $item['class'] = ''; | |
539 | if (!isset($item['type'])) $item['type'] = ''; | |
335aae2b | 540 | if (!isset($item['args'])) $item['args'] = []; |
2b6fbe94 | 541 | |
5bc5a7e6 | 542 | // strip database credentials |
2b6fbe94 TD |
543 | if (preg_match('~\\\\?wcf\\\\system\\\\database\\\\[a-zA-Z]*Database~', $item['class']) || $item['class'] === 'PDO') { |
544 | if ($item['function'] === '__construct') { | |
545 | $item['args'] = array_map(function () { | |
546 | return '[redacted]'; | |
547 | }, $item['args']); | |
5bc5a7e6 TD |
548 | } |
549 | } | |
390ec505 | 550 | |
5bc5a7e6 TD |
551 | if (!$ignorePaths) { |
552 | $item['args'] = array_map(function ($item) { | |
b8e0376c | 553 | if (!is_string($item)) return $item; |
390ec505 TD |
554 | |
555 | if (preg_match('~^('.preg_quote($_SERVER['DOCUMENT_ROOT'], '~').'|'.preg_quote(WCF_DIR, '~').')~', $item)) { | |
5bc5a7e6 TD |
556 | $item = sanitizePath($item); |
557 | } | |
2b6fbe94 | 558 | |
390ec505 | 559 | return $item; |
5bc5a7e6 | 560 | }, $item['args']); |
390ec505 | 561 | |
2b6fbe94 | 562 | $item['file'] = sanitizePath($item['file']); |
5bc5a7e6 | 563 | } |
390ec505 | 564 | |
5bc5a7e6 TD |
565 | return $item; |
566 | }, $trace); | |
322ced57 | 567 | } |
390ec505 | 568 | |
5bc5a7e6 | 569 | function sanitizePath($path) { |
962c9241 TD |
570 | if (WCF::debugModeIsEnabled() && defined('EXCEPTION_PRIVACY') && EXCEPTION_PRIVACY === 'public') { |
571 | return $path; | |
572 | } | |
390ec505 | 573 | |
5bc5a7e6 | 574 | return '*/'.FileUtil::removeTrailingSlash(FileUtil::getRelativePath(WCF_DIR, $path)); |
322ced57 AE |
575 | } |
576 | } | |
fc23fa20 | 577 | // @codingStandardsIgnoreEnd |