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