pictcode / lib / Cake / Error / ExceptionRenderer.php @ 635eef61
履歴 | 表示 | アノテート | ダウンロード (9.644 KB)
| 1 | 635eef61 | spyder1211 | <?php
|
|---|---|---|---|
| 2 | /**
|
||
| 3 | * Exception Renderer
|
||
| 4 | *
|
||
| 5 | * Provides Exception rendering features. Which allow exceptions to be rendered
|
||
| 6 | * as HTML pages.
|
||
| 7 | *
|
||
| 8 | * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||
| 9 | * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||
| 10 | *
|
||
| 11 | * Licensed under The MIT License
|
||
| 12 | * For full copyright and license information, please see the LICENSE.txt
|
||
| 13 | * Redistributions of files must retain the above copyright notice.
|
||
| 14 | *
|
||
| 15 | * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||
| 16 | * @link http://cakephp.org CakePHP(tm) Project
|
||
| 17 | * @package Cake.Error
|
||
| 18 | * @since CakePHP(tm) v 2.0
|
||
| 19 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||
| 20 | */
|
||
| 21 | |||
| 22 | App::uses('Sanitize', 'Utility'); |
||
| 23 | App::uses('Router', 'Routing'); |
||
| 24 | App::uses('CakeResponse', 'Network'); |
||
| 25 | App::uses('Controller', 'Controller'); |
||
| 26 | |||
| 27 | /**
|
||
| 28 | * Exception Renderer.
|
||
| 29 | *
|
||
| 30 | * Captures and handles all unhandled exceptions. Displays helpful framework errors when debug > 1.
|
||
| 31 | * When debug < 1 a CakeException will render 404 or 500 errors. If an uncaught exception is thrown
|
||
| 32 | * and it is a type that ExceptionHandler does not know about it will be treated as a 500 error.
|
||
| 33 | *
|
||
| 34 | * ### Implementing application specific exception rendering
|
||
| 35 | *
|
||
| 36 | * You can implement application specific exception handling in one of a few ways:
|
||
| 37 | *
|
||
| 38 | * - Create an AppController::appError();
|
||
| 39 | * - Create a subclass of ExceptionRenderer and configure it to be the `Exception.renderer`
|
||
| 40 | *
|
||
| 41 | * #### Using AppController::appError();
|
||
| 42 | *
|
||
| 43 | * This controller method is called instead of the default exception handling. It receives the
|
||
| 44 | * thrown exception as its only argument. You should implement your error handling in that method.
|
||
| 45 | *
|
||
| 46 | * #### Using a subclass of ExceptionRenderer
|
||
| 47 | *
|
||
| 48 | * Using a subclass of ExceptionRenderer gives you full control over how Exceptions are rendered, you
|
||
| 49 | * can configure your class in your core.php, with `Configure::write('Exception.renderer', 'MyClass');`
|
||
| 50 | * You should place any custom exception renderers in `app/Lib/Error`.
|
||
| 51 | *
|
||
| 52 | * @package Cake.Error
|
||
| 53 | */
|
||
| 54 | class ExceptionRenderer { |
||
| 55 | |||
| 56 | /**
|
||
| 57 | * Controller instance.
|
||
| 58 | *
|
||
| 59 | * @var Controller
|
||
| 60 | */
|
||
| 61 | public $controller = null; |
||
| 62 | |||
| 63 | /**
|
||
| 64 | * template to render for CakeException
|
||
| 65 | *
|
||
| 66 | * @var string
|
||
| 67 | */
|
||
| 68 | public $template = ''; |
||
| 69 | |||
| 70 | /**
|
||
| 71 | * The method corresponding to the Exception this object is for.
|
||
| 72 | *
|
||
| 73 | * @var string
|
||
| 74 | */
|
||
| 75 | public $method = ''; |
||
| 76 | |||
| 77 | /**
|
||
| 78 | * The exception being handled.
|
||
| 79 | *
|
||
| 80 | * @var Exception
|
||
| 81 | */
|
||
| 82 | public $error = null; |
||
| 83 | |||
| 84 | /**
|
||
| 85 | * Creates the controller to perform rendering on the error response.
|
||
| 86 | * If the error is a CakeException it will be converted to either a 400 or a 500
|
||
| 87 | * code error depending on the code used to construct the error.
|
||
| 88 | *
|
||
| 89 | * @param Exception $exception Exception
|
||
| 90 | */
|
||
| 91 | public function __construct(Exception $exception) { |
||
| 92 | $this->controller = $this->_getController($exception); |
||
| 93 | |||
| 94 | if (method_exists($this->controller, 'appError')) { |
||
| 95 | $this->controller->appError($exception); |
||
| 96 | return;
|
||
| 97 | } |
||
| 98 | $method = $template = Inflector::variable(str_replace('Exception', '', get_class($exception))); |
||
| 99 | $code = $exception->getCode(); |
||
| 100 | |||
| 101 | $methodExists = method_exists($this, $method); |
||
| 102 | |||
| 103 | if ($exception instanceof CakeException && !$methodExists) { |
||
| 104 | $method = '_cakeError'; |
||
| 105 | if (empty($template) || $template === 'internalError') { |
||
| 106 | $template = 'error500'; |
||
| 107 | } |
||
| 108 | } elseif ($exception instanceof PDOException) { |
||
| 109 | $method = 'pdoError'; |
||
| 110 | $template = 'pdo_error'; |
||
| 111 | $code = 500; |
||
| 112 | } elseif (!$methodExists) { |
||
| 113 | $method = 'error500'; |
||
| 114 | if ($code >= 400 && $code < 500) { |
||
| 115 | $method = 'error400'; |
||
| 116 | } |
||
| 117 | } |
||
| 118 | |||
| 119 | $isNotDebug = !Configure::read('debug'); |
||
| 120 | if ($isNotDebug && $method === '_cakeError') { |
||
| 121 | $method = 'error400'; |
||
| 122 | } |
||
| 123 | if ($isNotDebug && $code == 500) { |
||
| 124 | $method = 'error500'; |
||
| 125 | } |
||
| 126 | $this->template = $template; |
||
| 127 | $this->method = $method; |
||
| 128 | $this->error = $exception; |
||
| 129 | } |
||
| 130 | |||
| 131 | /**
|
||
| 132 | * Get the controller instance to handle the exception.
|
||
| 133 | * Override this method in subclasses to customize the controller used.
|
||
| 134 | * This method returns the built in `CakeErrorController` normally, or if an error is repeated
|
||
| 135 | * a bare controller will be used.
|
||
| 136 | *
|
||
| 137 | * @param Exception $exception The exception to get a controller for.
|
||
| 138 | * @return Controller
|
||
| 139 | */
|
||
| 140 | protected function _getController($exception) { |
||
| 141 | App::uses('AppController', 'Controller'); |
||
| 142 | App::uses('CakeErrorController', 'Controller'); |
||
| 143 | if (!$request = Router::getRequest(true)) { |
||
| 144 | $request = new CakeRequest(); |
||
| 145 | } |
||
| 146 | $response = new CakeResponse(); |
||
| 147 | |||
| 148 | if (method_exists($exception, 'responseHeader')) { |
||
| 149 | $response->header($exception->responseHeader()); |
||
| 150 | } |
||
| 151 | |||
| 152 | if (class_exists('AppController')) { |
||
| 153 | try {
|
||
| 154 | $controller = new CakeErrorController($request, $response); |
||
| 155 | $controller->startupProcess();
|
||
| 156 | $startup = true; |
||
| 157 | } catch (Exception $e) { |
||
| 158 | $startup = false; |
||
| 159 | } |
||
| 160 | // Retry RequestHandler, as another aspect of startupProcess()
|
||
| 161 | // could have failed. Ignore any exceptions out of startup, as
|
||
| 162 | // there could be userland input data parsers.
|
||
| 163 | if ($startup === false && |
||
| 164 | !empty($controller) && |
||
| 165 | $controller->Components->enabled('RequestHandler') |
||
| 166 | ) {
|
||
| 167 | try {
|
||
| 168 | $controller->RequestHandler->startup($controller); |
||
| 169 | } catch (Exception $e) { |
||
| 170 | } |
||
| 171 | } |
||
| 172 | } |
||
| 173 | if (empty($controller)) { |
||
| 174 | $controller = new Controller($request, $response); |
||
| 175 | $controller->viewPath = 'Errors'; |
||
| 176 | } |
||
| 177 | return $controller; |
||
| 178 | } |
||
| 179 | |||
| 180 | /**
|
||
| 181 | * Renders the response for the exception.
|
||
| 182 | *
|
||
| 183 | * @return void
|
||
| 184 | */
|
||
| 185 | public function render() { |
||
| 186 | if ($this->method) { |
||
| 187 | call_user_func_array(array($this, $this->method), array($this->error)); |
||
| 188 | } |
||
| 189 | } |
||
| 190 | |||
| 191 | /**
|
||
| 192 | * Generic handler for the internal framework errors CakePHP can generate.
|
||
| 193 | *
|
||
| 194 | * @param CakeException $error The exception to render.
|
||
| 195 | * @return void
|
||
| 196 | */
|
||
| 197 | protected function _cakeError(CakeException $error) { |
||
| 198 | $url = $this->controller->request->here(); |
||
| 199 | $code = ($error->getCode() >= 400 && $error->getCode() < 506) ? $error->getCode() : 500; |
||
| 200 | $this->controller->response->statusCode($code); |
||
| 201 | $this->controller->set(array( |
||
| 202 | 'code' => $code, |
||
| 203 | 'name' => h($error->getMessage()), |
||
| 204 | 'message' => h($error->getMessage()), |
||
| 205 | 'url' => h($url), |
||
| 206 | 'error' => $error, |
||
| 207 | '_serialize' => array('code', 'name', 'message', 'url') |
||
| 208 | )); |
||
| 209 | $this->controller->set($error->getAttributes()); |
||
| 210 | $this->_outputMessage($this->template); |
||
| 211 | } |
||
| 212 | |||
| 213 | /**
|
||
| 214 | * Convenience method to display a 400 series page.
|
||
| 215 | *
|
||
| 216 | * @param Exception $error The exception to render.
|
||
| 217 | * @return void
|
||
| 218 | */
|
||
| 219 | public function error400($error) { |
||
| 220 | $message = $error->getMessage(); |
||
| 221 | if (!Configure::read('debug') && $error instanceof CakeException) { |
||
| 222 | $message = __d('cake', 'Not Found'); |
||
| 223 | } |
||
| 224 | $url = $this->controller->request->here(); |
||
| 225 | $this->controller->response->statusCode($error->getCode()); |
||
| 226 | $this->controller->set(array( |
||
| 227 | 'name' => h($message), |
||
| 228 | 'message' => h($message), |
||
| 229 | 'url' => h($url), |
||
| 230 | 'error' => $error, |
||
| 231 | '_serialize' => array('name', 'message', 'url') |
||
| 232 | )); |
||
| 233 | $this->_outputMessage('error400'); |
||
| 234 | } |
||
| 235 | |||
| 236 | /**
|
||
| 237 | * Convenience method to display a 500 page.
|
||
| 238 | *
|
||
| 239 | * @param Exception $error The exception to render.
|
||
| 240 | * @return void
|
||
| 241 | */
|
||
| 242 | public function error500($error) { |
||
| 243 | $message = $error->getMessage(); |
||
| 244 | if (!Configure::read('debug')) { |
||
| 245 | $message = __d('cake', 'An Internal Error Has Occurred.'); |
||
| 246 | } |
||
| 247 | $url = $this->controller->request->here(); |
||
| 248 | $code = ($error->getCode() > 500 && $error->getCode() < 506) ? $error->getCode() : 500; |
||
| 249 | $this->controller->response->statusCode($code); |
||
| 250 | $this->controller->set(array( |
||
| 251 | 'name' => h($message), |
||
| 252 | 'message' => h($message), |
||
| 253 | 'url' => h($url), |
||
| 254 | 'error' => $error, |
||
| 255 | '_serialize' => array('name', 'message', 'url') |
||
| 256 | )); |
||
| 257 | $this->_outputMessage('error500'); |
||
| 258 | } |
||
| 259 | |||
| 260 | /**
|
||
| 261 | * Convenience method to display a PDOException.
|
||
| 262 | *
|
||
| 263 | * @param PDOException $error The exception to render.
|
||
| 264 | * @return void
|
||
| 265 | */
|
||
| 266 | public function pdoError(PDOException $error) { |
||
| 267 | $url = $this->controller->request->here(); |
||
| 268 | $code = 500; |
||
| 269 | $this->controller->response->statusCode($code); |
||
| 270 | $this->controller->set(array( |
||
| 271 | 'code' => $code, |
||
| 272 | 'name' => h($error->getMessage()), |
||
| 273 | 'message' => h($error->getMessage()), |
||
| 274 | 'url' => h($url), |
||
| 275 | 'error' => $error, |
||
| 276 | '_serialize' => array('code', 'name', 'message', 'url', 'error') |
||
| 277 | )); |
||
| 278 | $this->_outputMessage($this->template); |
||
| 279 | } |
||
| 280 | |||
| 281 | /**
|
||
| 282 | * Generate the response using the controller object.
|
||
| 283 | *
|
||
| 284 | * @param string $template The template to render.
|
||
| 285 | * @return void
|
||
| 286 | */
|
||
| 287 | protected function _outputMessage($template) { |
||
| 288 | try {
|
||
| 289 | $this->controller->render($template); |
||
| 290 | $this->controller->afterFilter();
|
||
| 291 | $this->controller->response->send();
|
||
| 292 | } catch (MissingViewException $e) { |
||
| 293 | $attributes = $e->getAttributes(); |
||
| 294 | if (isset($attributes['file']) && strpos($attributes['file'], 'error500') !== false) { |
||
| 295 | $this->_outputMessageSafe('error500'); |
||
| 296 | } else {
|
||
| 297 | $this->_outputMessage('error500'); |
||
| 298 | } |
||
| 299 | } catch (MissingPluginException $e) { |
||
| 300 | $attributes = $e->getAttributes(); |
||
| 301 | if (isset($attributes['plugin']) && $attributes['plugin'] === $this->controller->plugin) { |
||
| 302 | $this->controller->plugin = null; |
||
| 303 | } |
||
| 304 | $this->_outputMessageSafe('error500'); |
||
| 305 | } catch (Exception $e) { |
||
| 306 | $this->_outputMessageSafe('error500'); |
||
| 307 | } |
||
| 308 | } |
||
| 309 | |||
| 310 | /**
|
||
| 311 | * A safer way to render error messages, replaces all helpers, with basics
|
||
| 312 | * and doesn't call component methods.
|
||
| 313 | *
|
||
| 314 | * @param string $template The template to render
|
||
| 315 | * @return void
|
||
| 316 | */
|
||
| 317 | protected function _outputMessageSafe($template) { |
||
| 318 | $this->controller->layoutPath = null; |
||
| 319 | $this->controller->subDir = null; |
||
| 320 | $this->controller->viewPath = 'Errors'; |
||
| 321 | $this->controller->layout = 'error'; |
||
| 322 | $this->controller->helpers = array('Form', 'Html', 'Session'); |
||
| 323 | |||
| 324 | $view = new View($this->controller); |
||
| 325 | $this->controller->response->body($view->render($template, 'error')); |
||
| 326 | $this->controller->response->type('html'); |
||
| 327 | $this->controller->response->send();
|
||
| 328 | } |
||
| 329 | |||
| 330 | } |