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