0c499167a3458a68ba073cdaa7157274740c6802
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Error / ErrorHandler.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 use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19 /**
20 * Global error handler for TYPO3
21 *
22 * This file is a backport from TYPO3 Flow
23 */
24 class ErrorHandler implements ErrorHandlerInterface
25 {
26 /**
27 * Error levels which should result in an exception thrown.
28 *
29 * @var array
30 */
31 protected $exceptionalErrors = array();
32
33 /**
34 * Whether to write a flash message in case of an error
35 *
36 * @var bool
37 */
38 protected $debugMode = false;
39
40 /**
41 * Registers this class as default error handler
42 *
43 * @param int $errorHandlerErrors The integer representing the E_* error level which should be
44 */
45 public function __construct($errorHandlerErrors)
46 {
47 $excludedErrors = E_COMPILE_WARNING | E_COMPILE_ERROR | E_CORE_WARNING | E_CORE_ERROR | E_PARSE | E_ERROR;
48 // reduces error types to those a custom error handler can process
49 $errorHandlerErrors = $errorHandlerErrors & ~$excludedErrors;
50 set_error_handler(array($this, 'handleError'), $errorHandlerErrors);
51 }
52
53 /**
54 * Defines which error levels should result in an exception thrown.
55 *
56 * @param int $exceptionalErrors The integer representing the E_* error level to handle as exceptions
57 * @return void
58 */
59 public function setExceptionalErrors($exceptionalErrors)
60 {
61 $this->exceptionalErrors = (int)$exceptionalErrors;
62 }
63
64 /**
65 * @param bool $debugMode
66 */
67 public function setDebugMode($debugMode)
68 {
69 $this->debugMode = (bool)$debugMode;
70 }
71
72 /**
73 * Handles an error.
74 * If the error is registered as exceptionalError it will by converted into an exception, to be handled
75 * by the configured exceptionhandler. Additionally the error message is written to the configured logs.
76 * If TYPO3_MODE is 'BE' the error message is also added to the flashMessageQueue, in FE the error message
77 * is displayed in the admin panel (as TsLog message)
78 *
79 * @param int $errorLevel The error level - one of the E_* constants
80 * @param string $errorMessage The error message
81 * @param string $errorFile Name of the file the error occurred in
82 * @param int $errorLine Line number where the error occurred
83 * @return bool
84 * @throws Exception with the data passed to this method if the error is registered as exceptionalError
85 */
86 public function handleError($errorLevel, $errorMessage, $errorFile, $errorLine)
87 {
88 // Don't do anything if error_reporting is disabled by an @ sign
89 if (error_reporting() === 0) {
90 return true;
91 }
92 $errorLevels = array(
93 E_WARNING => 'Warning',
94 E_NOTICE => 'Notice',
95 E_USER_ERROR => 'User Error',
96 E_USER_WARNING => 'User Warning',
97 E_USER_NOTICE => 'User Notice',
98 E_STRICT => 'Runtime Notice',
99 E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
100 E_DEPRECATED => 'Runtime Deprecation Notice'
101 );
102 $message = 'PHP ' . $errorLevels[$errorLevel] . ': ' . $errorMessage . ' in ' . $errorFile . ' line ' . $errorLine;
103 if ($errorLevel & $this->exceptionalErrors) {
104 // handle error raised at early parse time
105 // autoloader not available & built-in classes not resolvable
106 if (!class_exists('stdClass', false)) {
107 $message = 'PHP ' . $errorLevels[$errorLevel] . ': ' . $errorMessage . ' in ' . basename($errorFile) .
108 'line ' . $errorLine;
109 die($message);
110 }
111 throw new Exception($message, 1);
112 } else {
113 switch ($errorLevel) {
114 case E_USER_ERROR:
115 case E_RECOVERABLE_ERROR:
116 $severity = 2;
117 break;
118 case E_USER_WARNING:
119 case E_WARNING:
120 $severity = 1;
121 break;
122 default:
123 $severity = 0;
124 }
125 $logTitle = 'Core: Error handler (' . TYPO3_MODE . ')';
126 $message = $logTitle . ': ' . $message;
127 // Write error message to the configured syslogs,
128 // see: $TYPO3_CONF_VARS['SYS']['systemLog']
129 if ($errorLevel & $GLOBALS['TYPO3_CONF_VARS']['SYS']['syslogErrorReporting']) {
130 GeneralUtility::sysLog($message, 'core', $severity + 1);
131 }
132 // Write error message to devlog extension(s),
133 // see: $TYPO3_CONF_VARS['SYS']['enable_errorDLOG']
134 if (TYPO3_ERROR_DLOG) {
135 GeneralUtility::devLog($message, 'core', $severity + 1);
136 }
137 // Write error message to TSlog (admin panel)
138 $timeTracker = $this->getTimeTracker();
139 if (is_object($timeTracker)) {
140 $timeTracker->setTSlogMessage($message, $severity + 1);
141 }
142 // Write error message to sys_log table (ext: belog, Tools->Log)
143 if ($errorLevel & $GLOBALS['TYPO3_CONF_VARS']['SYS']['belogErrorReporting']) {
144 // Silently catch in case an error occurs before a database connection exists,
145 // but DatabaseConnection fails to connect.
146 try {
147 $this->writeLog($message, $severity);
148 } catch (\Exception $e) {
149 }
150 }
151 if ($severity === 2) {
152 // Let the internal handler continue. This will stop the script
153 return false;
154 } else {
155 if ($this->debugMode) {
156 /** @var $flashMessage \TYPO3\CMS\Core\Messaging\FlashMessage */
157 $flashMessage = GeneralUtility::makeInstance(
158 \TYPO3\CMS\Core\Messaging\FlashMessage::class,
159 $message,
160 'PHP ' . $errorLevels[$errorLevel],
161 $severity
162 );
163 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
164 $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
165 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
166 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
167 $defaultFlashMessageQueue->enqueue($flashMessage);
168 }
169 // Don't execute PHP internal error handler
170 return true;
171 }
172 }
173 }
174
175 /**
176 * Writes an error in the sys_log table
177 *
178 * @param string $logMessage Default text that follows the message (in english!).
179 * @param int $severity The error level of the message (0 = OK, 1 = warning, 2 = error)
180 * @return void
181 */
182 protected function writeLog($logMessage, $severity)
183 {
184 $databaseConnection = $this->getDatabaseConnection();
185 if (is_object($databaseConnection) && $databaseConnection->isConnected()) {
186 $userId = 0;
187 $workspace = 0;
188 $backendUser = $this->getBackendUser();
189 if (is_object($backendUser)) {
190 if (isset($backendUser->user['uid'])) {
191 $userId = $backendUser->user['uid'];
192 }
193 if (isset($backendUser->workspace)) {
194 $workspace = $backendUser->workspace;
195 }
196 }
197 $fields_values = array(
198 'userid' => $userId,
199 'type' => 5,
200 'action' => 0,
201 'error' => $severity,
202 'details_nr' => 0,
203 'details' => str_replace('%', '%%', $logMessage),
204 'IP' => (string)GeneralUtility::getIndpEnv('REMOTE_ADDR'),
205 'tstamp' => $GLOBALS['EXEC_TIME'],
206 'workspace' => $workspace
207 );
208 $databaseConnection->exec_INSERTquery('sys_log', $fields_values);
209 }
210 }
211
212 /**
213 * @return \TYPO3\CMS\Core\TimeTracker\TimeTracker
214 */
215 protected function getTimeTracker()
216 {
217 return $GLOBALS['TT'];
218 }
219
220 /**
221 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
222 */
223 protected function getBackendUser()
224 {
225 return $GLOBALS['BE_USER'];
226 }
227
228 /**
229 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
230 */
231 protected function getDatabaseConnection()
232 {
233 return $GLOBALS['TYPO3_DB'];
234 }
235 }