5cccd9182b8609f6faf6d88c8660ba9f714ca1b2
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Error / DebugExceptionHandler.php
1 <?php
2 namespace TYPO3\CMS\Core\Error;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * A basic but solid exception handler which catches everything which
19 * falls through the other exception handlers and provides useful debugging
20 * information.
21 *
22 * This file is a backport from TYPO3 Flow
23 */
24 class DebugExceptionHandler extends AbstractExceptionHandler
25 {
26 /**
27 * Constructs this exception handler - registers itself as the default exception handler.
28 */
29 public function __construct()
30 {
31 set_exception_handler([$this, 'handleException']);
32 }
33
34 /**
35 * Formats and echoes the exception as XHTML.
36 *
37 * @param \Throwable $exception The throwable object.
38 */
39 public function echoExceptionWeb(\Throwable $exception)
40 {
41 $this->sendStatusHeaders($exception);
42 $filePathAndName = $exception->getFile();
43 $exceptionCodeNumber = $exception->getCode() > 0 ? '#' . $exception->getCode() . ': ' : '';
44 $moreInformationLink = $exceptionCodeNumber !== ''
45 ? '(<a href="' . TYPO3_URL_EXCEPTION . 'debug/' . $exception->getCode() . '" target="_blank">More information</a>)'
46 : '';
47 $backtraceCode = $this->getBacktraceCode($exception->getTrace());
48 $this->writeLogEntries($exception, self::CONTEXT_WEB);
49 echo '<?xml version="1.0" encoding="utf-8"?>
50 <!DOCTYPE html
51 PUBLIC "-//W3C//DTD XHTML 1.1//EN"
52 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
53 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
54 <head>
55 <title>TYPO3 Exception</title>
56 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
57 <style type="text/css">
58 .ExceptionProperty {
59 color: #101010;
60 }
61 pre {
62 margin: 0;
63 font-size: 11px;
64 color: #515151;
65 background-color: #D0D0D0;
66 padding-left: 30px;
67 }
68 </style>
69 </head>
70 <body>
71 <div style="
72 position: absolute;
73 left: 10px;
74 background-color: #B9B9B9;
75 outline: 1px solid #515151;
76 color: #515151;
77 font-family: Arial, Helvetica, sans-serif;
78 font-size: 12px;
79 margin: 10px;
80 padding: 0;
81 ">
82 <div style="width: 100%; background-color: #515151; color: white; padding: 2px; margin: 0 0 6px 0;">Uncaught TYPO3 Exception</div>
83 <div style="width: 100%; padding: 2px; margin: 0 0 6px 0;">
84 <strong style="color: #BE0027;">' . $exceptionCodeNumber . htmlspecialchars($exception->getMessage()) . '</strong> ' . $moreInformationLink . '<br />
85 <br />
86 <span class="ExceptionProperty">' . get_class($exception) . '</span> thrown in file<br />
87 <span class="ExceptionProperty">' . htmlspecialchars($filePathAndName) . '</span> in line
88 <span class="ExceptionProperty">' . $exception->getLine() . '</span>.<br />
89 <br />
90 ' . $backtraceCode . '
91 </div>
92 </div>
93 </body>
94 </html>
95 ';
96 }
97
98 /**
99 * Formats and echoes the exception for the command line
100 *
101 * @param \Throwable $exception The throwable object.
102 */
103 public function echoExceptionCLI(\Throwable $exception)
104 {
105 $filePathAndName = $exception->getFile();
106 $exceptionCodeNumber = $exception->getCode() > 0 ? '#' . $exception->getCode() . ': ' : '';
107 $this->writeLogEntries($exception, self::CONTEXT_CLI);
108 echo LF . 'Uncaught TYPO3 Exception ' . $exceptionCodeNumber . $exception->getMessage() . LF;
109 echo 'thrown in file ' . $filePathAndName . LF;
110 echo 'in line ' . $exception->getLine() . LF . LF;
111 die(1);
112 }
113
114 /**
115 * Renders some backtrace
116 *
117 * @param array $trace The trace
118 * @return string Backtrace information
119 */
120 protected function getBacktraceCode(array $trace)
121 {
122 $backtraceCode = '';
123 if (!empty($trace)) {
124 foreach ($trace as $index => $step) {
125 $class = isset($step['class']) ? htmlspecialchars($step['class']) . '<span style="color:white;">::</span>' : '';
126 $arguments = '';
127 if (isset($step['args']) && is_array($step['args'])) {
128 foreach ($step['args'] as $argument) {
129 $arguments .= (string)$arguments === '' ? '' : '<span style="color:white;">,</span> ';
130 if (is_object($argument)) {
131 $arguments .= '<span style="color:#FF8700;"><em>' . htmlspecialchars(get_class($argument)) . '</em></span>';
132 } elseif (is_string($argument)) {
133 $preparedArgument = strlen($argument) < 100
134 ? $argument
135 : substr($argument, 0, 50) . '#tripleDot#' . substr($argument, -50);
136 $preparedArgument = str_replace(
137 [
138 '#tripleDot#',
139 LF],
140 [
141 '<span style="color:white;">&hellip;</span>',
142 '<span style="color:white;">&crarr;</span>'
143 ],
144 htmlspecialchars($preparedArgument)
145 );
146 $arguments .= '"<span style="color:#FF8700;" title="' . htmlspecialchars($argument) . '">'
147 . $preparedArgument . '</span>"';
148 } elseif (is_numeric($argument)) {
149 $arguments .= '<span style="color:#FF8700;">' . (string)$argument . '</span>';
150 } else {
151 $arguments .= '<span style="color:#FF8700;"><em>' . gettype($argument) . '</em></span>';
152 }
153 }
154 }
155 $backtraceCode .= '<pre style="color:#69A550; background-color: #414141; padding: 4px 2px 4px 2px;">';
156 $backtraceCode .= '<span style="color:white;">' . (count($trace) - $index) . '</span> ' . $class
157 . $step['function'] . '<span style="color:white;">(' . $arguments . ')</span>';
158 $backtraceCode .= '</pre>';
159 if (isset($step['file'])) {
160 $backtraceCode .= $this->getCodeSnippet($step['file'], $step['line']) . '<br />';
161 }
162 }
163 }
164 return $backtraceCode;
165 }
166
167 /**
168 * Returns a code snippet from the specified file.
169 *
170 * @param string $filePathAndName Absolute path and file name of the PHP file
171 * @param int $lineNumber Line number defining the center of the code snippet
172 * @return string The code snippet
173 */
174 protected function getCodeSnippet($filePathAndName, $lineNumber)
175 {
176 $codeSnippet = '<br />';
177 if (@file_exists($filePathAndName)) {
178 $phpFile = @file($filePathAndName);
179 if (is_array($phpFile)) {
180 $startLine = $lineNumber > 2 ? $lineNumber - 2 : 1;
181 $phpFileCount = count($phpFile);
182 $endLine = $lineNumber < $phpFileCount - 2 ? $lineNumber + 3 : $phpFileCount + 1;
183 if ($endLine > $startLine) {
184 $codeSnippet = '<br /><span style="font-size:10px;">' . htmlspecialchars($filePathAndName) . ':</span><br /><pre>';
185 for ($line = $startLine; $line < $endLine; $line++) {
186 $codeLine = str_replace("\t", ' ', $phpFile[$line - 1]);
187 if ($line === $lineNumber) {
188 $codeSnippet .= '</pre><pre style="background-color: #F1F1F1; color: black;">';
189 }
190 $codeSnippet .= sprintf('%05d', $line) . ': ' . htmlspecialchars($codeLine);
191 if ($line === $lineNumber) {
192 $codeSnippet .= '</pre><pre>';
193 }
194 }
195 $codeSnippet .= '</pre>';
196 }
197 }
198 }
199 return $codeSnippet;
200 }
201 }