PageRenderTime 65ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/fuel/core/classes/error.php

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