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

/classes/error.php

https://github.com/KenjiOhtsuka/core
PHP | 315 lines | 211 code | 39 blank | 65 comment | 27 complexity | 011fc4b66aadc647477362f61f9f2ea0 MD5 | raw file
  1. <?php
  2. /**
  3. * Part of the Fuel framework.
  4. *
  5. * @package Fuel
  6. * @version 1.7
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2014 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Fuel\Core;
  13. /**
  14. * Exception class for standard PHP errors, this will make them catchable
  15. */
  16. class PhpErrorException extends \ErrorException
  17. {
  18. public static $count = 0;
  19. public static $loglevel = \Fuel::L_ERROR;
  20. /**
  21. * Allow the error handler from recovering from error types defined in the config
  22. */
  23. public function recover()
  24. {
  25. // handle the error based on the config and the environment we're in
  26. if (static::$count <= \Config::get('errors.throttle', 10))
  27. {
  28. logger(static::$loglevel, $this->code.' - '.$this->message.' in '.$this->file.' on line '.$this->line);
  29. if (\Fuel::$env != \Fuel::PRODUCTION and ($this->code & error_reporting()) == $this->code)
  30. {
  31. static::$count++;
  32. \Error::show_php_error(new \ErrorException($this->message, $this->code, 0, $this->file, $this->line));
  33. }
  34. }
  35. elseif (\Fuel::$env != \Fuel::PRODUCTION
  36. and static::$count == (\Config::get('errors.throttle', 10) + 1)
  37. and ($this->severity & error_reporting()) == $this->severity)
  38. {
  39. static::$count++;
  40. \Error::notice('Error throttling threshold was reached, no more full error reports are shown.', true);
  41. }
  42. }
  43. }
  44. /**
  45. *
  46. */
  47. class Error
  48. {
  49. public static $loglevel = \Fuel::L_ERROR;
  50. public static $levels = array(
  51. 0 => 'Error',
  52. E_ERROR => 'Fatal Error',
  53. E_WARNING => 'Warning',
  54. E_PARSE => 'Parsing Error',
  55. E_NOTICE => 'Notice',
  56. E_CORE_ERROR => 'Core Error',
  57. E_CORE_WARNING => 'Core Warning',
  58. E_COMPILE_ERROR => 'Compile Error',
  59. E_COMPILE_WARNING => 'Compile Warning',
  60. E_USER_ERROR => 'User Error',
  61. E_USER_WARNING => 'User Warning',
  62. E_USER_NOTICE => 'User Notice',
  63. E_STRICT => 'Runtime Notice',
  64. E_RECOVERABLE_ERROR => 'Runtime Recoverable error',
  65. E_DEPRECATED => 'Runtime Deprecated code usage',
  66. E_USER_DEPRECATED => 'User Deprecated code usage',
  67. );
  68. public static $fatal_levels = array(E_PARSE, E_ERROR, E_USER_ERROR, E_COMPILE_ERROR);
  69. public static $non_fatal_cache = array();
  70. /**
  71. * Native PHP shutdown handler
  72. *
  73. * @return string
  74. */
  75. public static function shutdown_handler()
  76. {
  77. $last_error = error_get_last();
  78. // Only show valid fatal errors
  79. if ($last_error AND in_array($last_error['type'], static::$fatal_levels))
  80. {
  81. $severity = static::$levels[$last_error['type']];
  82. logger(static::$loglevel, $severity.' - '.$last_error['message'].' in '.$last_error['file'].' on line '.$last_error['line']);
  83. $error = new \ErrorException($last_error['message'], $last_error['type'], 0, $last_error['file'], $last_error['line']);
  84. if (\Fuel::$env != \Fuel::PRODUCTION)
  85. {
  86. static::show_php_error($error);
  87. }
  88. else
  89. {
  90. static::show_production_error($error);
  91. }
  92. exit(1);
  93. }
  94. }
  95. /**
  96. * PHP Exception handler
  97. *
  98. * @param Exception $e the exception
  99. * @return bool
  100. */
  101. public static function exception_handler(\Exception $e)
  102. {
  103. if (method_exists($e, 'handle'))
  104. {
  105. return $e->handle();
  106. }
  107. $severity = ( ! isset(static::$levels[$e->getCode()])) ? $e->getCode() : static::$levels[$e->getCode()];
  108. logger(static::$loglevel, $severity.' - '.$e->getMessage().' in '.$e->getFile().' on line '.$e->getLine());
  109. if (\Fuel::$env != \Fuel::PRODUCTION)
  110. {
  111. static::show_php_error($e);
  112. }
  113. else
  114. {
  115. static::show_production_error($e);
  116. }
  117. }
  118. /**
  119. * PHP Error handler
  120. *
  121. * @param int $severity the severity code
  122. * @param string $message the error message
  123. * @param string $filepath the path to the file throwing the error
  124. * @param int $line the line number of the error
  125. * @return bool whether to continue with execution
  126. */
  127. public static function error_handler($severity, $message, $filepath, $line)
  128. {
  129. // don't do anything if error reporting is disabled
  130. if (error_reporting() !== 0)
  131. {
  132. $fatal = (bool)( ! in_array($severity, \Config::get('errors.continue_on', array())));
  133. if ($fatal)
  134. {
  135. throw new \PhpErrorException($message, $severity, 0, $filepath, $line);
  136. }
  137. else
  138. {
  139. // non-fatal, recover from the error
  140. $e = new \PhpErrorException($message, $severity, 0, $filepath, $line);
  141. $e->recover();
  142. }
  143. }
  144. return true;
  145. }
  146. /**
  147. * Shows an error. It will stop script execution if the error code is not
  148. * in the errors.continue_on whitelist.
  149. *
  150. * @param Exception $e the exception to show
  151. * @return void
  152. */
  153. public static function show_php_error(\Exception $e)
  154. {
  155. $fatal = (bool)( ! in_array($e->getCode(), \Config::get('errors.continue_on', array())));
  156. $data = static::prepare_exception($e, $fatal);
  157. if ($fatal)
  158. {
  159. $data['contents'] = ob_get_contents();
  160. while (ob_get_level() > 0)
  161. {
  162. ob_end_clean();
  163. }
  164. ob_start(\Config::get('ob_callback', null));
  165. }
  166. else
  167. {
  168. static::$non_fatal_cache[] = $data;
  169. }
  170. if (\Fuel::$is_cli)
  171. {
  172. \Cli::write(\Cli::color($data['severity'].' - '.$data['message'].' in '.\Fuel::clean_path($data['filepath']).' on line '.$data['error_line'], 'red'));
  173. if (\Config::get('cli_backtrace'))
  174. {
  175. \Cli::write('Stack trace:');
  176. \Cli::write(\Debug::backtrace($e->getTrace()));
  177. }
  178. return;
  179. }
  180. if ($fatal)
  181. {
  182. if ( ! headers_sent())
  183. {
  184. $protocol = \Input::server('SERVER_PROTOCOL') ? \Input::server('SERVER_PROTOCOL') : 'HTTP/1.1';
  185. header($protocol.' 500 Internal Server Error');
  186. }
  187. $data['non_fatal'] = static::$non_fatal_cache;
  188. try
  189. {
  190. exit(\View::forge('errors'.DS.'php_fatal_error', $data, false));
  191. }
  192. catch (\FuelException $view_exception)
  193. {
  194. exit($data['severity'].' - '.$data['message'].' in '.\Fuel::clean_path($data['filepath']).' on line '.$data['error_line']);
  195. }
  196. }
  197. try
  198. {
  199. echo \View::forge('errors'.DS.'php_error', $data, false);
  200. }
  201. catch (\FuelException $e)
  202. {
  203. echo $e->getMessage().'<br />';
  204. }
  205. }
  206. /**
  207. * Shows a small notice error, only when not in production or when forced.
  208. * This is used by several libraries to notify the developer of certain things.
  209. *
  210. * @param string $msg the message to display
  211. * @param bool $always_show whether to force display the notice or not
  212. * @return void
  213. */
  214. public static function notice($msg, $always_show = false)
  215. {
  216. $trace = array_merge(array('file' => '(unknown)', 'line' => '(unknown)'), \Arr::get(debug_backtrace(), 1));
  217. logger(\Fuel::L_DEBUG, 'Notice - '.$msg.' in '.$trace['file'].' on line '.$trace['line']);
  218. if (\Fuel::$is_test or ( ! $always_show and (\Fuel::$env == \Fuel::PRODUCTION or \Config::get('errors.notices', true) === false)))
  219. {
  220. return;
  221. }
  222. $data['message'] = $msg;
  223. $data['type'] = 'Notice';
  224. $data['filepath'] = \Fuel::clean_path($trace['file']);
  225. $data['line'] = $trace['line'];
  226. $data['function'] = $trace['function'];
  227. echo \View::forge('errors'.DS.'php_short', $data, false);
  228. }
  229. /**
  230. * Shows the errors/production view and exits. This only gets
  231. * called when an error occurs in production mode.
  232. *
  233. * @return void
  234. */
  235. public static function show_production_error(\Exception $e)
  236. {
  237. // when we're on CLI, always show the php error
  238. if (\Fuel::$is_cli)
  239. {
  240. return static::show_php_error($e);
  241. }
  242. if ( ! headers_sent())
  243. {
  244. $protocol = \Input::server('SERVER_PROTOCOL') ? \Input::server('SERVER_PROTOCOL') : 'HTTP/1.1';
  245. header($protocol.' 500 Internal Server Error');
  246. }
  247. exit(\View::forge('errors'.DS.'production'));
  248. }
  249. protected static function prepare_exception(\Exception $e, $fatal = true)
  250. {
  251. $data = array();
  252. $data['type'] = get_class($e);
  253. $data['severity'] = $e->getCode();
  254. $data['message'] = $e->getMessage();
  255. $data['filepath'] = $e->getFile();
  256. $data['error_line'] = $e->getLine();
  257. $data['backtrace'] = $e->getTrace();
  258. $data['severity'] = ( ! isset(static::$levels[$data['severity']])) ? $data['severity'] : static::$levels[$data['severity']];
  259. foreach ($data['backtrace'] as $key => $trace)
  260. {
  261. if ( ! isset($trace['file']))
  262. {
  263. unset($data['backtrace'][$key]);
  264. }
  265. elseif ($trace['file'] == COREPATH.'classes/error.php')
  266. {
  267. unset($data['backtrace'][$key]);
  268. }
  269. }
  270. $data['debug_lines'] = \Debug::file_lines($data['filepath'], $data['error_line'], $fatal);
  271. $data['orig_filepath'] = $data['filepath'];
  272. $data['filepath'] = \Fuel::clean_path($data['filepath']);
  273. $data['filepath'] = str_replace("\\", "/", $data['filepath']);
  274. return $data;
  275. }
  276. }