PageRenderTime 26ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/vendor/cakephp/cakephp/src/Error/ExceptionRenderer.php

https://gitlab.com/alexandresgv/siteentec
PHP | 381 lines | 213 code | 33 blank | 135 comment | 28 complexity | c50679f6d46f63dc0316e8fb09697b48 MD5 | raw file
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 2.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Error;
  16. use Cake\Controller\Controller;
  17. use Cake\Core\App;
  18. use Cake\Core\Configure;
  19. use Cake\Core\Exception\Exception as CakeException;
  20. use Cake\Core\Exception\MissingPluginException;
  21. use Cake\Error\PHP7ErrorException;
  22. use Cake\Event\Event;
  23. use Cake\Network\Exception\HttpException;
  24. use Cake\Network\Request;
  25. use Cake\Network\Response;
  26. use Cake\Routing\DispatcherFactory;
  27. use Cake\Routing\Router;
  28. use Cake\Utility\Inflector;
  29. use Cake\View\Exception\MissingTemplateException;
  30. use Exception;
  31. use PDOException;
  32. /**
  33. * Exception Renderer.
  34. *
  35. * Captures and handles all unhandled exceptions. Displays helpful framework errors when debug is true.
  36. * When debug is false a CakeException will render 404 or 500 errors. If an uncaught exception is thrown
  37. * and it is a type that ExceptionHandler does not know about it will be treated as a 500 error.
  38. *
  39. * ### Implementing application specific exception rendering
  40. *
  41. * You can implement application specific exception handling by creating a subclass of
  42. * ExceptionRenderer and configure it to be the `exceptionRenderer` in config/error.php
  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 config/app.php.
  48. */
  49. class ExceptionRenderer
  50. {
  51. /**
  52. * Controller instance.
  53. *
  54. * @var Controller
  55. */
  56. public $controller = null;
  57. /**
  58. * Template to render for Cake\Core\Exception\Exception
  59. *
  60. * @var string
  61. */
  62. public $template = '';
  63. /**
  64. * The method corresponding to the Exception this object is for.
  65. *
  66. * @var string
  67. */
  68. public $method = '';
  69. /**
  70. * The exception being handled.
  71. *
  72. * @var \Exception
  73. */
  74. public $error = null;
  75. /**
  76. * Creates the controller to perform rendering on the error response.
  77. * If the error is a Cake\Core\Exception\Exception it will be converted to either a 400 or a 500
  78. * code error depending on the code used to construct the error.
  79. *
  80. * @param Exception $exception Exception.
  81. */
  82. public function __construct(Exception $exception)
  83. {
  84. $this->error = $exception;
  85. $this->controller = $this->_getController();
  86. }
  87. /**
  88. * Returns the unwrapped exception object in case we are dealing with
  89. * a PHP 7 Error object
  90. *
  91. * @param \Exception $exception The object to unwrap
  92. * @return \Exception|\Error
  93. */
  94. protected function _unwrap($exception)
  95. {
  96. return $exception instanceof PHP7ErrorException ? $exception->getError() : $exception;
  97. }
  98. /**
  99. * Get the controller instance to handle the exception.
  100. * Override this method in subclasses to customize the controller used.
  101. * This method returns the built in `ErrorController` normally, or if an error is repeated
  102. * a bare controller will be used.
  103. *
  104. * @return \Cake\Controller\Controller
  105. * @triggers Controller.startup $controller
  106. */
  107. protected function _getController()
  108. {
  109. if (!$request = Router::getRequest(true)) {
  110. $request = Request::createFromGlobals();
  111. }
  112. $response = new Response();
  113. try {
  114. $class = App::className('Error', 'Controller', 'Controller');
  115. $controller = new $class($request, $response);
  116. $controller->startupProcess();
  117. $startup = true;
  118. } catch (Exception $e) {
  119. $startup = false;
  120. }
  121. // Retry RequestHandler, as another aspect of startupProcess()
  122. // could have failed. Ignore any exceptions out of startup, as
  123. // there could be userland input data parsers.
  124. if ($startup === false && !empty($controller) && isset($controller->RequestHandler)) {
  125. try {
  126. $event = new Event('Controller.startup', $controller);
  127. $controller->RequestHandler->startup($event);
  128. } catch (Exception $e) {
  129. }
  130. }
  131. if (empty($controller)) {
  132. $controller = new Controller($request, $response);
  133. }
  134. return $controller;
  135. }
  136. /**
  137. * Renders the response for the exception.
  138. *
  139. * @return \Cake\Network\Response The response to be sent.
  140. */
  141. public function render()
  142. {
  143. $exception = $this->error;
  144. $code = $this->_code($exception);
  145. $method = $this->_method($exception);
  146. $template = $this->_template($exception, $method, $code);
  147. $unwrapped = $this->_unwrap($exception);
  148. $isDebug = Configure::read('debug');
  149. if (($isDebug || $exception instanceof HttpException) &&
  150. method_exists($this, $method)
  151. ) {
  152. return $this->_customMethod($method, $unwrapped);
  153. }
  154. $message = $this->_message($exception, $code);
  155. $url = $this->controller->request->here();
  156. if (method_exists($exception, 'responseHeader')) {
  157. $this->controller->response->header($exception->responseHeader());
  158. }
  159. $this->controller->response->statusCode($code);
  160. $viewVars = [
  161. 'message' => $message,
  162. 'url' => h($url),
  163. 'error' => $unwrapped,
  164. 'code' => $code,
  165. '_serialize' => ['message', 'url', 'code']
  166. ];
  167. if ($isDebug) {
  168. $viewVars['trace'] = Debugger::formatTrace($unwrapped->getTrace(), [
  169. 'format' => 'array',
  170. 'args' => false
  171. ]);
  172. $viewVars['_serialize'][] = 'trace';
  173. }
  174. $this->controller->set($viewVars);
  175. if ($unwrapped instanceof CakeException && $isDebug) {
  176. $this->controller->set($unwrapped->getAttributes());
  177. }
  178. return $this->_outputMessage($template);
  179. }
  180. /**
  181. * Render a custom error method/template.
  182. *
  183. * @param string $method The method name to invoke.
  184. * @param Exception $exception The exception to render.
  185. * @return \Cake\Network\Response The response to send.
  186. */
  187. protected function _customMethod($method, $exception)
  188. {
  189. $result = call_user_func([$this, $method], $exception);
  190. $this->_shutdown();
  191. if (is_string($result)) {
  192. $this->controller->response->body($result);
  193. $result = $this->controller->response;
  194. }
  195. return $result;
  196. }
  197. /**
  198. * Get method name
  199. *
  200. * @param Exception $exception Exception instance.
  201. * @return string
  202. */
  203. protected function _method(Exception $exception)
  204. {
  205. $exception = $this->_unwrap($exception);
  206. list(, $baseClass) = namespaceSplit(get_class($exception));
  207. if (substr($baseClass, -9) === 'Exception') {
  208. $baseClass = substr($baseClass, 0, -9);
  209. }
  210. $method = Inflector::variable($baseClass) ?: 'error500';
  211. return $this->method = $method;
  212. }
  213. /**
  214. * Get error message.
  215. *
  216. * @param Exception $exception Exception.
  217. * @param int $code Error code.
  218. * @return string Error message
  219. */
  220. protected function _message(Exception $exception, $code)
  221. {
  222. $exception = $this->_unwrap($exception);
  223. $message = $exception->getMessage();
  224. if (!Configure::read('debug') &&
  225. !($exception instanceof HttpException)
  226. ) {
  227. if ($code < 500) {
  228. $message = __d('cake', 'Not Found');
  229. } else {
  230. $message = __d('cake', 'An Internal Error Has Occurred.');
  231. }
  232. }
  233. return $message;
  234. }
  235. /**
  236. * Get template for rendering exception info.
  237. *
  238. * @param \Exception $exception Exception instance.
  239. * @param string $method Method name.
  240. * @param int $code Error code.
  241. * @return string Template name
  242. */
  243. protected function _template(Exception $exception, $method, $code)
  244. {
  245. $exception = $this->_unwrap($exception);
  246. $isHttpException = $exception instanceof HttpException;
  247. if (!Configure::read('debug') && !$isHttpException) {
  248. $template = 'error500';
  249. if ($code < 500) {
  250. $template = 'error400';
  251. }
  252. return $this->template = $template;
  253. }
  254. if ($isHttpException) {
  255. $template = 'error500';
  256. if ($code < 500) {
  257. $template = 'error400';
  258. }
  259. return $this->template = $template;
  260. }
  261. $template = $method ?: 'error500';
  262. if ($exception instanceof PDOException) {
  263. $template = 'pdo_error';
  264. }
  265. return $this->template = $template;
  266. }
  267. /**
  268. * Get an error code value within range 400 to 506
  269. *
  270. * @param \Exception $exception Exception.
  271. * @return int Error code value within range 400 to 506
  272. */
  273. protected function _code(Exception $exception)
  274. {
  275. $code = 500;
  276. $exception = $this->_unwrap($exception);
  277. $errorCode = $exception->getCode();
  278. if ($errorCode >= 400 && $errorCode < 506) {
  279. $code = $errorCode;
  280. }
  281. return $code;
  282. }
  283. /**
  284. * Generate the response using the controller object.
  285. *
  286. * @param string $template The template to render.
  287. * @return \Cake\Network\Response A response object that can be sent.
  288. */
  289. protected function _outputMessage($template)
  290. {
  291. try {
  292. $this->controller->render($template);
  293. return $this->_shutdown();
  294. } catch (MissingTemplateException $e) {
  295. $attributes = $e->getAttributes();
  296. if (isset($attributes['file']) && strpos($attributes['file'], 'error500') !== false) {
  297. return $this->_outputMessageSafe('error500');
  298. }
  299. return $this->_outputMessage('error500');
  300. } catch (MissingPluginException $e) {
  301. $attributes = $e->getAttributes();
  302. if (isset($attributes['plugin']) && $attributes['plugin'] === $this->controller->plugin) {
  303. $this->controller->plugin = null;
  304. }
  305. return $this->_outputMessageSafe('error500');
  306. } catch (Exception $e) {
  307. return $this->_outputMessageSafe('error500');
  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 \Cake\Network\Response A response object that can be sent.
  316. */
  317. protected function _outputMessageSafe($template)
  318. {
  319. $helpers = ['Form', 'Html'];
  320. $this->controller->helpers = $helpers;
  321. $builder = $this->controller->viewBuilder();
  322. $builder->helpers($helpers, false)
  323. ->layoutPath('')
  324. ->templatePath('Error');
  325. $view = $this->controller->createView();
  326. $this->controller->response->body($view->render($template, 'error'));
  327. $this->controller->response->type('html');
  328. return $this->controller->response;
  329. }
  330. /**
  331. * Run the shutdown events.
  332. *
  333. * Triggers the afterFilter and afterDispatch events.
  334. *
  335. * @return \Cake\Network\Response The response to serve.
  336. */
  337. protected function _shutdown()
  338. {
  339. $this->controller->dispatchEvent('Controller.shutdown');
  340. $dispatcher = DispatcherFactory::create();
  341. $args = [
  342. 'request' => $this->controller->request,
  343. 'response' => $this->controller->response
  344. ];
  345. $result = $dispatcher->dispatchEvent('Dispatcher.afterDispatch', $args);
  346. return $result->data['response'];
  347. }
  348. }