Cake/Error/ErrorHandler.php

1 <?php
2 /**
3 * Error handler
4 *
5 * Provides Error Capturing for Framework errors.
6 *
7 * PHP 5
8 *
9 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
10 * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
11 *
12 * Licensed under The MIT License
13 * Redistributions of files must retain the above copyright notice.
14 *
15 * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
16 * @link http://cakephp.org CakePHP(tm) Project
17 * @package Cake.Error
18 * @since CakePHP(tm) v 0.10.5.1732
19 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
20 */
21  
22 App::uses('Debugger', 'Utility');
23 App::uses('CakeLog', 'Log');
24 App::uses('ExceptionRenderer', 'Error');
25 App::uses('AppController', 'Controller');
26  
27 /**
28 *
29 * Error Handler provides basic error and exception handling for your application. It captures and
30 * handles all unhandled exceptions and errors. Displays helpful framework errors when debug > 1.
31 *
32 * ### Uncaught exceptions
33 *
34 * When debug < 1 a CakeException will render 404 or 500 errors. If an uncaught exception is thrown
35 * and it is a type that ErrorHandler does not know about it will be treated as a 500 error.
36 *
37 * ### Implementing application specific exception handling
38 *
39 * You can implement application specific exception handling in one of a few ways. Each approach
40 * gives you different amounts of control over the exception handling process.
41 *
42 * - Set Configure::write('Exception.handler', 'YourClass::yourMethod');
43 * - Create AppController::appError();
44 * - Set Configure::write('Exception.renderer', 'YourClass');
45 *
46 * #### Create your own Exception handler with `Exception.handler`
47 *
48 * This gives you full control over the exception handling process. The class you choose should be
49 * loaded in your app/Config/bootstrap.php, so its available to handle any exceptions. You can
50 * define the handler as any callback type. Using Exception.handler overrides all other exception
51 * handling settings and logic.
52 *
53 * #### Using `AppController::appError();`
54 *
55 * This controller method is called instead of the default exception rendering. It receives the
56 * thrown exception as its only argument. You should implement your error handling in that method.
57 * Using AppController::appError(), will supersede any configuration for Exception.renderer.
58 *
59 * #### Using a custom renderer with `Exception.renderer`
60 *
61 * If you don't want to take control of the exception handling, but want to change how exceptions are
62 * rendered you can use `Exception.renderer` to choose a class to render exception pages. By default
63 * `ExceptionRenderer` is used. Your custom exception renderer class should be placed in app/Lib/Error.
64 *
65 * Your custom renderer should expect an exception in its constructor, and implement a render method.
66 * Failing to do so will cause additional errors.
67 *
68 * #### Logging exceptions
69 *
70 * Using the built-in exception handling, you can log all the exceptions
71 * that are dealt with by ErrorHandler by setting `Exception.log` to true in your core.php.
72 * Enabling this will log every exception to CakeLog and the configured loggers.
73 *
74 * ### PHP errors
75 *
76 * Error handler also provides the built in features for handling php errors (trigger_error).
77 * While in debug mode, errors will be output to the screen using debugger. While in production mode,
78 * errors will be logged to CakeLog. You can control which errors are logged by setting
79 * `Error.level` in your core.php.
80 *
81 * #### Logging errors
82 *
83 * When ErrorHandler is used for handling errors, you can enable error logging by setting `Error.log` to true.
84 * This will log all errors to the configured log handlers.
85 *
86 * #### Controlling what errors are logged/displayed
87 *
88 * You can control which errors are logged / displayed by ErrorHandler by setting `Error.level`. Setting this
89 * to one or a combination of a few of the E_* constants will only enable the specified errors.
90 *
91 * e.g. `Configure::write('Error.level', E_ALL & ~E_NOTICE);`
92 *
93 * Would enable handling for all non Notice errors.
94 *
95 * @package Cake.Error
96 * @see ExceptionRenderer for more information on how to customize exception rendering.
97 */
98 class ErrorHandler {
99  
100 /**
101 * Set as the default exception handler by the CakePHP bootstrap process.
102 *
103 * This will either use custom exception renderer class if configured,
104 * or use the default ExceptionRenderer.
105 *
106 * @param Exception $exception
107 * @return void
108 * @see http://php.net/manual/en/function.set-exception-handler.php
109 */
110 public static function handleException(Exception $exception) {
111 $config = Configure::read('Exception');
112 if (!empty($config['log'])) {
113 $message = sprintf("[%s] %s\n%s",
114 get_class($exception),
115 $exception->getMessage(),
116 $exception->getTraceAsString()
117 );
118 CakeLog::write(LOG_ERR, $message);
119 }
120 $renderer = $config['renderer'];
121 if ($renderer !== 'ExceptionRenderer') {
122 list($plugin, $renderer) = pluginSplit($renderer, true);
123 App::uses($renderer, $plugin . 'Error');
124 }
125 try {
126 $error = new $renderer($exception);
127 $error->render();
128 } catch (Exception $e) {
129 set_error_handler(Configure::read('Error.handler')); // Should be using configured ErrorHandler
130 Configure::write('Error.trace', false); // trace is useless here since it's internal
131 $message = sprintf("[%s] %s\n%s", // Keeping same message format
132 get_class($e),
133 $e->getMessage(),
134 $e->getTraceAsString()
135 );
136 trigger_error($message, E_USER_ERROR);
137 }
138 }
139  
140 /**
141 * Set as the default error handler by CakePHP. Use Configure::write('Error.handler', $callback), to use your own
142 * error handling methods. This function will use Debugger to display errors when debug > 0. And
143 * will log errors to CakeLog, when debug == 0.
144 *
145 * You can use Configure::write('Error.level', $value); to set what type of errors will be handled here.
146 * Stack traces for errors can be enabled with Configure::write('Error.trace', true);
147 *
148 * @param integer $code Code of error
149 * @param string $description Error description
150 * @param string $file File on which error occurred
151 * @param integer $line Line that triggered the error
152 * @param array $context Context
153 * @return boolean true if error was handled
154 */
155 public static function handleError($code, $description, $file = null, $line = null, $context = null) {
156 if (error_reporting() === 0) {
157 return false;
158 }
159 $errorConfig = Configure::read('Error');
160 list($error, $log) = self::mapErrorCode($code);
161  
162 $debug = Configure::read('debug');
163 if ($debug) {
164 $data = array(
165 'level' => $log,
166 'code' => $code,
167 'error' => $error,
168 'description' => $description,
169 'file' => $file,
170 'line' => $line,
171 'context' => $context,
172 'start' => 2,
173 'path' => Debugger::trimPath($file)
174 );
175 return Debugger::getInstance()->outputError($data);
176 } else {
177 $message = $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']';
178 if (!empty($errorConfig['trace'])) {
179 $trace = Debugger::trace(array('start' => 1, 'format' => 'log'));
180 $message .= "\nTrace:\n" . $trace . "\n";
181 }
182 return CakeLog::write($log, $message);
183 }
184 }
185  
186 /**
187 * Map an error code into an Error word, and log location.
188 *
189 * @param integer $code Error code to map
190 * @return array Array of error word, and log location.
191 */
192 public static function mapErrorCode($code) {
193 $error = $log = null;
194 switch ($code) {
195 case E_PARSE:
196 case E_ERROR:
197 case E_CORE_ERROR:
198 case E_COMPILE_ERROR:
199 case E_USER_ERROR:
200 $error = 'Fatal Error';
201 $log = LOG_ERROR;
202 break;
203 case E_WARNING:
204 case E_USER_WARNING:
205 case E_COMPILE_WARNING:
206 case E_RECOVERABLE_ERROR:
207 $error = 'Warning';
208 $log = LOG_WARNING;
209 break;
210 case E_NOTICE:
211 case E_USER_NOTICE:
212 $error = 'Notice';
213 $log = LOG_NOTICE;
214 break;
215 case E_STRICT:
216 $error = 'Strict';
217 $log = LOG_NOTICE;
218 break;
219 case E_DEPRECATED:
220 case E_USER_DEPRECATED:
221 $error = 'Deprecated';
222 $log = LOG_NOTICE;
223 break;
224 }
225 return array($error, $log);
226 }
227  
228 }
229  
230