Passed
Push — 5.2 ( 8b8fc2...cf53d3 )
by liu
03:33
created

Handle::isIgnoreReport()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
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 <[email protected]>
10
// +----------------------------------------------------------------------
11
12
namespace think\exception;
13
14
use Exception;
15
use think\console\Output;
16
use think\Container;
17
use think\Response;
18
use Throwable;
19
20
class Handle
21
{
22
    protected $render;
23
    protected $ignoreReport = [
24
        '\\think\\exception\\HttpException',
25
    ];
26
27
    public function setRender(\Closure $render)
28
    {
29
        $this->render = $render;
30
    }
31
32
    /**
33
     * Report or log an exception.
34
     *
35
     * @access public
36
     * @param  Throwable $exception
37
     * @return void
38
     */
39
    public function report(Throwable $exception)
40
    {
41
        if (!$this->isIgnoreReport($exception)) {
42
            // 收集异常数据
43
            if (Container::pull('app')->isDebug()) {
44
                $data = [
45
                    'file'    => $exception->getFile(),
46
                    'line'    => $exception->getLine(),
47
                    'message' => $this->getMessage($exception),
48
                    'code'    => $this->getCode($exception),
49
                ];
50
                $log = "[{$data['code']}]{$data['message']}[{$data['file']}:{$data['line']}]";
51
            } else {
52
                $data = [
53
                    'code'    => $this->getCode($exception),
54
                    'message' => $this->getMessage($exception),
55
                ];
56
                $log = "[{$data['code']}]{$data['message']}";
57
            }
58
59
            if (Container::pull('config')->get('log.record_trace')) {
60
                $log .= "\r\n" . $exception->getTraceAsString();
61
            }
62
63
            Container::pull('log')->record($log, 'error');
64
        }
65
    }
66
67
    protected function isIgnoreReport(Throwable $exception)
68
    {
69
        foreach ($this->ignoreReport as $class) {
70
            if ($exception instanceof $class) {
71
                return true;
72
            }
73
        }
74
75
        return false;
76
    }
77
78
    /**
79
     * Render an exception into an HTTP response.
80
     *
81
     * @access public
82
     * @param  Throwable $e
83
     * @return Response
84
     */
85
    public function render(Throwable $e)
86
    {
87
        if ($this->render && $this->render instanceof \Closure) {
88
            $result = call_user_func_array($this->render, [$e]);
89
90
            if ($result) {
91
                return $result;
92
            }
93
        }
94
95
        if ($e instanceof HttpException) {
96
            return $this->renderHttpException($e);
97
        } else {
98
            return $this->convertExceptionToResponse($e);
99
        }
100
    }
101
102
    /**
103
     * @access public
104
     * @param  Output    $output
105
     * @param  Throwable $e
106
     */
107
    public function renderForConsole(Output $output, Throwable $e)
108
    {
109
        if (Container::pull('app')->isDebug()) {
110
            $output->setVerbosity(Output::VERBOSITY_DEBUG);
111
        }
112
113
        $output->renderException($e);
114
    }
115
116
    /**
117
     * @access protected
118
     * @param  HttpException $e
119
     * @return Response
120
     */
121
    protected function renderHttpException(HttpException $e)
122
    {
123
        $status   = $e->getStatusCode();
124
        $template = Container::pull('config')->get('app.http_exception_template');
125
126
        if (!Container::pull('app')->isDebug() && !empty($template[$status])) {
127
            return Response::create($template[$status], 'view', $status)->assign(['e' => $e]);
0 ignored issues
show
Bug introduced by
The method assign() does not exist on think\Response. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

127
            return Response::create($template[$status], 'view', $status)->/** @scrutinizer ignore-call */ assign(['e' => $e]);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
128
        } else {
129
            return $this->convertExceptionToResponse($e);
130
        }
131
    }
132
133
    /**
134
     * @access protected
135
     * @param  Throwable $exception
136
     * @return Response
137
     */
138
    protected function convertExceptionToResponse(Throwable $exception)
139
    {
140
        // 收集异常数据
141
        if (Container::pull('app')->isDebug()) {
142
            // 调试模式,获取详细的错误信息
143
            $data = [
144
                'name'    => get_class($exception),
145
                'file'    => $exception->getFile(),
146
                'line'    => $exception->getLine(),
147
                'message' => $this->getMessage($exception),
148
                'trace'   => $exception->getTrace(),
149
                'code'    => $this->getCode($exception),
150
                'source'  => $this->getSourceCode($exception),
151
                'datas'   => $this->getExtendData($exception),
152
                'tables'  => [
153
                    'GET Data'              => $_GET,
154
                    'POST Data'             => $_POST,
155
                    'Files'                 => $_FILES,
156
                    'Cookies'               => $_COOKIE,
157
                    'Session'               => $_SESSION ?? [],
158
                    'Server/Request Data'   => $_SERVER,
159
                    'Environment Variables' => $_ENV,
160
                    'ThinkPHP Constants'    => $this->getConst(),
161
                ],
162
            ];
163
        } else {
164
            // 部署模式仅显示 Code 和 Message
165
            $data = [
166
                'code'    => $this->getCode($exception),
167
                'message' => $this->getMessage($exception),
168
            ];
169
170
            if (!Container::pull('config')->get('app.show_error_msg')) {
171
                // 不显示详细错误信息
172
                $data['message'] = Container::pull('config')->get('app.error_message');
173
            }
174
        }
175
176
        $type = Container::pull('config')->get('app.exception_response_type') ?: 'html';
177
178
        if ('html' == $type) {
179
            //保留一层
180
            while (ob_get_level() > 1) {
181
                ob_end_clean();
182
            }
183
184
            $data['echo'] = ob_get_clean();
185
186
            ob_start();
187
            extract($data);
188
            include Container::pull('config')->get('app.exception_tmpl') ?: __DIR__ . '/../../tpl/think_exception.tpl';
189
190
            // 获取并清空缓存
191
            $data = ob_get_clean();
192
        }
193
194
        $response = Response::create($data, $type);
195
196
        if ($exception instanceof HttpException) {
197
            $statusCode = $exception->getStatusCode();
198
            $response->header($exception->getHeaders());
199
        }
200
201
        return $response->code($statusCode ?? 500);
202
    }
203
204
    /**
205
     * 获取错误编码
206
     * ErrorException则使用错误级别作为错误编码
207
     * @access protected
208
     * @param  Throwable $exception
209
     * @return integer                错误编码
210
     */
211
    protected function getCode(Throwable $exception)
212
    {
213
        $code = $exception->getCode();
214
215
        if (!$code && $exception instanceof ErrorException) {
216
            $code = $exception->getSeverity();
217
        }
218
219
        return $code;
220
    }
221
222
    /**
223
     * 获取错误信息
224
     * ErrorException则使用错误级别作为错误编码
225
     * @access protected
226
     * @param  Throwable $exception
227
     * @return string                错误信息
228
     */
229
    protected function getMessage(Throwable $exception)
230
    {
231
        $message = $exception->getMessage();
232
233
        if (PHP_SAPI == 'cli') {
234
            return $message;
235
        }
236
237
        $lang = Container::pull('lang');
238
239
        if (strpos($message, ':')) {
240
            $name    = strstr($message, ':', true);
241
            $message = $lang->has($name) ? $lang->get($name) . strstr($message, ':') : $message;
242
        } elseif (strpos($message, ',')) {
243
            $name    = strstr($message, ',', true);
244
            $message = $lang->has($name) ? $lang->get($name) . ':' . substr(strstr($message, ','), 1) : $message;
245
        } elseif ($lang->has($message)) {
246
            $message = $lang->get($message);
247
        }
248
249
        return $message;
250
    }
251
252
    /**
253
     * 获取出错文件内容
254
     * 获取错误的前9行和后9行
255
     * @access protected
256
     * @param  Throwable $exception
257
     * @return array                 错误文件内容
258
     */
259
    protected function getSourceCode(Throwable $exception)
260
    {
261
        // 读取前9行和后9行
262
        $line  = $exception->getLine();
263
        $first = ($line - 9 > 0) ? $line - 9 : 1;
264
265
        try {
266
            $contents = file($exception->getFile()) ?: [];
267
            $source   = [
268
                'first'  => $first,
269
                'source' => array_slice($contents, $first - 1, 19),
270
            ];
271
        } catch (Exception $e) {
272
            $source = [];
273
        }
274
275
        return $source;
276
    }
277
278
    /**
279
     * 获取异常扩展信息
280
     * 用于非调试模式html返回类型显示
281
     * @access protected
282
     * @param  Throwable $exception
283
     * @return array                 异常类定义的扩展数据
284
     */
285
    protected function getExtendData(Throwable $exception)
286
    {
287
        $data = [];
288
289
        if ($exception instanceof \think\Exception) {
290
            $data = $exception->getData();
291
        }
292
293
        return $data;
294
    }
295
296
    /**
297
     * 获取常量列表
298
     * @access private
299
     * @return array 常量列表
300
     */
301
    private static function getConst()
302
    {
303
        $const = get_defined_constants(true);
304
305
        return $const['user'] ?? [];
306
    }
307
}
308