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