Completed
Push — 6.0 ( 0d3bb7...2e57ce )
by liu
06:03
created

Handle::isIgnoreReport()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 1
dl 0
loc 9
ccs 0
cts 5
cp 0
crap 12
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
declare (strict_types = 1);
12
13
namespace think\exception;
14
15
use Exception;
16
use think\App;
17
use think\console\Output;
18
use think\db\exception\DataNotFoundException;
19
use think\db\exception\ModelNotFoundException;
20
use think\Request;
21
use think\Response;
22
use Throwable;
23
24
/**
25
 * 系统异常处理类
26
 */
27
class Handle
28
{
29
    /** @var App */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
30
    protected $app;
31
32
    protected $ignoreReport = [
33
        HttpException::class,
34
        HttpResponseException::class,
35
        ModelNotFoundException::class,
36
        DataNotFoundException::class,
37
        ValidateException::class,
38
    ];
39
40
    protected $isJson = false;
41
42
    public function __construct(App $app)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
43
    {
44
        $this->app = $app;
45
    }
46
47
    /**
48
     * Report or log an exception.
49
     *
50
     * @access public
51
     * @param Throwable $exception
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
52
     * @return void
53
     */
54
    public function report(Throwable $exception): void
55
    {
56
        if (!$this->isIgnoreReport($exception)) {
57
            // 收集异常数据
58
            if ($this->app->isDebug()) {
59
                $data = [
60
                    'file'    => $exception->getFile(),
61
                    'line'    => $exception->getLine(),
62
                    'message' => $this->getMessage($exception),
63
                    'code'    => $this->getCode($exception),
64
                ];
65
                $log = "[{$data['code']}]{$data['message']}[{$data['file']}:{$data['line']}]";
66
            } else {
67
                $data = [
68
                    'code'    => $this->getCode($exception),
69
                    'message' => $this->getMessage($exception),
70
                ];
71
                $log = "[{$data['code']}]{$data['message']}";
72
            }
73
74
            if ($this->app->config->get('log.record_trace')) {
75
                $log .= PHP_EOL . $exception->getTraceAsString();
76
            }
77
78
            try {
79
                $this->app->log->record($log, 'error');
80
            } catch (Exception $e){}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
81
        }
82
    }
83
84
    protected function isIgnoreReport(Throwable $exception): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function isIgnoreReport()
Loading history...
85
    {
86
        foreach ($this->ignoreReport as $class) {
87
            if ($exception instanceof $class) {
88
                return true;
89
            }
90
        }
91
92
        return false;
93
    }
94
95
    /**
96
     * Render an exception into an HTTP response.
97
     *
98
     * @access public
99
     * @param Request   $request
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
100
     * @param Throwable $e
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
101
     * @return Response
102
     */
103
    public function render($request, Throwable $e): Response
104
    {
105
        $this->isJson = $request->isJson();
106
        if ($e instanceof HttpResponseException) {
107
            return $e->getResponse();
108
        } elseif ($e instanceof HttpException) {
109
            return $this->renderHttpException($e);
110
        } else {
111
            return $this->convertExceptionToResponse($e);
112
        }
113
    }
114
115
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
116
     * @access public
117
     * @param Output    $output
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
118
     * @param Throwable $e
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
119
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
120
    public function renderForConsole(Output $output, Throwable $e): void
121
    {
122
        if ($this->app->isDebug()) {
123
            $output->setVerbosity(Output::VERBOSITY_DEBUG);
124
        }
125
126
        $output->renderException($e);
127
    }
128
129
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
130
     * @access protected
131
     * @param HttpException $e
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
132
     * @return Response
133
     */
134
    protected function renderHttpException(HttpException $e): Response
135
    {
136
        $status   = $e->getStatusCode();
137
        $template = $this->app->config->get('app.http_exception_template');
138
139
        if (!$this->app->isDebug() && !empty($template[$status])) {
140
            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. It seems like you code against a sub-type of think\Response such as think\response\View. ( Ignorable by Annotation )

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

140
            return Response::create($template[$status], 'view', $status)->/** @scrutinizer ignore-call */ assign(['e' => $e]);
Loading history...
141
        } else {
142
            return $this->convertExceptionToResponse($e);
143
        }
144
    }
145
146
    /**
147
     * 收集异常数据
148
     * @param Throwable $exception
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
149
     * @return array
150
     */
151
    protected function convertExceptionToArray(Throwable $exception): array
152
    {
153
        if ($this->app->isDebug()) {
154
            // 调试模式,获取详细的错误信息
155
            $traces = [];
156
            $nextException = $exception;
157
            do {
158
                $traces[] = [
159
                    'name'    => get_class($nextException),
160
                    'file'    => $nextException->getFile(),
161
                    'line'    => $nextException->getLine(),
162
                    'code'    => $this->getCode($nextException),
163
                    'message' => $this->getMessage($nextException),
164
                    'trace'   => $nextException->getTrace(),
165
                    'source'  => $this->getSourceCode($nextException),
166
                ];
167
            } while ($nextException = $nextException->getPrevious());
168
            $data = [
169
                'code'    => $this->getCode($exception),
170
                'message' => $this->getMessage($exception),
171
                'traces'  => $traces,
172
                'datas'   => $this->getExtendData($exception),
173
                'tables'  => [
174
                    'GET Data'              => $this->app->request->get(),
175
                    'POST Data'             => $this->app->request->post(),
176
                    'Files'                 => $this->app->request->file(),
177
                    'Cookies'               => $this->app->request->cookie(),
178
                    'Session'               => $this->app->session->all(),
0 ignored issues
show
Bug introduced by
The method all() does not exist on think\Session. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

178
                    'Session'               => $this->app->session->/** @scrutinizer ignore-call */ all(),
Loading history...
179
                    'Server/Request Data'   => $this->app->request->server(),
180
                    'Environment Variables' => $this->app->request->env(),
181
                    'ThinkPHP Constants'    => $this->getConst(),
182
                ],
183
            ];
184
        } else {
185
            // 部署模式仅显示 Code 和 Message
186
            $data = [
187
                'code'    => $this->getCode($exception),
188
                'message' => $this->getMessage($exception),
189
            ];
190
191
            if (!$this->app->config->get('app.show_error_msg')) {
192
                // 不显示详细错误信息
193
                $data['message'] = $this->app->config->get('app.error_message');
194
            }
195
        }
196
197
        return $data;
198
    }
199
200
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
201
     * @access protected
202
     * @param Throwable $exception
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
203
     * @return Response
204
     */
205
    protected function convertExceptionToResponse(Throwable $exception): Response
206
    {
207
        if (!$this->isJson) {
208
            $response = Response::create($this->renderExceptionContent($exception));
209
        } else {
210
            $response = Response::create($this->convertExceptionToArray($exception), 'json');
211
        }
212
213
        if ($exception instanceof HttpException) {
214
            $statusCode = $exception->getStatusCode();
215
            $response->header($exception->getHeaders());
216
        }
217
218
        return $response->code($statusCode ?? 500);
219
    }
220
221
    protected function renderExceptionContent(Throwable $exception): string
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function renderExceptionContent()
Loading history...
222
    {
223
        ob_start();
224
        $data = $this->convertExceptionToArray($exception);
225
        extract($data);
226
        include $this->app->config->get('app.exception_tmpl') ?: __DIR__ . '/../../tpl/think_exception.tpl';
227
228
        return ob_get_clean();
229
    }
230
231
    /**
232
     * 获取错误编码
233
     * ErrorException则使用错误级别作为错误编码
234
     * @access protected
235
     * @param Throwable $exception
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
236
     * @return integer                错误编码
237
     */
238
    protected function getCode(Throwable $exception)
239
    {
240
        $code = $exception->getCode();
241
242
        if (!$code && $exception instanceof ErrorException) {
243
            $code = $exception->getSeverity();
244
        }
245
246
        return $code;
247
    }
248
249
    /**
250
     * 获取错误信息
251
     * ErrorException则使用错误级别作为错误编码
252
     * @access protected
253
     * @param Throwable $exception
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
254
     * @return string                错误信息
255
     */
256
    protected function getMessage(Throwable $exception): string
257
    {
258
        $message = $exception->getMessage();
259
260
        if ($this->app->runningInConsole()) {
261
            return $message;
262
        }
263
264
        $lang = $this->app->lang;
265
266
        if (strpos($message, ':')) {
267
            $name    = strstr($message, ':', true);
268
            $message = $lang->has($name) ? $lang->get($name) . strstr($message, ':') : $message;
269
        } elseif (strpos($message, ',')) {
270
            $name    = strstr($message, ',', true);
271
            $message = $lang->has($name) ? $lang->get($name) . ':' . substr(strstr($message, ','), 1) : $message;
272
        } elseif ($lang->has($message)) {
273
            $message = $lang->get($message);
274
        }
275
276
        return $message;
277
    }
278
279
    /**
280
     * 获取出错文件内容
281
     * 获取错误的前9行和后9行
282
     * @access protected
283
     * @param Throwable $exception
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
284
     * @return array                 错误文件内容
285
     */
286
    protected function getSourceCode(Throwable $exception): array
287
    {
288
        // 读取前9行和后9行
289
        $line  = $exception->getLine();
290
        $first = ($line - 9 > 0) ? $line - 9 : 1;
291
292
        try {
293
            $contents = file($exception->getFile()) ?: [];
294
            $source   = [
295
                'first'  => $first,
296
                'source' => array_slice($contents, $first - 1, 19),
297
            ];
298
        } catch (Exception $e) {
299
            $source = [];
300
        }
301
302
        return $source;
303
    }
304
305
    /**
306
     * 获取异常扩展信息
307
     * 用于非调试模式html返回类型显示
308
     * @access protected
309
     * @param Throwable $exception
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
310
     * @return array                 异常类定义的扩展数据
311
     */
312
    protected function getExtendData(Throwable $exception): array
313
    {
314
        $data = [];
315
316
        if ($exception instanceof \think\Exception) {
317
            $data = $exception->getData();
318
        }
319
320
        return $data;
321
    }
322
323
    /**
324
     * 获取常量列表
325
     * @access protected
326
     * @return array 常量列表
327
     */
328
    protected function getConst(): array
329
    {
330
        $const = get_defined_constants(true);
331
332
        return $const['user'] ?? [];
333
    }
334
}
335