[FOLLOWUP][BUGFIX] Accept \Throwable in ExceptionHandlerInterface
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Error / AbstractExceptionHandler.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\DatabaseConnection;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19
20 /**
21 * An abstract exception handler
22 *
23 * This file is a backport from TYPO3 Flow
24 */
25 abstract class AbstractExceptionHandler implements ExceptionHandlerInterface, \TYPO3\CMS\Core\SingletonInterface
26 {
27 const CONTEXT_WEB = 'WEB';
28 const CONTEXT_CLI = 'CLI';
29
30 /**
31 * Displays the given exception
32 *
33 * @param \Exception|\Throwable $exception The exception(PHP 5.x) or throwable(PHP >= 7.0) object.
34 * @TODO #72293 This will change to \Throwable only if we are >= PHP7.0 only
35 *
36 * @throws \Exception
37 */
38 public function handleException($exception)
39 {
40 if ($exception instanceof \Throwable || $exception instanceof \Exception) {
41 switch (PHP_SAPI) {
42 case 'cli':
43 $this->echoExceptionCLI($exception);
44 break;
45 default:
46 $this->echoExceptionWeb($exception);
47 }
48 } else {
49 throw new \Exception('handleException was called the wrong way.', 1450714322);
50 }
51 }
52
53 /**
54 * Writes exception to different logs
55 *
56 * @param \Exception|\Throwable $exception The exception(PHP 5.x) or throwable(PHP >= 7.0) object.
57 * @param string $context The context where the exception was thrown, WEB or CLI
58 * @return void
59 * @see \TYPO3\CMS\Core\Utility\GeneralUtility::sysLog(), \TYPO3\CMS\Core\Utility\GeneralUtility::devLog()
60 * @TODO #72293 This will change to \Throwable only if we are >= PHP7.0 only
61 */
62 protected function writeLogEntries($exception, $context)
63 {
64 // Do not write any logs for this message to avoid filling up tables or files with illegal requests
65 if ($exception->getCode() === 1396795884) {
66 return;
67 }
68 $filePathAndName = $exception->getFile();
69 $exceptionCodeNumber = $exception->getCode() > 0 ? '#' . $exception->getCode() . ': ' : '';
70 $logTitle = 'Core: Exception handler (' . $context . ')';
71 $logMessage = 'Uncaught TYPO3 Exception: ' . $exceptionCodeNumber . $exception->getMessage() . ' | '
72 . get_class($exception) . ' thrown in file ' . $filePathAndName . ' in line ' . $exception->getLine();
73 if ($context === 'WEB') {
74 $logMessage .= '. Requested URL: ' . GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL');
75 }
76 $backtrace = $exception->getTrace();
77 // Write error message to the configured syslogs
78 GeneralUtility::sysLog($logMessage, $logTitle, GeneralUtility::SYSLOG_SEVERITY_FATAL);
79 // When database credentials are wrong, the exception is probably
80 // caused by this. Therefor we cannot do any database operation,
81 // otherwise this will lead into recurring exceptions.
82 try {
83 // Write error message to devlog
84 // see: $TYPO3_CONF_VARS['SYS']['enable_exceptionDLOG']
85 if (TYPO3_EXCEPTION_DLOG) {
86 GeneralUtility::devLog($logMessage, $logTitle, 3, array(
87 'TYPO3_MODE' => TYPO3_MODE,
88 'backtrace' => $backtrace
89 ));
90 }
91 // Write error message to sys_log table
92 $this->writeLog($logTitle . ': ' . $logMessage);
93 } catch (\Exception $exception) {
94 }
95 }
96
97 /**
98 * Writes an exception in the sys_log table
99 *
100 * @param string $logMessage Default text that follows the message.
101 * @return void
102 */
103 protected function writeLog($logMessage)
104 {
105 $databaseConnection = $this->getDatabaseConnection();
106 if (!is_object($databaseConnection) || !$databaseConnection->isConnected()) {
107 return;
108 }
109 $userId = 0;
110 $workspace = 0;
111 if (is_object($GLOBALS['BE_USER'])) {
112 if (isset($GLOBALS['BE_USER']->user['uid'])) {
113 $userId = $GLOBALS['BE_USER']->user['uid'];
114 }
115 if (isset($GLOBALS['BE_USER']->workspace)) {
116 $workspace = $GLOBALS['BE_USER']->workspace;
117 }
118 }
119 $fields_values = array(
120 'userid' => $userId,
121 'type' => 5,
122 'action' => 0,
123 'error' => 2,
124 'details_nr' => 0,
125 'details' => str_replace('%', '%%', $logMessage),
126 'IP' => (string)GeneralUtility::getIndpEnv('REMOTE_ADDR'),
127 'tstamp' => $GLOBALS['EXEC_TIME'],
128 'workspace' => $workspace
129 );
130 $databaseConnection->exec_INSERTquery('sys_log', $fields_values);
131 }
132
133 /**
134 * Sends the HTTP Status 500 code, if $exception is *not* a
135 * TYPO3\CMS\Core\Error\Http\StatusException and headers are not sent, yet.
136 *
137 * @param \Exception|\Throwable $exception The exception(PHP 5.x) or throwable(PHP >= 7.0) object.
138 * @return void
139 * @TODO #72293 This will change to \Throwable only if we are >= PHP7.0 only
140 */
141 protected function sendStatusHeaders($exception)
142 {
143 if (method_exists($exception, 'getStatusHeaders')) {
144 $headers = $exception->getStatusHeaders();
145 } else {
146 $headers = array(\TYPO3\CMS\Core\Utility\HttpUtility::HTTP_STATUS_500);
147 }
148 if (!headers_sent()) {
149 foreach ($headers as $header) {
150 header($header);
151 }
152 }
153 }
154
155 /**
156 * Gets the Database Object
157 * @return DatabaseConnection
158 */
159 protected function getDatabaseConnection()
160 {
161 return $GLOBALS['TYPO3_DB'];
162 }
163 }