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