Completed
Push — 6.0 ( 1e69d2...64aa8b )
by yun
02:11
created

Handle::getExtendData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 9
ccs 4
cts 5
cp 0.8
crap 2.032
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 Throwable;
23
24
/**
25
 * 系统异常处理类
26
 */
27
class Handle
28
{
29
    /** @var App */
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 3
    public function __construct(App $app)
43
    {
44 3
        $this->app = $app;
45 3
    }
46
47
    /**
48
     * Report or log an exception.
49
     *
50
     * @access public
51
     * @param Throwable $exception
52
     * @return void
53
     */
54 3
    public function report(Throwable $exception): void
55
    {
56 3
        if (!$this->isIgnoreReport($exception)) {
57
            // 收集异常数据
58 3
            if ($this->app->isDebug()) {
59
                $data = [
60 3
                    'file'    => $exception->getFile(),
61 3
                    'line'    => $exception->getLine(),
62 3
                    'message' => $this->getMessage($exception),
63 3
                    'code'    => $this->getCode($exception),
64
                ];
65 3
                $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 3
            if ($this->app->config->get('log.record_trace')) {
75
                $log .= PHP_EOL . $exception->getTraceAsString();
76
            }
77
78
            try {
79 3
                $this->app->log->record($log, 'error');
80 3
            } catch (Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
81
        }
82 3
    }
83
84 3
    protected function isIgnoreReport(Throwable $exception): bool
85
    {
86 3
        foreach ($this->ignoreReport as $class) {
87 3
            if ($exception instanceof $class) {
88 1
                return true;
89
            }
90
        }
91
92 3
        return false;
93
    }
94
95
    /**
96
     * Render an exception into an HTTP response.
97
     *
98
     * @access public
99
     * @param Request   $request
100
     * @param Throwable $e
101
     * @return Response
102
     */
103 3
    public function render($request, Throwable $e): Response
104
    {
105 3
        $this->isJson = $request->isJson();
106 3
        if ($e instanceof HttpResponseException) {
107
            return $e->getResponse();
108 3
        } elseif ($e instanceof HttpException) {
109
            return $this->renderHttpException($e);
110
        } else {
111 3
            return $this->convertExceptionToResponse($e);
112
        }
113
    }
114
115
    /**
116
     * @access public
117
     * @param Output    $output
118
     * @param Throwable $e
119
     */
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
    /**
130
     * @access protected
131
     * @param HttpException $e
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
149
     * @return array
150
     */
151 3
    protected function convertExceptionToArray(Throwable $exception): array
152
    {
153 3
        if ($this->app->isDebug()) {
154
            // 调试模式,获取详细的错误信息
155 3
            $traces        = [];
156 3
            $nextException = $exception;
157
            do {
158 3
                $traces[] = [
159 3
                    'name'    => get_class($nextException),
160 3
                    'file'    => $nextException->getFile(),
161 3
                    'line'    => $nextException->getLine(),
162 3
                    'code'    => $this->getCode($nextException),
163 3
                    'message' => $this->getMessage($nextException),
164 3
                    'trace'   => $nextException->getTrace(),
165 3
                    'source'  => $this->getSourceCode($nextException),
166
                ];
167 3
            } while ($nextException = $nextException->getPrevious());
168
            $data = [
169 3
                'code'    => $this->getCode($exception),
170 3
                'message' => $this->getMessage($exception),
171 3
                'traces'  => $traces,
172 3
                'datas'   => $this->getExtendData($exception),
173
                'tables'  => [
174 3
                    'GET Data'            => $this->app->request->get(),
175 3
                    'POST Data'           => $this->app->request->post(),
176 3
                    'Files'               => $this->app->request->file(),
177 3
                    'Cookies'             => $this->app->request->cookie(),
178 3
                    '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

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