use wcf\util\FileUtil;
use wcf\util\StringUtil;
- function logThrowable($e) {
- $logFile = WCF_DIR . 'log/' . gmdate('Y-m-d', TIME_NOW) . '.txt';
+ function logThrowable($logFile, $e) {
touch($logFile);
// don't forget to update ExceptionLogViewPage, when changing the log file format
}
function printThrowable($e) {
- $exceptionID = logThrowable($e); // TODO
+ $logFile = WCF_DIR . 'log/' . gmdate('Y-m-d', TIME_NOW) . '.txt';
+ $exceptionID = logThrowable($logFile, $e); // TODO
+
+ $exceptionTitle = $exceptionSubtitle = $exceptionExplanation = '';
+ $logFile = sanitizePath($logFile);
+ try {
+ $exceptionTitle = WCF::getLanguage()->get('wcf.global.exception.title', true);
+ $exceptionSubtitle = str_replace('{$exceptionID}', $exceptionID, WCF::getLanguage()->get('wcf.global.exception.subtitle', true));
+ $exceptionExplanation= str_replace('{$logFile}', $logFile, WCF::getLanguage()->get('wcf.global.exception.explanation', true));
+ }
+ catch (\Exception $e) {
+ // ignore
+ }
+ catch (\Throwable $e) {
+ // ignore
+ }
+
+ if (!$exceptionTitle || !$exceptionSubtitle || !$exceptionExplanation) {
+ // one or more failed, fallback to english
+ $exceptionTitle = 'An error has occured';
+ $exceptionSubtitle = 'Internal error code: <span class="exceptionInlineCodeWrapper"><span class="exceptionInlineCode">{$exceptionID}</span></span>';
+ $exceptionExplanation = <<<EXPLANATION
+<p class="exceptionSubtitle">What happened?</p>
+<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>
+<p class="exceptionText"> </p> <!-- required to ensure spacing after copy & paste -->
+<p class="exceptionText">
+ The error code can be used by an administrator to lookup the full error message in the Administration Control Panel via “Logs » Errors”.
+ In addition the error has been writen 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.
+</p>
+<p class="exceptionText"> </p> <!-- required to ensure spacing after copy & paste -->
+<p class="exceptionText">Notice: The error code was randomly generated and has no use beyond looking up the full message.</p>
+EXPLANATION;
+
+ }
+
+ /*
+ * A notice on the HTML used below:
+ *
+ * It might appear a bit weird to use <p> all over the place where semantically
+ * other elements would fit in way better. The reason behind this is that we avoid
+ * inheriting unwanted styles (e.g. exception displayed in an overlay) and that
+ * the output needs to be properly readable when copied & pasted somewhere.
+ *
+ * Besides the visual appearance, the output was built to provide a maximum of
+ * compatibility and readability when pasted somewhere else, e.g. a WYSIWYG editor
+ * without the potential of messing up the formatting and thus harming the readability.
+ */
?><!DOCTYPE html>
<html>
<head>
<?php } else { ?>
<title>Fatal Error</title>
<?php } ?>
- <meta charset="utf-8" />
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
<style>
- .exception {
- font-size: 13px !important;
- font-family: 'Trebuchet MS', Arial, sans-serif !important;
- color: #444 !important;
- text-align: left !important;
- border: 1px solid #036 !important;
- border-radius: 7px !important;
- background-color: #eee !important;
- overflow: auto !important;
+ .exceptionBody {
+ margin: 0;
+ padding: 0;
}
- .exception h1 {
- font-size: 130% !important;
- font-weight: bold !important;
- line-height: 1.1 !important;
- text-shadow: 0 -1px 0 #003 !important;
- color: #fff !important;
- word-wrap: break-word !important;
- border-bottom: 1px solid #036;
- border-radius: 6px 6px 0 0 !important;
- background-color: #369 !important;
- margin: 0 !important;
- padding: 5px 10px !important;
+
+ .exceptionContainer {
+ box-sizing: border-box;
+ font-family: 'Segoe UI', 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ padding-bottom: 20px;
}
- .exception div {
- border-top: 1px solid #fff !important;
- border-bottom-right-radius: 6px !important;
- border-bottom-left-radius: 6px !important;
- padding: 0 10px 10px !important;
+
+ .exceptionContainer * {
+ box-sizing: inherit;
+ color: #000;
+ line-height: 1.5em;
+ margin: 0;
+ padding: 0;
}
- .exception p {
- margin: 0 !important;
+
+ .exceptionHeader {
+ background-color: rgb(44, 62, 80);
+ padding: 20px 0;
}
- .exception h2 {
- font-size: 130% !important;
- font-weight: bold !important;
- color: #369 !important;
- text-shadow: 0 1px 0 #fff !important;
- margin: 5px 0 !important;
+
+ .exceptionTitle {
+ color: #fff;
+ font-size: 28px;
+ font-weight: 300;
}
- .exception code {
- padding: 0px 3px !important;
- border-radius: 3px !important;
- background-color: white !important;
- border: 1px solid #036 !important;
+
+ .exceptionErrorCode {
+ color: #fff;
+ margin-top: .5em;
}
- .exception .pre {
- white-space: pre !important;
- font-family: monospace; !important;
- text-overflow: ellipsis !important;
- overflow: hidden !important;
+
+ .exceptionErrorCode .exceptionInlineCode {
+ background-color: rgb(52, 73, 94);
+ border-radius: 3px;
+ color: #fff;
+ font-family: monospace;
+ padding: 3px 10px;
+ white-space: nowrap;
}
- .exception .pre:hover {
- text-overflow: clip !important;
- overflow: auto !important;
+
+ .exceptionSubtitle {
+ border-bottom: 1px solid rgb(238, 238, 238);
+ color: rgb(44, 62, 80);
+ font-size: 24px;
+ font-weight: 300;
+ margin-bottom: 15px;
+ padding-bottom: 10px;
}
- .exception dt {
- float: left !important;
- width: 200px !important;
- text-align: right !important;
- font-weight: bold !important;
+
+ .exceptionContainer > .exceptionBoundary {
+ margin-top: 30px;
}
- .exception dd {
- margin-left: 210px !important;
+
+ .exceptionText .exceptionInlineCodeWrapper {
+ border: 1px solid rgb(169, 169, 169);
+ border-radius: 3px;
+ padding: 2px 5px;
}
- .exception dd::before {
- content: "\FEFF" !important;
+
+ .exceptionText .exceptionInlineCode {
+ font-family: monospace;
+ white-space: nowrap;
}
- .exception dl {
- margin: 0 !important;
+
+ .exceptionFieldTitle {
+ color: rgb(59, 109, 169);
}
- .exception dl::after {
- clear: both !important;
+
+ .exceptionFieldTitle .exceptionColon {
+ /* hide colon in browser, but will be visible after copy & paste */
+ opacity: 0;
}
- </style>
- </head>
- <body>
- <div class="exception">
- <?php if (!defined('EXCEPTION_PRIVACY') || EXCEPTION_PRIVACY !== 'private') { ?>
- <h1>Fatal Error: <?php echo StringUtil::encodeHTML($e->getMessage()); ?></h1>
- <?php } else { ?>
- <h1>Fatal Error <!-- :( --></h1>
- <?php } ?>
- <div>
- <?php
- $message = '
- <h2>What happened?</h2>
- <p>An unrecoverable error occured while trying to handle your request. The internal error code is as follows: <code><?php echo $exceptionID; ?></code></p>
- <p>Please send this code to the administrator to help him fix the issue.</p>
- <p>If you are the administrator you can view the complete error message at "ACP > Logs > Errors" einsehen. The error code itself is worthless for the support!</p>
- ';
- try {
- $message = str_replace('{$exceptionID}', $exceptionID, WCF::getLanguage()->get('wcf.global.error.exception', true));
+
+ .exceptionFieldValue {
+ font-size: 18px;
+ }
+
+ .exceptionSystemInformation,
+ .exceptionErrorDetails,
+ .exceptionStacktrace {
+ list-style-type: none;
+ }
+
+ .exceptionSystemInformation > li:not(:first-child),
+ .exceptionErrorDetails > li:not(:first-child) {
+ margin-top: 10px;
+ }
+
+ .exceptionStacktrace {
+ display: block;
+ margin-top: 5px;
+ overflow: auto;
+ padding-bottom: 20px;
+ }
+
+ .exceptionStacktraceFile,
+ .exceptionStacktraceFile span,
+ .exceptionStacktraceCall,
+ .exceptionStacktraceCall span {
+ font-family: monospace !important;
+ white-space: nowrap !important;
+ }
+
+ .exceptionStacktraceCall + .exceptionStacktraceFile {
+ margin-top: 5px;
+ }
+
+ .exceptionStacktraceCall {
+ padding-left: 40px;
+ }
+
+ .exceptionStacktraceCall,
+ .exceptionStacktraceCall span {
+ color: rgb(102, 102, 102) !important;
+ font-size: 13px !important;
+ }
+
+ /* mobile */
+ @media (max-width: 767px) {
+ .exceptionBoundary {
+ min-width: 320px;
+ padding: 0 10px;
}
- catch (\Exception $e) {
-
+
+ .exceptionText .exceptionInlineCodeWrapper {
+ display: inline-block;
+ overflow: auto;
}
- catch (\Throwable $e) {
-
+
+ .exceptionErrorCode .exceptionInlineCode {
+ font-size: 13px;
+ padding: 2px 5px;
}
- echo $message;
- ?>
+ }
+
+ /* desktop */
+ @media (min-width: 768px) {
+ .exceptionBoundary {
+ margin: 0 auto;
+ max-width: 1400px;
+ min-width: 1200px;
+ padding: 0 10px;
+ }
+
+ .exceptionSystemInformation {
+ display: flex;
+ flex-wrap: wrap;
+ }
+
+ .exceptionSystemInformation1,
+ .exceptionSystemInformation3,
+ .exceptionSystemInformation5 {
+ flex: 0 0 200px;
+ margin: 0 0 10px 0 !important;
+ }
+
+ .exceptionSystemInformation2,
+ .exceptionSystemInformation4,
+ .exceptionSystemInformation6 {
+ flex: 0 0 calc(100% - 210px);
+ margin: 0 0 10px 10px !important;
+ max-width: calc(100% - 210px);
+ }
+
+ .exceptionSystemInformation1 { order: 1; }
+ .exceptionSystemInformation2 { order: 2; }
+ .exceptionSystemInformation3 { order: 3; }
+ .exceptionSystemInformation4 { order: 4; }
+ .exceptionSystemInformation5 { order: 5; }
+ .exceptionSystemInformation6 { order: 6; }
+
+ .exceptionSystemInformation .exceptionFieldValue {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ }
+ </style>
+ </head>
+ <body class="exceptionBody">
+ <div class="exceptionContainer">
+ <div class="exceptionHeader">
+ <div class="exceptionBoundary">
+ <p class="exceptionTitle"><?php echo $exceptionTitle; ?></p>
+ <p class="exceptionErrorCode"><?php echo str_replace('{$exceptionID}', $exceptionID, $exceptionSubtitle); ?></p>
+ </div>
+ </div>
+
+ <div class="exceptionBoundary">
+ <?php echo $exceptionExplanation; ?>
</div>
<?php if (!defined('EXCEPTION_PRIVACY') || EXCEPTION_PRIVACY !== 'private') { ?>
- <div>
- <h2>System Information</h2>
- <dl>
- <dt>PHP Version</dt> <dd><?php echo StringUtil::encodeHTML(phpversion()); ?></dd>
- <dt>WCF Version</dt> <dd><?php echo StringUtil::encodeHTML(WCF_VERSION); ?></dd>
- <dt>Date</dt> <dd><?php echo gmdate('r'); ?></dd>
- <dt>Request URI</dt> <dd><?php if (isset($_SERVER['REQUEST_URI'])) echo StringUtil::encodeHTML($_SERVER['REQUEST_URI']); ?></dd>
- <dt>Referrer</dt> <dd><?php if (isset($_SERVER['HTTP_REFERER'])) echo StringUtil::encodeHTML($_SERVER['HTTP_REFERER']); ?></dd>
- <dt>User Agent</dt> <dd><?php if (isset($_SERVER['HTTP_USER_AGENT'])) echo StringUtil::encodeHTML($_SERVER['HTTP_USER_AGENT']); ?></dd>
- <dt>Peak Memory Usage</dt> <dd><?php echo $peakMemory = memory_get_peak_usage(); ?>/<?php echo $memoryLimit = FileUtil::getMemoryLimit(); ?> Byte (<?php echo round($peakMemory / 1024 / 1024, 3); ?>/<?php echo round($memoryLimit / 1024 / 1024, 3); ?> MiB)</dd>
- </dl>
+ <div class="exceptionBoundary">
+ <p class="exceptionSubtitle">System Information</p>
+ <ul class="exceptionSystemInformation">
+ <li class="exceptionSystemInformation1">
+ <p class="exceptionFieldTitle">PHP Version<span class="exceptionColon">:</span></p>
+ <p class="exceptionFieldValue"><?php echo StringUtil::encodeHTML(phpversion()); ?></p>
+ </li>
+ <li class="exceptionSystemInformation3">
+ <p class="exceptionFieldTitle">WCF Version<span class="exceptionColon">:</span></p>
+ <p class="exceptionFieldValue"><?php echo StringUtil::encodeHTML(WCF_VERSION); ?></p>
+ </li>
+ <li class="exceptionSystemInformation5">
+ <p class="exceptionFieldTitle">Peak Memory Usage<span class="exceptionColon">:</span></p>
+ <p class="exceptionFieldValue"><?php echo round(memory_get_peak_usage() / 1024 / 1024, 3); ?>/<?php echo round(FileUtil::getMemoryLimit() / 1024 / 1024, 3); ?> MiB</p>
+ </li>
+ <li class="exceptionSystemInformation2">
+ <p class="exceptionFieldTitle">Request URI<span class="exceptionColon">:</span></p>
+ <p class="exceptionFieldValue"><?php if (isset($_SERVER['REQUEST_URI'])) echo StringUtil::encodeHTML($_SERVER['REQUEST_URI']); ?></p>
+ </li>
+ <li class="exceptionSystemInformation4">
+ <p class="exceptionFieldTitle">Referrer<span class="exceptionColon">:</span></p>
+ <p class="exceptionFieldValue"><?php if (isset($_SERVER['HTTP_REFERER'])) echo StringUtil::encodeHTML($_SERVER['HTTP_REFERER']); ?></p>
+ </li>
+ <li class="exceptionSystemInformation6">
+ <p class="exceptionFieldTitle">User Agent<span class="exceptionColon">:</span></p>
+ <p class="exceptionFieldValue"><?php if (isset($_SERVER['HTTP_USER_AGENT'])) echo StringUtil::encodeHTML($_SERVER['HTTP_USER_AGENT']); ?></p>
+ </li>
+ </ul>
</div>
+
<?php
$first = true;
do {
?>
- <div>
- <h2><?php if (!$e->getPrevious() && !$first) { echo "Original "; } else if ($e->getPrevious() && $first) { echo "Final "; } ?>Error</h2>
+ <div class="exceptionBoundary">
+ <p class="exceptionSubtitle"><?php if (!$e->getPrevious() && !$first) { echo "Original "; } else if ($e->getPrevious() && $first) { echo "Final "; } ?>Error</p>
<?php if ($e instanceof SystemException && $e->getDescription()) { ?>
- <p><?php echo $e->getDescription(); ?></p>
+ <p class="exceptionText"><?php echo $e->getDescription(); ?></p>
<?php } ?>
- <dl>
- <dt>Error Class</dt> <dd><?php echo get_class($e); ?></dd>
- <dt>Error Message</dt> <dd><?php echo StringUtil::encodeHTML($e->getMessage()); ?></dd>
- <?php if ($e->getCode()) { ?><dt>Error Code</dt> <dd><?php echo intval($e->getCode()); ?></dd><?php } ?>
- <dt>File</dt> <dd><?php echo StringUtil::encodeHTML(sanitizePath($e->getFile())); ?> (<?php echo $e->getLine(); ?>)</dd>
+ <ul class="exceptionErrorDetails">
+ <li>
+ <p class="exceptionFieldTitle">Error Message<span class="exceptionColon">:</span></p>
+ <p class="exceptionFieldValue"><?php echo StringUtil::encodeHTML($e->getMessage()); ?></p>
+ </li>
+ <?php if ($e->getCode()) { ?>
+ <li>
+ <p class="exceptionFieldTitle">Error Code<span class="exceptionColon">:</span></p>
+ <p class="exceptionFieldValue"><?php echo intval($e->getCode()); ?></p>
+ </li>
+ <?php } ?>
+ <li>
+ <p class="exceptionFieldTitle">File<span class="exceptionColon">:</span></p>
+ <p class="exceptionFieldValue" style="word-break: break-all"><?php echo StringUtil::encodeHTML(sanitizePath($e->getFile())); ?> (<?php echo $e->getLine(); ?>)</p>
+ </li>
+
<?php
if ($e instanceof SystemException) {
ob_start();
}
if ($e instanceof IExtraInformationException) {
foreach ($e->getExtraInformation() as list($key, $value)) {
- echo "<dt>".StringUtil::encodeHTML($key)."</dt> <dd>".StringUtil::encodeHTML($value)."</dd>";
+ ?>
+ <li>
+ <p class="exceptionFieldTitle"><?php echo StringUtil::encodeHTML($key); ?><span class="exceptionColon">:</span></p>
+ <p class="exceptionFieldValue"><?php echo StringUtil::encodeHTML($value); ?></p>
+ </li>
+ <?php
}
}
?>
- <dt>Stack Trace</dt>
- <dd class="pre"><?php
- $trace = sanitizeStacktrace($e);
- $pathLength = array_reduce($trace, function ($carry, $item) {
- return max($carry, mb_strlen($item['file'].$item['line']));
- }, 0) + 3;
- for ($i = 0, $max = count($trace); $i < $max; $i++) {
- echo str_pad('#'.$i, strlen($max - 1) + 1, ' ', STR_PAD_RIGHT).' '.str_pad(StringUtil::encodeHTML($trace[$i]['file']).' ('.$trace[$i]['line'].')', $pathLength, ' ', STR_PAD_RIGHT).':';
- echo ' '.$trace[$i]['class'].$trace[$i]['type'].$trace[$i]['function'].'(';
- echo implode(', ', array_map(function ($item) {
- switch (gettype($item)) {
- case 'integer':
- case 'double':
- return $item;
- case 'NULL':
- return 'null';
- case 'string':
- return '<span title="'.StringUtil::encodeHTML($item).'">"'.StringUtil::encodeHTML(addcslashes(StringUtil::truncate($item, 25, StringUtil::HELLIP, true), "\n")).'"</span>';
- case 'boolean':
- return $item ? 'true' : 'false';
- case 'array':
- $keys = array_keys($item);
- if (count($keys) > 5) return "[ ".count($keys)." items ]";
- return '[ '.implode(', ', array_map(function ($item) {
- return $item.' => ';
- }, $keys)).']';
- case 'object':
- return get_class($item);
- }
- }, $trace[$i]['args']));
- echo ")\n";
- }
- ?></dd>
- </dl>
+ <li>
+ <p class="exceptionFieldTitle">Stack Trace<span class="exceptionColon">:</span></p>
+ <ul class="exceptionStacktrace">
+ <?php
+ $trace = sanitizeStacktrace($e);
+ $pathLength = array_reduce($trace, function ($carry, $item) {
+ return max($carry, mb_strlen($item['file'].$item['line']));
+ }, 0) + 3;
+ for ($i = 0, $max = count($trace); $i < $max; $i++) {
+ ?>
+ <li class="exceptionStacktraceFile"><?php echo '#'.$i.' '.StringUtil::encodeHTML($trace[$i]['file']).' ('.$trace[$i]['line'].')'.':'; ?></li>
+ <li class="exceptionStacktraceCall">
+ <?php
+ echo $trace[$i]['class'].$trace[$i]['type'].$trace[$i]['function'].'(';
+ echo implode(', ', array_map(function ($item) {
+ switch (gettype($item)) {
+ case 'integer':
+ case 'double':
+ return $item;
+ case 'NULL':
+ return 'null';
+ case 'string':
+ return StringUtil::encodeHTML($item);
+ case 'boolean':
+ return $item ? 'true' : 'false';
+ case 'array':
+ $keys = array_keys($item);
+ if (count($keys) > 5) return "[ ".count($keys)." items ]";
+ return '[ '.implode(', ', array_map(function ($item) {
+ return $item.' => ';
+ }, $keys)).']';
+ case 'object':
+ return get_class($item);
+ }
+ }, $trace[$i]['args']));
+ echo ')</li>';
+ }
+ ?>
+ </ul>
+ </li>
+ </ul>
</div>
<?php
$first = false;