-
Notifications
You must be signed in to change notification settings - Fork 3.4k
/
ErrorHandler.php
201 lines (192 loc) · 6.75 KB
/
ErrorHandler.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
<?php
/**
* ErrorHandler class
*
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.5
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Error;
use Cake\Core\App;
use Cake\Http\ResponseEmitter;
use Exception;
use Throwable;
/**
* Error Handler provides basic error and exception handling for your application. It captures and
* handles all unhandled exceptions and errors. Displays helpful framework errors when debug mode is on.
*
* ### Uncaught exceptions
*
* When debug mode is off a ExceptionRenderer will render 404 or 500 errors. If an uncaught exception is thrown
* and it is a type that ExceptionRenderer does not know about it will be treated as a 500 error.
*
* ### Implementing application specific exception handling
*
* You can implement application specific exception handling in one of a few ways. Each approach
* gives you different amounts of control over the exception handling process.
*
* - Modify config/error.php and setup custom exception handling.
* - Use the `exceptionRenderer` option to inject an Exception renderer. This will
* let you keep the existing handling logic but override the rendering logic.
*
* #### Create your own Exception handler
*
* This gives you full control over the exception handling process. The class you choose should be
* loaded in your config/error.php and registered as the default exception handler.
*
* #### Using a custom renderer with `exceptionRenderer`
*
* If you don't want to take control of the exception handling, but want to change how exceptions are
* rendered you can use `exceptionRenderer` option to choose a class to render exception pages. By default
* `Cake\Error\ExceptionRenderer` is used. Your custom exception renderer class should be placed in src/Error.
*
* Your custom renderer should expect an exception in its constructor, and implement a render method.
* Failing to do so will cause additional errors.
*
* #### Logging exceptions
*
* Using the built-in exception handling, you can log all the exceptions
* that are dealt with by ErrorHandler by setting `log` option to true in your config/error.php.
* Enabling this will log every exception to Log and the configured loggers.
*
* ### PHP errors
*
* Error handler also provides the built in features for handling php errors (trigger_error).
* While in debug mode, errors will be output to the screen using debugger. While in production mode,
* errors will be logged to Log. You can control which errors are logged by setting
* `errorLevel` option in config/error.php.
*
* #### Logging errors
*
* When ErrorHandler is used for handling errors, you can enable error logging by setting the `log`
* option to true. This will log all errors to the configured log handlers.
*
* #### Controlling what errors are logged/displayed
*
* You can control which errors are logged / displayed by ErrorHandler by setting `errorLevel`. Setting this
* to one or a combination of a few of the E_* constants will only enable the specified errors:
*
* ```
* $options['errorLevel'] = E_ALL & ~E_NOTICE;
* ```
*
* Would enable handling for all non Notice errors.
*
* @see \Cake\Error\ExceptionRenderer for more information on how to customize exception rendering.
*/
class ErrorHandler extends BaseErrorHandler
{
/**
* Constructor
*
* @param array $options The options for error handling.
*/
public function __construct($options = [])
{
$defaults = [
'log' => true,
'trace' => false,
'exceptionRenderer' => ExceptionRenderer::class,
];
$this->_options = $options + $defaults;
}
/**
* Display an error.
*
* Template method of BaseErrorHandler.
*
* @param array $error An array of error data.
* @param bool $debug Whether or not the app is in debug mode.
* @return void
*/
protected function _displayError($error, $debug)
{
if (!$debug) {
return;
}
Debugger::getInstance()->outputError($error);
}
/**
* Displays an exception response body.
*
* @param \Exception $exception The exception to display.
* @return void
* @throws \Exception When the chosen exception renderer is invalid.
*/
protected function _displayException($exception)
{
$rendererClassName = App::className($this->_options['exceptionRenderer'], 'Error');
try {
if (!$rendererClassName) {
throw new Exception("$rendererClassName is an invalid class.");
}
/** @var \Cake\Error\ExceptionRendererInterface $renderer */
$renderer = new $rendererClassName($exception);
$response = $renderer->render();
$this->_clearOutput();
$this->_sendResponse($response);
} catch (Throwable $exception) {
$this->_logInternalError($exception);
} catch (Exception $exception) {
$this->_logInternalError($exception);
}
}
/**
* Clear output buffers so error pages display properly.
*
* Easily stubbed in testing.
*
* @return void
*/
protected function _clearOutput()
{
while (ob_get_level()) {
ob_end_clean();
}
}
/**
* Logs both PHP5 and PHP7 errors.
*
* The PHP5 part will be removed with 4.0.
*
* @param \Throwable|\Exception $exception Exception.
* @return void
*/
protected function _logInternalError($exception)
{
// Disable trace for internal errors.
$this->_options['trace'] = false;
$message = sprintf(
"[%s] %s (%s:%s)\n%s", // Keeping same message format
get_class($exception),
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
$exception->getTraceAsString()
);
trigger_error($message, E_USER_ERROR);
}
/**
* Method that can be easily stubbed in testing.
*
* @param string|\Cake\Http\Response $response Either the message or response object.
* @return void
*/
protected function _sendResponse($response)
{
if (is_string($response)) {
echo $response;
return;
}
$emitter = new ResponseEmitter();
$emitter->emit($response);
}
}