Completed
Push — 6.0 ( 2801f0...a75685 )
by yun
05:44
created

Handle::renderExceptionContent()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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

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