Handle.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: yunwuxin <448901948@qq.com>
  10. // +----------------------------------------------------------------------
  11. namespace think\exception;
  12. use Exception;
  13. use think\App;
  14. use think\Config;
  15. use think\console\Output;
  16. use think\Lang;
  17. use think\Log;
  18. use think\Response;
  19. class Handle
  20. {
  21. protected $ignoreReport = [
  22. '\\think\\exception\\HttpException',
  23. ];
  24. /**
  25. * Report or log an exception.
  26. *
  27. * @param \Exception $exception
  28. * @return void
  29. */
  30. public function report(Exception $exception)
  31. {
  32. if (!$this->isIgnoreReport($exception)) {
  33. // 收集异常数据
  34. if (App::$debug) {
  35. $data = [
  36. 'file' => $exception->getFile(),
  37. 'line' => $exception->getLine(),
  38. 'message' => $this->getMessage($exception),
  39. 'code' => $this->getCode($exception),
  40. ];
  41. $log = "[{$data['code']}]{$data['message']}[{$data['file']}:{$data['line']}]";
  42. } else {
  43. $data = [
  44. 'code' => $this->getCode($exception),
  45. 'message' => $this->getMessage($exception),
  46. ];
  47. $log = "[{$data['code']}]{$data['message']}";
  48. }
  49. if (Config::get('record_trace')) {
  50. $log .= "\r\n" . $exception->getTraceAsString();
  51. }
  52. Log::record($log, 'error');
  53. }
  54. }
  55. protected function isIgnoreReport(Exception $exception)
  56. {
  57. foreach ($this->ignoreReport as $class) {
  58. if ($exception instanceof $class) {
  59. return true;
  60. }
  61. }
  62. return false;
  63. }
  64. /**
  65. * Render an exception into an HTTP response.
  66. *
  67. * @param \Exception $e
  68. * @return Response
  69. */
  70. public function render(Exception $e)
  71. {
  72. if ($e instanceof HttpException) {
  73. return $this->renderHttpException($e);
  74. } else {
  75. return $this->convertExceptionToResponse($e);
  76. }
  77. }
  78. /**
  79. * @param Output $output
  80. * @param Exception $e
  81. */
  82. public function renderForConsole(Output $output, Exception $e)
  83. {
  84. if (App::$debug) {
  85. $output->setVerbosity(Output::VERBOSITY_DEBUG);
  86. }
  87. $output->renderException($e);
  88. }
  89. /**
  90. * @param HttpException $e
  91. * @return Response
  92. */
  93. protected function renderHttpException(HttpException $e)
  94. {
  95. $status = $e->getStatusCode();
  96. $template = Config::get('http_exception_template');
  97. if (!App::$debug && !empty($template[$status])) {
  98. return Response::create($template[$status], 'view', $status)->assign(['e' => $e]);
  99. } else {
  100. return $this->convertExceptionToResponse($e);
  101. }
  102. }
  103. /**
  104. * @param Exception $exception
  105. * @return Response
  106. */
  107. protected function convertExceptionToResponse(Exception $exception)
  108. {
  109. // 收集异常数据
  110. if (App::$debug) {
  111. // 调试模式,获取详细的错误信息
  112. $data = [
  113. 'name' => get_class($exception),
  114. 'file' => $exception->getFile(),
  115. 'line' => $exception->getLine(),
  116. 'message' => $this->getMessage($exception),
  117. 'trace' => $exception->getTrace(),
  118. 'code' => $this->getCode($exception),
  119. 'source' => $this->getSourceCode($exception),
  120. 'datas' => $this->getExtendData($exception),
  121. 'tables' => [
  122. 'GET Data' => $_GET,
  123. 'POST Data' => $_POST,
  124. 'Files' => $_FILES,
  125. 'Cookies' => $_COOKIE,
  126. 'Session' => isset($_SESSION) ? $_SESSION : [],
  127. 'Server/Request Data' => $_SERVER,
  128. 'Environment Variables' => $_ENV,
  129. 'ThinkPHP Constants' => $this->getConst(),
  130. ],
  131. ];
  132. } else {
  133. // 部署模式仅显示 Code 和 Message
  134. $data = [
  135. 'code' => $this->getCode($exception),
  136. 'message' => $this->getMessage($exception),
  137. ];
  138. if (!Config::get('show_error_msg')) {
  139. // 不显示详细错误信息
  140. $data['message'] = Config::get('error_message');
  141. }
  142. }
  143. //保留一层
  144. while (ob_get_level() > 1) {
  145. ob_end_clean();
  146. }
  147. $data['echo'] = ob_get_clean();
  148. ob_start();
  149. extract($data);
  150. include Config::get('exception_tmpl');
  151. // 获取并清空缓存
  152. $content = ob_get_clean();
  153. $response = new Response($content, 'html');
  154. if ($exception instanceof HttpException) {
  155. $statusCode = $exception->getStatusCode();
  156. $response->header($exception->getHeaders());
  157. }
  158. if (!isset($statusCode)) {
  159. $statusCode = 500;
  160. }
  161. $response->code($statusCode);
  162. return $response;
  163. }
  164. /**
  165. * 获取错误编码
  166. * ErrorException则使用错误级别作为错误编码
  167. * @param \Exception $exception
  168. * @return integer 错误编码
  169. */
  170. protected function getCode(Exception $exception)
  171. {
  172. $code = $exception->getCode();
  173. if (!$code && $exception instanceof ErrorException) {
  174. $code = $exception->getSeverity();
  175. }
  176. return $code;
  177. }
  178. /**
  179. * 获取错误信息
  180. * ErrorException则使用错误级别作为错误编码
  181. * @param \Exception $exception
  182. * @return string 错误信息
  183. */
  184. protected function getMessage(Exception $exception)
  185. {
  186. $message = $exception->getMessage();
  187. if (IS_CLI) {
  188. return $message;
  189. }
  190. if (strpos($message, ':')) {
  191. $name = strstr($message, ':', true);
  192. $message = Lang::has($name) ? Lang::get($name) . strstr($message, ':') : $message;
  193. } elseif (strpos($message, ',')) {
  194. $name = strstr($message, ',', true);
  195. $message = Lang::has($name) ? Lang::get($name) . ':' . substr(strstr($message, ','), 1) : $message;
  196. } elseif (Lang::has($message)) {
  197. $message = Lang::get($message);
  198. }
  199. return $message;
  200. }
  201. /**
  202. * 获取出错文件内容
  203. * 获取错误的前9行和后9行
  204. * @param \Exception $exception
  205. * @return array 错误文件内容
  206. */
  207. protected function getSourceCode(Exception $exception)
  208. {
  209. // 读取前9行和后9行
  210. $line = $exception->getLine();
  211. $first = ($line - 9 > 0) ? $line - 9 : 1;
  212. try {
  213. $contents = file($exception->getFile());
  214. $source = [
  215. 'first' => $first,
  216. 'source' => array_slice($contents, $first - 1, 19),
  217. ];
  218. } catch (Exception $e) {
  219. $source = [];
  220. }
  221. return $source;
  222. }
  223. /**
  224. * 获取异常扩展信息
  225. * 用于非调试模式html返回类型显示
  226. * @param \Exception $exception
  227. * @return array 异常类定义的扩展数据
  228. */
  229. protected function getExtendData(Exception $exception)
  230. {
  231. $data = [];
  232. if ($exception instanceof \think\Exception) {
  233. $data = $exception->getData();
  234. }
  235. return $data;
  236. }
  237. /**
  238. * 获取常量列表
  239. * @return array 常量列表
  240. */
  241. private static function getConst()
  242. {
  243. return get_defined_constants(true)['user'];
  244. }
  245. }