PageRenderTime 26ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Cake/Error/ExceptionRenderer.php

https://bitbucket.org/luobailiang/cake
PHP | 284 lines | 140 code | 19 blank | 125 comment | 22 complexity | 7b9bd1c7bd6605cc2104e81322876d5c MD5 | raw file
  1. <?php
  2. /**
  3. * Exception Renderer
  4. *
  5. * Provides Exception rendering features. Which allow exceptions to be rendered
  6. * as HTML pages.
  7. *
  8. * PHP 5
  9. *
  10. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  11. * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  12. *
  13. * Licensed under The MIT License
  14. * Redistributions of files must retain the above copyright notice.
  15. *
  16. * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  17. * @link http://cakephp.org CakePHP(tm) Project
  18. * @package Cake.Error
  19. * @since CakePHP(tm) v 2.0
  20. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  21. */
  22. App::uses('Sanitize', 'Utility');
  23. App::uses('Router', 'Routing');
  24. App::uses('CakeResponse', 'Network');
  25. /**
  26. * Exception Renderer.
  27. *
  28. * Captures and handles all unhandled exceptions. Displays helpful framework errors when debug > 1.
  29. * When debug < 1 a CakeException will render 404 or 500 errors. If an uncaught exception is thrown
  30. * and it is a type that ExceptionHandler does not know about it will be treated as a 500 error.
  31. *
  32. * ### Implementing application specific exception rendering
  33. *
  34. * You can implement application specific exception handling in one of a few ways:
  35. *
  36. * - Create a AppController::appError();
  37. * - Create a subclass of ExceptionRenderer and configure it to be the `Exception.renderer`
  38. *
  39. * #### Using AppController::appError();
  40. *
  41. * This controller method is called instead of the default exception handling. It receives the
  42. * thrown exception as its only argument. You should implement your error handling in that method.
  43. *
  44. * #### Using a subclass of ExceptionRenderer
  45. *
  46. * Using a subclass of ExceptionRenderer gives you full control over how Exceptions are rendered, you
  47. * can configure your class in your core.php, with `Configure::write('Exception.renderer', 'MyClass');`
  48. * You should place any custom exception renderers in `app/Lib/Error`.
  49. *
  50. * @package Cake.Error
  51. */
  52. class ExceptionRenderer {
  53. /**
  54. * Controller instance.
  55. *
  56. * @var Controller
  57. */
  58. public $controller = null;
  59. /**
  60. * template to render for CakeException
  61. *
  62. * @var string
  63. */
  64. public $template = '';
  65. /**
  66. * The method corresponding to the Exception this object is for.
  67. *
  68. * @var string
  69. */
  70. public $method = '';
  71. /**
  72. * The exception being handled.
  73. *
  74. * @var Exception
  75. */
  76. public $error = null;
  77. /**
  78. * Creates the controller to perform rendering on the error response.
  79. * If the error is a CakeException it will be converted to either a 400 or a 500
  80. * code error depending on the code used to construct the error.
  81. *
  82. * @param Exception $exception Exception
  83. */
  84. public function __construct(Exception $exception) {
  85. $this->controller = $this->_getController($exception);
  86. if (method_exists($this->controller, 'apperror')) {
  87. return $this->controller->appError($exception);
  88. }
  89. $method = $template = Inflector::variable(str_replace('Exception', '', get_class($exception)));
  90. $code = $exception->getCode();
  91. $methodExists = method_exists($this, $method);
  92. if ($exception instanceof CakeException && !$methodExists) {
  93. $method = '_cakeError';
  94. if (empty($template)) {
  95. $template = 'error500';
  96. }
  97. if ($template == 'internalError') {
  98. $template = 'error500';
  99. }
  100. } elseif ($exception instanceof PDOException) {
  101. $method = 'pdoError';
  102. $template = 'pdo_error';
  103. $code = 500;
  104. } elseif (!$methodExists) {
  105. $method = 'error500';
  106. if ($code >= 400 && $code < 500) {
  107. $method = 'error400';
  108. }
  109. }
  110. if (Configure::read('debug') == 0) {
  111. $parentClass = get_parent_class($this);
  112. if ($parentClass != __CLASS__) {
  113. $method = 'error400';
  114. }
  115. $parentMethods = (array)get_class_methods($parentClass);
  116. if (in_array($method, $parentMethods)) {
  117. $method = 'error400';
  118. }
  119. if ($code == 500) {
  120. $method = 'error500';
  121. }
  122. }
  123. $this->template = $template;
  124. $this->method = $method;
  125. $this->error = $exception;
  126. }
  127. /**
  128. * Get the controller instance to handle the exception.
  129. * Override this method in subclasses to customize the controller used.
  130. * This method returns the built in `CakeErrorController` normally, or if an error is repeated
  131. * a bare controller will be used.
  132. *
  133. * @param Exception $exception The exception to get a controller for.
  134. * @return Controller
  135. */
  136. protected function _getController($exception) {
  137. App::uses('CakeErrorController', 'Controller');
  138. if (!$request = Router::getRequest(false)) {
  139. $request = new CakeRequest();
  140. }
  141. $response = new CakeResponse(array('charset' => Configure::read('App.encoding')));
  142. try {
  143. $controller = new CakeErrorController($request, $response);
  144. } catch (Exception $e) {
  145. $controller = new Controller($request, $response);
  146. $controller->viewPath = 'Errors';
  147. }
  148. return $controller;
  149. }
  150. /**
  151. * Renders the response for the exception.
  152. *
  153. * @return void
  154. */
  155. public function render() {
  156. if ($this->method) {
  157. call_user_func_array(array($this, $this->method), array($this->error));
  158. }
  159. }
  160. /**
  161. * Generic handler for the internal framework errors CakePHP can generate.
  162. *
  163. * @param CakeException $error
  164. * @return void
  165. */
  166. protected function _cakeError(CakeException $error) {
  167. $url = $this->controller->request->here();
  168. $code = ($error->getCode() >= 400 && $error->getCode() < 506) ? $error->getCode() : 500;
  169. $this->controller->response->statusCode($code);
  170. $this->controller->set(array(
  171. 'code' => $code,
  172. 'url' => h($url),
  173. 'name' => $error->getMessage(),
  174. 'error' => $error,
  175. ));
  176. try {
  177. $this->controller->set($error->getAttributes());
  178. $this->_outputMessage($this->template);
  179. } catch (Exception $e) {
  180. $this->_outputMessageSafe('error500');
  181. }
  182. }
  183. /**
  184. * Convenience method to display a 400 series page.
  185. *
  186. * @param Exception $error
  187. * @return void
  188. */
  189. public function error400($error) {
  190. $message = $error->getMessage();
  191. if (Configure::read('debug') == 0 && $error instanceof CakeException) {
  192. $message = __('Not Found');
  193. }
  194. $url = $this->controller->request->here();
  195. $this->controller->response->statusCode($error->getCode());
  196. $this->controller->set(array(
  197. 'name' => $message,
  198. 'url' => h($url),
  199. 'error' => $error,
  200. ));
  201. $this->_outputMessage('error400');
  202. }
  203. /**
  204. * Convenience method to display a 500 page.
  205. *
  206. * @param Exception $error
  207. * @return void
  208. */
  209. public function error500($error) {
  210. $url = $this->controller->request->here();
  211. $code = ($error->getCode() > 500 && $error->getCode() < 506) ? $error->getCode() : 500;
  212. $this->controller->response->statusCode($code);
  213. $this->controller->set(array(
  214. 'name' => __('An Internal Error Has Occurred'),
  215. 'message' => h($url),
  216. 'error' => $error,
  217. ));
  218. $this->_outputMessage('error500');
  219. }
  220. /**
  221. * Convenience method to display a PDOException.
  222. *
  223. * @param PDOException $error
  224. * @return void
  225. */
  226. public function pdoError(PDOException $error) {
  227. $url = $this->controller->request->here();
  228. $code = 500;
  229. $this->controller->response->statusCode($code);
  230. $this->controller->set(array(
  231. 'code' => $code,
  232. 'url' => h($url),
  233. 'name' => $error->getMessage(),
  234. 'error' => $error,
  235. ));
  236. try {
  237. $this->_outputMessage($this->template);
  238. } catch (Exception $e) {
  239. $this->_outputMessageSafe('error500');
  240. }
  241. }
  242. /**
  243. * Generate the response using the controller object.
  244. *
  245. * @param string $template The template to render.
  246. * @return void
  247. */
  248. protected function _outputMessage($template) {
  249. $this->controller->render($template);
  250. $this->controller->afterFilter();
  251. $this->controller->response->send();
  252. }
  253. /**
  254. * A safer way to render error messages, replaces all helpers, with basics
  255. * and doesn't call component methods.
  256. *
  257. * @param string $template The template to render
  258. * @return void
  259. */
  260. protected function _outputMessageSafe($template) {
  261. $this->controller->helpers = array('Form', 'Html', 'Session');
  262. $this->controller->render($template);
  263. $this->controller->response->send();
  264. }
  265. }