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

/exceptionhandler.php

https://github.com/feri/midgardmvc_core
PHP | 281 lines | 217 code | 27 blank | 37 comment | 8 complexity | b3844411a9420851a9aed90ad8a5e7eb MD5 | raw file
  1. <?php
  2. /**
  3. * @package midgardmvc_core
  4. * @author The Midgard Project, http://www.midgard-project.org
  5. * @copyright The Midgard Project, http://www.midgard-project.org
  6. * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
  7. */
  8. /**
  9. * Midgard MVC exception handler
  10. *
  11. * @package midgardmvc_core
  12. */
  13. class midgardmvc_core_exceptionhandler
  14. {
  15. public static function handle(Exception $exception)
  16. {
  17. $data = self::prepare_exception_data($exception);
  18. if (isset($exception->midgardmvc))
  19. {
  20. $midgardmvc = $exception->midgardmvc;
  21. }
  22. try
  23. {
  24. if (! $midgardmvc)
  25. {
  26. $midgardmvc = midgardmvc_core::get_instance();
  27. }
  28. $midgardmvc->log($data['message_type'], $data['message'], 'warning');
  29. }
  30. catch (Exception $e)
  31. {
  32. // MVC not initialized, display original message and exit
  33. return self::show_error_plaintext($data);
  34. }
  35. if (!$midgardmvc->dispatcher->headers_sent())
  36. {
  37. $midgardmvc->dispatcher->header("X-MidgardMVC-Error: {$data['message']}");
  38. $midgardmvc->dispatcher->header($data['header']);
  39. }
  40. if ($data['http_code'] == 304)
  41. {
  42. return;
  43. }
  44. self::show_error_templated($data, $midgardmvc, $exception->request);
  45. }
  46. public static function handle_assert($file, $line, $expression)
  47. {
  48. $message = "Assertion '{$expression}' failed, {$file} line {$line}";
  49. midgardmvc_core::get_instance()->log('midgardmvc_core', "Assertion {$expression} failed, {$file} {$line}", 'warning');
  50. throw new RunTimeException($message);
  51. }
  52. private static function prepare_exception_data(Exception $exception)
  53. {
  54. $data = array();
  55. $data['message_type'] = get_class($exception);
  56. $data['http_code'] = self::code_by_exception($exception);
  57. $data['message'] = strip_tags($exception->getMessage());
  58. $data['header'] = self::header_by_code($data['http_code']);
  59. $data['exception'] = $exception;
  60. $data['trace'] = null;
  61. return $data;
  62. }
  63. private static function show_error_plaintext(array $data, $dispatcher = null)
  64. {
  65. $message = "<h1>Unexpected Error</h1>\n\n<p>Headers were sent so we don't have correct HTTP code ({$data['http_code']}).</p>\n\n<p>{$data['message_type']}: {$data['message']}</p>\n";
  66. if (is_null($dispatcher))
  67. {
  68. // We got an exception before MVC was fully initialized
  69. if (!headers_sent())
  70. {
  71. header("X-MidgardMVC-Error: {$data['message']}");
  72. header(self::header_by_code($data['http_code']));
  73. }
  74. die($message);
  75. }
  76. echo $message;
  77. $dispatcher->end_request();
  78. }
  79. private static function show_error_templated(array $data, midgardmvc_core $midgardmvc, midgardmvc_core_request $request)
  80. {
  81. $midgardmvc->dispatcher->header('Content-Type: text/html; charset=utf-8');
  82. if ($midgardmvc->configuration->enable_exception_trace)
  83. {
  84. $data['trace'] = $exception->getTrace();
  85. }
  86. try
  87. {
  88. if (! $request)
  89. {
  90. // Exception happened before request was set to context
  91. $request = self::bootstrap_request();
  92. }
  93. $route = $request->get_route();
  94. if ($route)
  95. {
  96. $errorRoute = clone $route;
  97. $errorRoute->template_aliases['root'] = 'midgardmvc-show-error';
  98. $request->set_route($errorRoute);
  99. }
  100. $request->set_data_item('midgardmvc_core_exceptionhandler', $data);
  101. $request->set_data_item('cache_enabled', false);
  102. $midgardmvc->templating->template($request);
  103. $midgardmvc->templating->display($request);
  104. }
  105. catch (Exception $e)
  106. {
  107. // Templating isn't working
  108. self::show_error_untemplated($data);
  109. }
  110. // Clean up and finish
  111. $midgardmvc->context->delete();
  112. }
  113. private static function show_error_untemplated(array $data)
  114. {
  115. echo "<!DOCTYPE html>\n";
  116. echo "<html>\n";
  117. echo " <head>\n";
  118. echo " <title>{$data['header']}</title>\n";
  119. echo " </head>\n";
  120. echo " <body class=\"{$data['message_type']}\">\n";
  121. echo " <h1>{$data['header']}</h1>\n";
  122. echo " <p>{$data['message']}</p>\n";
  123. echo " </body>\n";
  124. echo "</html>";
  125. }
  126. private static function bootstrap_request()
  127. {
  128. $request = new midgardmvc_core_request();
  129. $core = midgardmvc_core::get_instance()->component->get('midgardmvc_core');
  130. $request->add_component_to_chain($core);
  131. $route = new midgardmvc_core_route('midgardmvc_show_error', '', '', '', array());
  132. $request->set_route($route);
  133. $request->set_component($core);
  134. midgardmvc_core::get_instance()->context->create($request);
  135. return $request;
  136. }
  137. public static function code_by_exception(Exception $exception)
  138. {
  139. // Different HTTP error codes for different Exceptions
  140. switch (get_class($exception))
  141. {
  142. case 'midgardmvc_exception_notfound':
  143. case 'midgardmvc_exception_unauthorized':
  144. case 'midgardmvc_exception_httperror':
  145. return $exception->getHttpCode();
  146. }
  147. return 500;
  148. }
  149. public static function header_by_code($code)
  150. {
  151. $headers = array
  152. (
  153. 200 => 'HTTP/1.0 200 OK',
  154. 303 => 'HTTP/1.0 303 See Other',
  155. 304 => 'HTTP/1.0 304 Not Modified',
  156. 401 => 'HTTP/1.0 401 Unauthorized',
  157. 404 => 'HTTP/1.0 404 Not Found',
  158. 405 => 'HTTP/1.0 405 Method not allowed',
  159. 500 => 'HTTP/1.0 500 Server Error',
  160. 503 => 'HTTP/1.0 503 Service Unavailable',
  161. );
  162. if (!isset($headers[$code]))
  163. {
  164. $code = 500;
  165. }
  166. return $headers[$code];
  167. }
  168. }
  169. /**
  170. * Basic Midgard MVC exception
  171. *
  172. * @package midgardmvc_core
  173. */
  174. class midgardmvc_exception extends Exception
  175. {
  176. var $midgardvc = null;
  177. var $request = null;
  178. public function __construct($message, $code = 500)
  179. {
  180. parent::__construct($message, $code);
  181. $this->midgardmvc = midgardmvc_core::get_instance();
  182. if ($this->midgardmvc->context)
  183. {
  184. $this->request = $this->midgardmvc->context->get_request();
  185. }
  186. }
  187. public function getHttpCode()
  188. {
  189. return 500;
  190. }
  191. }
  192. /**
  193. * Midgard MVC "not found" exception
  194. *
  195. * @package midgardmvc_core
  196. */
  197. class midgardmvc_exception_notfound extends midgardmvc_exception
  198. {
  199. // Redefine the exception so message isn't optional
  200. public function __construct($message, $code = 404)
  201. {
  202. parent::__construct($message, $code);
  203. }
  204. public function getHttpCode()
  205. {
  206. return 404;
  207. }
  208. }
  209. /**
  210. * Midgard MVC "unauthorized" exception
  211. *
  212. * @package midgardmvc_core
  213. */
  214. class midgardmvc_exception_unauthorized extends midgardmvc_exception
  215. {
  216. // Redefine the exception so message isn't optional
  217. public function __construct($message, $code = 401)
  218. {
  219. parent::__construct($message, $code);
  220. }
  221. public function getHttpCode()
  222. {
  223. return 401;
  224. }
  225. }
  226. /**
  227. * Midgard MVC generic HTTP error exception
  228. *
  229. * @package midgardmvc_core
  230. */
  231. class midgardmvc_exception_httperror extends midgardmvc_exception
  232. {
  233. protected $httpcode = 500;
  234. // Redefine the exception so message isn't optional
  235. public function __construct($message, $code = 500)
  236. {
  237. $this->httpcode = $code;
  238. parent::__construct($message, $code);
  239. }
  240. public function getHttpCode()
  241. {
  242. return $this->httpcode;
  243. }
  244. }
  245. set_exception_handler(array('midgardmvc_core_exceptionhandler', 'handle'));
  246. ?>