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