use wcf\system\exception\SystemException;
use wcf\util\FileUtil;
use wcf\util\StringUtil;
+
+ /**
+ * If the stacktrace contains a compiled template, the context of the relevant template line
+ * is returned, otherwise an empty array is returned.
+ */
+ function getTemplateContextLines(\Throwable $e): array {
+ try {
+ $contextLineCount = 5;
+ foreach ($e->getTrace() as $traceEntry) {
+ if (isset($traceEntry['file']) && \preg_match('~/templates/compiled/.+\.php$~',
+ $traceEntry['file'])) {
+ $startLine = $traceEntry['line'] - $contextLineCount;
+ $relativeErrorLine = $contextLineCount;
+ if ($startLine < 0) {
+ $startLine = 0;
+ $relativeErrorLine = $traceEntry['line'] - 1;
+ }
+
+ $file = \fopen($traceEntry['file'], 'r');
+ if (!$file) {
+ return [];
+ }
+
+ for ($line = 0; $line < $startLine; $line++) {
+ if (\substr(\fgets($file, 1024), -1) !== "\n") {
+ // We don't want to handle a file where lines exceed 1024 Bytes.
+ return [];
+ }
+ }
+
+ $maxLineCount = 2 * $contextLineCount + 1;
+ $lines = [];
+ while (!\feof($file) && \count($lines) < $maxLineCount) {
+ $line = \fgets($file, 1024);
+ if (\substr($line, -1) !== "\n" && !\feof($file)) {
+ // We don't want to handle a file where lines exceed 1024 Bytes.
+ return [];
+ }
+
+ if (count($lines) === $relativeErrorLine - 1) {
+ $line = "====> {$line}";
+ }
+
+ $lines[] = $line;
+ }
+
+ return $lines;
+ }
+ }
+ }
+ catch (\Throwable $e) {
+ // Ignore errors while extracting the template context to be saved in the exception log.
+ }
+
+ return [];
+ }
/**
* Logs the given Throwable.
return str_replace("\n", ' ', $item);
};
+ $getExtraInformation = function (\Throwable $e) {
+ $extraInformation = [];
+
+ if ($e instanceof IExtraInformationException) {
+ $extraInformation = $e->getExtraInformation();
+ }
+
+ $templateContextLines = getTemplateContextLines($e);
+ if (!empty($templateContextLines)) {
+ $extraInformation[] = [
+ 'Template Context',
+ \implode("", $templateContextLines),
+ ];
+ }
+
+ return !empty($extraInformation) ? base64_encode(serialize($extraInformation)) : "-";
+ };
+
// don't forget to update ExceptionLogUtil / ExceptionLogViewPage, when changing the log file format
$message = gmdate('r', TIME_NOW)."\n".
'Message: '.$stripNewlines($e->getMessage())."\n".
'Error Message: '.$stripNewlines($prev->getMessage())."\n".
'Error Code: '.$stripNewlines($prev->getCode())."\n".
'File: '.$stripNewlines($prev->getFile()).' ('.$prev->getLine().')'."\n".
- 'Extra Information: '.($prev instanceof IExtraInformationException ? base64_encode(serialize($prev->getExtraInformation())) : '-')."\n".
+ 'Extra Information: ' . $getExtraInformation($prev) . "\n".
'Stack Trace: '.json_encode(array_map(function ($item) {
$item['args'] = array_map(function ($item) {
switch (gettype($item)) {
min-height: 1.5em;
}
+ pre.exceptionFieldValue {
+ font-size: 14px;
+ white-space: pre-wrap;
+ }
+
.exceptionSystemInformation,
.exceptionErrorDetails,
.exceptionStacktrace {
<?php
}
}
+
+ $templateContextLines = getTemplateContextLines($e);
+ if (!empty($templateContextLines)) {
+ ?>
+ <li>
+ <p class="exceptionFieldTitle">Template Context<span class="exceptionColon">:</span></p>
+ <pre class="exceptionFieldValue"><?php echo StringUtil::encodeHTML(implode("", $templateContextLines));?></pre>
+ </li>
+ <?php
+ }
?>
<li>
<p class="exceptionFieldTitle">Stack Trace<span class="exceptionColon">:</span></p>