Passed
Push — 8.0 ( f574a5...625916 )
by yun
02:09
created

Handle   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 325
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 8
Bugs 0 Features 0
Metric Value
eloc 124
c 8
b 0
f 0
dl 0
loc 325
ccs 0
cts 135
cp 0
rs 8.5599
wmc 48

17 Methods

Rating   Name   Duplication   Size   Complexity  
A isIgnoreReport() 0 9 3
A getConst() 0 5 1
A render() 0 8 3
A convertExceptionToArray() 0 3 2
A renderExceptionContent() 0 8 2
A getDebugMsg() 0 30 3
A renderHttpException() 0 9 3
A getExtendData() 0 9 2
A getSourceCode() 0 17 4
A __construct() 0 2 1
A report() 0 27 5
A getCode() 0 9 3
B getMessage() 0 21 7
A convertExceptionToResponse() 0 14 3
A renderForConsole() 0 7 2
A getDeployMsg() 0 16 3
A isJson() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Handle often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Handle, and based on these observations, apply Extract Interface, too.

1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006-2021 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 ReflectionClass;
17
use think\App;
18
use think\console\Output;
19
use think\db\exception\DataNotFoundException;
20
use think\db\exception\ModelNotFoundException;
21
use think\Request;
22
use think\Response;
23
use Throwable;
24
25
/**
26
 * 系统异常处理类
27
 */
28
class Handle
29
{
30
    protected $ignoreReport = [
31
        HttpException::class,
32
        HttpResponseException::class,
33
        ModelNotFoundException::class,
34
        DataNotFoundException::class,
35
        ValidateException::class,
36
    ];
37
38
    public function __construct(protected App $app)
39
    {
40
    }
41
42
    /**
43
     * Report or log an exception.
44
     *
45
     * @access public
46
     * @param Throwable $exception
47
     * @return void
48
     */
49
    public function report(Throwable $exception): void
50
    {
51
        if (!$this->isIgnoreReport($exception)) {
52
            // 收集异常数据
53
            if ($this->app->isDebug()) {
54
                $data = [
55
                    'file'    => $exception->getFile(),
56
                    'line'    => $exception->getLine(),
57
                    'message' => $this->getMessage($exception),
58
                    'code'    => $this->getCode($exception),
59
                ];
60
                $log  = "[{$data['code']}]{$data['message']}[{$data['file']}:{$data['line']}]";
61
            } else {
62
                $data = [
63
                    'code'    => $this->getCode($exception),
64
                    'message' => $this->getMessage($exception),
65
                ];
66
                $log  = "[{$data['code']}]{$data['message']}";
67
            }
68
69
            if ($this->app->config->get('log.record_trace')) {
70
                $log .= PHP_EOL . $exception->getTraceAsString();
71
            }
72
73
            try {
74
                $this->app->log->record($log, 'error');
75
            } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
76
            }
77
        }
78
    }
79
80
    protected function isIgnoreReport(Throwable $exception): bool
81
    {
82
        foreach ($this->ignoreReport as $class) {
83
            if ($exception instanceof $class) {
84
                return true;
85
            }
86
        }
87
88
        return false;
89
    }
90
91
    /**
92
     * Render an exception into an HTTP response.
93
     *
94
     * @access public
95
     * @param Request   $request
96
     * @param Throwable $e
97
     * @return Response
98
     */
99
    public function render(Request $request, Throwable $e): Response
100
    {
101
        if ($e instanceof HttpResponseException) {
102
            return $e->getResponse();
103
        } elseif ($e instanceof HttpException) {
104
            return $this->renderHttpException($request, $e);
105
        } else {
106
            return $this->convertExceptionToResponse($request, $e);
107
        }
108
    }
109
110
    /**
111
     * @access public
112
     * @param Output    $output
113
     * @param Throwable $e
114
     */
115
    public function renderForConsole(Output $output, Throwable $e): void
116
    {
117
        if ($this->app->isDebug()) {
118
            $output->setVerbosity(Output::VERBOSITY_DEBUG);
119
        }
120
121
        $output->renderException($e);
122
    }
123
124
    /**
125
     * @access protected
126
     * @param HttpException $e
127
     * @return Response
128
     */
129
    protected function renderHttpException(Request $request, HttpException $e): Response
130
    {
131
        $status   = $e->getStatusCode();
132
        $template = $this->app->config->get('app.http_exception_template');
133
134
        if (!$this->app->isDebug() && !empty($template[$status])) {
135
            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

135
            return Response::create($template[$status], 'view', $status)->/** @scrutinizer ignore-call */ assign(['e' => $e]);
Loading history...
136
        } else {
137
            return $this->convertExceptionToResponse($request, $e);
138
        }
139
    }
140
141
    /**
142
     * 收集异常数据
143
     * @param Throwable $exception
144
     * @return array
145
     */
146
    protected function convertExceptionToArray(Throwable $exception): array
147
    {
148
        return $this->app->isDebug() ? $this->getDebugMsg($exception) : $this->getDeployMsg($exception);
149
    }
150
151
    /**
152
     * 获取部署模式异常数据
153
     * @access protected
154
     * @param Throwable $exception
155
     * @return array
156
     */
157
    protected function getDeployMsg(Throwable $exception): array
158
    {
159
        $data = [
160
            'code'    => $this->getCode($exception),
161
            'message' => $this->getMessage($exception),
162
        ];
163
164
        $reflectionClass = new ReflectionClass($exception);
165
        $alwaysMsg       = $reflectionClass->getAttributes(AlwaysErrorMsg::class);
166
167
        if (empty($alwaysMsg) && !$this->app->config->get('app.show_error_msg')) {
168
            // 不显示详细错误信息
169
            $data['message'] = $this->app->config->get('app.error_message');
170
        }
171
172
        return $data;
173
    }
174
175
    /**
176
     * 收集调试模式异常数据
177
     * @access protected
178
     * @param Throwable $exception
179
     * @return array
180
     */
181
    protected function getDebugMsg(Throwable $exception): array
182
    {
183
        // 调试模式,获取详细的错误信息
184
        $traces        = [];
185
        $nextException = $exception;
186
187
        do {
188
            $traces[] = [
189
                'name'    => $nextException::class,
190
                'file'    => $nextException->getFile(),
191
                'line'    => $nextException->getLine(),
192
                'code'    => $this->getCode($nextException),
193
                'message' => $this->getMessage($nextException),
194
                'trace'   => $nextException->getTrace(),
195
                'source'  => $this->getSourceCode($nextException),
196
            ];
197
        } while ($nextException = $nextException->getPrevious());
198
199
        return [
200
            'code'    => $this->getCode($exception),
201
            'message' => $this->getMessage($exception),
202
            'traces'  => $traces,
203
            'datas'   => $this->getExtendData($exception),
204
            'tables'  => [
205
                'GET Data'            => $this->app->request->get(),
206
                'POST Data'           => $this->app->request->post(),
207
                'Files'               => $this->app->request->file(),
208
                'Cookies'             => $this->app->request->cookie(),
209
                'Session'             => $this->app->exists('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

209
                'Session'             => $this->app->exists('session') ? $this->app->session->/** @scrutinizer ignore-call */ all() : [],
Loading history...
210
                'Server/Request Data' => $this->app->request->server(),
211
            ],
212
        ];
213
    }
214
215
    protected function isJson(Request $request, Throwable $exception)
0 ignored issues
show
Unused Code introduced by
The parameter $exception is not used and could be removed. ( Ignorable by Annotation )

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

215
    protected function isJson(Request $request, /** @scrutinizer ignore-unused */ Throwable $exception)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
216
    {
217
        return $request->isJson();
218
    }
219
220
    /**
221
     * @access protected
222
     * @param Throwable $exception
223
     * @return Response
224
     */
225
    protected function convertExceptionToResponse(Request $request, Throwable $exception): Response
226
    {
227
        if ($this->isJson($request, $exception)) {
228
            $response = Response::create($this->convertExceptionToArray($exception), 'json');
229
        } else {
230
            $response = Response::create($this->renderExceptionContent($exception));
231
        }
232
233
        if ($exception instanceof HttpException) {
234
            $statusCode = $exception->getStatusCode();
235
            $response->header($exception->getHeaders());
236
        }
237
238
        return $response->code($statusCode ?? 500);
239
    }
240
241
    protected function renderExceptionContent(Throwable $exception): string
242
    {
243
        ob_start();
244
        $data = $this->convertExceptionToArray($exception);
245
        extract($data);
246
        include $this->app->config->get('app.exception_tmpl') ?: __DIR__ . '/../../tpl/think_exception.tpl';
247
248
        return ob_get_clean();
249
    }
250
251
    /**
252
     * 获取错误编码
253
     * ErrorException则使用错误级别作为错误编码
254
     * @access protected
255
     * @param Throwable $exception
256
     * @return integer                错误编码
257
     */
258
    protected function getCode(Throwable $exception)
259
    {
260
        $code = $exception->getCode();
261
262
        if (!$code && $exception instanceof ErrorException) {
263
            $code = $exception->getSeverity();
264
        }
265
266
        return $code;
267
    }
268
269
    /**
270
     * 获取错误信息
271
     * ErrorException则使用错误级别作为错误编码
272
     * @access protected
273
     * @param Throwable $exception
274
     * @return string                错误信息
275
     */
276
    protected function getMessage(Throwable $exception): string
277
    {
278
        $message = $exception->getMessage();
279
280
        if ($this->app->runningInConsole()) {
281
            return $message;
282
        }
283
284
        $lang = $this->app->lang;
285
286
        if (str_contains($message, ':')) {
287
            $name    = strstr($message, ':', true);
288
            $message = $lang->has($name) ? $lang->get($name) . strstr($message, ':') : $message;
289
        } elseif (str_contains($message, ',')) {
290
            $name    = strstr($message, ',', true);
291
            $message = $lang->has($name) ? $lang->get($name) . ':' . substr(strstr($message, ','), 1) : $message;
292
        } elseif ($lang->has($message)) {
293
            $message = $lang->get($message);
294
        }
295
296
        return $message;
297
    }
298
299
    /**
300
     * 获取出错文件内容
301
     * 获取错误的前9行和后9行
302
     * @access protected
303
     * @param Throwable $exception
304
     * @return array                 错误文件内容
305
     */
306
    protected function getSourceCode(Throwable $exception): array
307
    {
308
        // 读取前9行和后9行
309
        $line  = $exception->getLine();
310
        $first = ($line - 9 > 0) ? $line - 9 : 1;
311
312
        try {
313
            $contents = file($exception->getFile()) ?: [];
314
            $source   = [
315
                'first'  => $first,
316
                'source' => array_slice($contents, $first - 1, 19),
317
            ];
318
        } catch (Exception $e) {
319
            $source = [];
320
        }
321
322
        return $source;
323
    }
324
325
    /**
326
     * 获取异常扩展信息
327
     * 用于非调试模式html返回类型显示
328
     * @access protected
329
     * @param Throwable $exception
330
     * @return array                 异常类定义的扩展数据
331
     */
332
    protected function getExtendData(Throwable $exception): array
333
    {
334
        $data = [];
335
336
        if ($exception instanceof \think\Exception) {
337
            $data = $exception->getData();
338
        }
339
340
        return $data;
341
    }
342
343
    /**
344
     * 获取常量列表
345
     * @access protected
346
     * @return array 常量列表
347
     */
348
    protected function getConst(): array
349
    {
350
        $const = get_defined_constants(true);
351
352
        return $const['user'] ?? [];
353
    }
354
}
355