‪TYPO3CMS  11.5
ErrorHandler.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
16 namespace ‪TYPO3\CMS\Core\Error;
17 
18 use Psr\Http\Message\ServerRequestInterface;
19 use Psr\Log\LoggerAwareInterface;
20 use Psr\Log\LoggerAwareTrait;
21 use Psr\Log\LogLevel;
29 use ‪TYPO3\CMS\Core\SysLog\Error as SystemLogErrorClassification;
30 use ‪TYPO3\CMS\Core\SysLog\Type as SystemLogType;
33 
39 class ‪ErrorHandler implements ‪ErrorHandlerInterface, LoggerAwareInterface
40 {
41  use LoggerAwareTrait;
42 
46  protected int ‪$exceptionalErrors = 0;
47 
51  protected int ‪$errorHandlerErrors = 0;
52 
56  protected bool ‪$debugMode = false;
57 
58  protected const ‪ERROR_LEVEL_LABELS = [
59  E_WARNING => 'PHP Warning',
60  E_NOTICE => 'PHP Notice',
61  E_USER_ERROR => 'PHP User Error',
62  E_USER_WARNING => 'PHP User Warning',
63  E_USER_NOTICE => 'PHP User Notice',
64  E_STRICT => 'PHP Runtime Notice',
65  E_RECOVERABLE_ERROR => 'PHP Catchable Fatal Error',
66  E_USER_DEPRECATED => 'TYPO3 Deprecation Notice',
67  E_DEPRECATED => 'PHP Runtime Deprecation Notice',
68  ];
69 
76  {
77  $excludedErrors = E_COMPILE_WARNING | E_COMPILE_ERROR | E_CORE_WARNING | E_CORE_ERROR | E_PARSE | E_ERROR;
78  // reduces error types to those a custom error handler can process
79  $this->errorHandlerErrors = (int)‪$errorHandlerErrors & ~$excludedErrors;
80  }
81 
88  {
90  // We always disallow E_USER_DEPRECATED to generate exceptions as this may cause
91  // bad user experience specifically during upgrades.
92  $this->exceptionalErrors = ‪$exceptionalErrors & ~E_USER_DEPRECATED;
93  }
94 
98  public function ‪setDebugMode(‪$debugMode)
99  {
100  $this->debugMode = (bool)‪$debugMode;
101  }
102 
103  public function ‪registerErrorHandler()
104  {
105  set_error_handler([$this, 'handleError']);
106  }
107 
122  public function ‪handleError($errorLevel, $errorMessage, $errorFile, $errorLine)
123  {
124  // Filter all errors, that should not be reported/ handled from current error reporting
125  $reportingLevel = $this->errorHandlerErrors & error_reporting();
126  // Since symfony does this:
127  // @trigger_error('...', E_USER_DEPRECATED), and we DO want to log these,
128  // we always enforce deprecation messages to be handled, even when they are silenced
129  $reportingLevel |= E_USER_DEPRECATED;
130  $shouldHandleError = (bool)($reportingLevel & $errorLevel);
131  if (!$shouldHandleError) {
132  return ‪self::ERROR_HANDLED;
133  }
134 
135  $message = self::ERROR_LEVEL_LABELS[$errorLevel] . ': ' . $errorMessage . ' in ' . $errorFile . ' line ' . $errorLine;
136  if ($errorLevel & $this->exceptionalErrors) {
137  throw new ‪Exception($message, 1476107295);
138  }
139 
140  $message = $this->‪getFormattedLogMessage($message);
141 
142  if ($errorLevel === E_USER_DEPRECATED || $errorLevel === E_DEPRECATED) {
143  $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger('TYPO3.CMS.deprecations');
144  $logger->notice($message);
145  return ‪self::ERROR_HANDLED;
146  }
147 
148  switch ($errorLevel) {
149  case E_USER_ERROR:
150  case E_RECOVERABLE_ERROR:
151  $logLevel = LogLevel::ERROR;
152  break;
153  case E_USER_WARNING:
154  case E_WARNING:
155  $logLevel = LogLevel::WARNING;
156  break;
157  default:
158  $logLevel = LogLevel::NOTICE;
159  }
160 
161  if ($this->logger) {
162  $this->logger->log($logLevel, $message);
163  }
164 
165  try {
166  // Write error message to TSlog (admin panel)
167  $this->‪getTimeTracker()->setTSlogMessage($message, $logLevel);
168  } catch (\Throwable $e) {
169  // Silently catch in case an error occurs before the DI container is in place
170  }
171  // Write error message to sys_log table (ext: belog, Tools->Log)
172  if ($errorLevel & (‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['belogErrorReporting'] ?? 0)) {
173  // Silently catch in case an error occurs before a database connection exists.
174  try {
175  $this->‪writeLog($message, $logLevel);
176  } catch (\‪Exception $e) {
177  }
178  }
179  if ($logLevel === LogLevel::ERROR) {
180  // Let the internal handler continue. This will stop the script
182  }
183  if ($this->debugMode) {
184  $this->‪createAndEnqueueFlashMessage($message, $errorLevel);
185  }
186  // Don't execute PHP internal error handler
187  return ‪self::ERROR_HANDLED;
188  }
189 
190  protected function ‪createAndEnqueueFlashMessage(string $message, int $errorLevel): void
191  {
192  switch ($errorLevel) {
193  case E_USER_WARNING:
194  case E_WARNING:
195  $flashMessageSeverity = ‪FlashMessage::WARNING;
196  break;
197  default:
198  $flashMessageSeverity = ‪FlashMessage::NOTICE;
199  }
200  $flashMessage = GeneralUtility::makeInstance(
201  FlashMessage::class,
202  $message,
203  self::ERROR_LEVEL_LABELS[$errorLevel],
204  $flashMessageSeverity
205  );
206  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
207  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
208  $defaultFlashMessageQueue->enqueue($flashMessage);
209  }
210 
217  protected function ‪writeLog($logMessage, string $logLevel)
218  {
219  // Avoid ConnectionPool usage prior boot completion, as that is deprecated since #94979.
220  if (!GeneralUtility::getContainer()->get('boot.state')->complete) {
221  if ($this->logger) {
222  // Log via debug(), the original message has already been logged with the original serverity in handleError().
223  // This log entry is targeted for users that try to debug why a log record is missing in sys_log
224  // while it has been logged to the logging framework.
225  $this->logger->debug(
226  'An error could not be logged to database as it appeared during early bootstrap (ext_tables.php or ext_localconf.php loading).',
227  ['original_message' => $logMessage, 'original_loglevel' => $logLevel]
228  );
229  }
230  return;
231  }
232  $connection = GeneralUtility::makeInstance(ConnectionPool::class)
233  ->getConnectionForTable('sys_log');
234  if ($connection->isConnected()) {
235  $userId = 0;
236  $workspace = 0;
237  $data = [];
238  $backendUser = $this->‪getBackendUser();
239  if (is_object($backendUser)) {
240  if (isset($backendUser->user['uid'])) {
241  $userId = $backendUser->user['uid'];
242  }
243  if (isset($backendUser->workspace)) {
244  $workspace = $backendUser->workspace;
245  }
246  if ($backUserId = $backendUser->getOriginalUserIdWhenInSwitchUserMode()) {
247  $data['originalUser'] = $backUserId;
248  }
249  }
250 
251  $connection->insert(
252  'sys_log',
253  [
254  'userid' => $userId,
255  'type' => SystemLogType::ERROR,
256  'channel' => SystemLogType::toChannel(SystemLogType::ERROR),
257  'action' => SystemLogGenericAction::UNDEFINED,
258  'error' => SystemLogErrorClassification::SYSTEM_ERROR,
259  'level' => $logLevel,
260  'details_nr' => 0,
261  'details' => str_replace('%', '%%', $logMessage),
262  'log_data' => empty($data) ? '' : serialize($data),
263  'IP' => (string)GeneralUtility::getIndpEnv('REMOTE_ADDR'),
264  'tstamp' => ‪$GLOBALS['EXEC_TIME'],
265  'workspace' => $workspace,
266  ]
267  );
268  }
269  }
270 
271  protected function ‪getFormattedLogMessage(string $message): string
272  {
273  // String 'FE' if in FrontendApplication, else 'BE' (also in CLI without request object)
274  $applicationType = (‪$GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
275  && ‪ApplicationType::fromRequest(‪$GLOBALS['TYPO3_REQUEST'])->isFrontend() ? 'FE' : 'BE';
276  $logPrefix = 'Core: Error handler (' . $applicationType . ')';
277  return $logPrefix . ': ' . $message;
278  }
279 
280  protected function ‪getTimeTracker(): ‪TimeTracker
281  {
282  return GeneralUtility::makeInstance(TimeTracker::class);
283  }
284 
286  {
287  return ‪$GLOBALS['BE_USER'] ?? null;
288  }
289 }
‪TYPO3\CMS\Core\Http\ApplicationType\fromRequest
‪static static fromRequest(ServerRequestInterface $request)
Definition: ApplicationType.php:62
‪TYPO3\CMS\Core\SysLog\Action
Definition: Cache.php:18
‪TYPO3\CMS\Core\Error\ErrorHandler\$debugMode
‪bool $debugMode
Definition: ErrorHandler.php:56
‪TYPO3\CMS\Core\Error\ErrorHandler\getBackendUser
‪getBackendUser()
Definition: ErrorHandler.php:285
‪TYPO3\CMS\Core\Error\ErrorHandler\$errorHandlerErrors
‪int $errorHandlerErrors
Definition: ErrorHandler.php:51
‪TYPO3\CMS\Core\Error\ErrorHandlerInterface\PROPAGATE_ERROR
‪const PROPAGATE_ERROR
Definition: ErrorHandlerInterface.php:27
‪TYPO3\CMS\Core\Error\ErrorHandler\$exceptionalErrors
‪int $exceptionalErrors
Definition: ErrorHandler.php:46
‪TYPO3\CMS\Core\Error\ErrorHandler\setExceptionalErrors
‪setExceptionalErrors($exceptionalErrors)
Definition: ErrorHandler.php:87
‪TYPO3\CMS\Core\Http\ApplicationType
Definition: ApplicationType.php:52
‪TYPO3\CMS\Core\Error\ErrorHandler\createAndEnqueueFlashMessage
‪createAndEnqueueFlashMessage(string $message, int $errorLevel)
Definition: ErrorHandler.php:190
‪TYPO3\CMS\Core\Messaging\AbstractMessage\WARNING
‪const WARNING
Definition: AbstractMessage.php:30
‪TYPO3\CMS\Core\Error\ErrorHandlerInterface
Definition: ErrorHandlerInterface.php:24
‪TYPO3\CMS\Core\Error\ErrorHandler\__construct
‪__construct($errorHandlerErrors)
Definition: ErrorHandler.php:75
‪TYPO3\CMS\Core\Error\ErrorHandler\setDebugMode
‪setDebugMode($debugMode)
Definition: ErrorHandler.php:98
‪TYPO3\CMS\Core\Error\ErrorHandlerInterface\ERROR_HANDLED
‪const ERROR_HANDLED
Definition: ErrorHandlerInterface.php:26
‪TYPO3\CMS\Core\SysLog\Error
Definition: Error.php:24
‪TYPO3\CMS\Core\Error\ErrorHandler\registerErrorHandler
‪registerErrorHandler()
Definition: ErrorHandler.php:103
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Core\Error\ErrorHandler\handleError
‪bool handleError($errorLevel, $errorMessage, $errorFile, $errorLine)
Definition: ErrorHandler.php:122
‪TYPO3\CMS\Core\Error\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:26
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Log\LogManager
Definition: LogManager.php:33
‪TYPO3\CMS\Core\Error\ErrorHandler\getFormattedLogMessage
‪getFormattedLogMessage(string $message)
Definition: ErrorHandler.php:271
‪TYPO3\CMS\Core\Error
Definition: AbstractExceptionHandler.php:16
‪TYPO3\CMS\Core\Error\ErrorHandler\writeLog
‪writeLog($logMessage, string $logLevel)
Definition: ErrorHandler.php:217
‪TYPO3\CMS\Core\Messaging\AbstractMessage\NOTICE
‪const NOTICE
Definition: AbstractMessage.php:27
‪TYPO3\CMS\Core\Error\ErrorHandler\getTimeTracker
‪getTimeTracker()
Definition: ErrorHandler.php:280
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:31
‪TYPO3\CMS\Core\Error\ErrorHandler\ERROR_LEVEL_LABELS
‪const ERROR_LEVEL_LABELS
Definition: ErrorHandler.php:58
‪TYPO3\CMS\Core\Messaging\FlashMessageService
Definition: FlashMessageService.php:27
‪TYPO3\CMS\Core\SysLog\Type
Definition: Type.php:28
‪TYPO3\CMS\Core\Error\ErrorHandler
Definition: ErrorHandler.php:40