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