Passed
Push — master ( 102376...acba12 )
by 世昌
03:37
created

Debugger   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 154
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
eloc 58
c 7
b 0
f 0
dl 0
loc 154
rs 10
wmc 20

9 Methods

Rating   Name   Duplication   Size   Complexity  
A register() 0 6 1
A __construct() 0 12 3
A getLogger() 0 3 1
A writeTiming() 0 16 4
A uncaughtException() 0 3 1
A getDefaultConfig() 0 6 1
A uncaughtFatalError() 0 15 5
A uncaughtError() 0 15 3
A getIgnoreTraces() 0 5 1
1
<?php
2
3
namespace suda\framework;
4
5
6
use ErrorException;
7
use Psr\Log\LoggerInterface;
8
use suda\framework\debug\Debug;
9
use suda\framework\debug\log\logger\NullLogger;
10
use Throwable;
11
12
/**
13
 * 调试器
14
 */
15
class Debugger extends Debug
16
{
17
    /**
18
     * 环境内容
19
     *
20
     * @var Context
21
     */
22
    protected $context;
23
24
    /**
25
     * 初始化
26
     * @param Context $context
27
     * @param LoggerInterface $logger
28
     */
29
    public function __construct(Context $context, LoggerInterface $logger)
30
    {
31
        $this->context = $context;
32
        $this->logger = $logger;
33
        $this->applyConfig([
34
            'start-time' => defined('SUDA_START_TIME') ? constant('SUDA_START_TIME') : microtime(true),
35
            'start-memory' => defined('SUDA_START_MEMORY') ? constant('SUDA_START_MEMORY') : memory_get_usage(),
36
        ]);
37
        $this->context = $context;
38
        $this->timing = [];
39
        $this->timeRecord = [];
40
        $context->event()->listen('response::before-send', [$this, 'writeTiming']);
41
    }
42
43
    /**
44
     * 注册错误处理函数
45
     * @return $this
46
     */
47
    public function register()
48
    {
49
        register_shutdown_function([$this, 'uncaughtFatalError']);
50
        set_error_handler([$this, 'uncaughtError']);
51
        set_exception_handler([$this, 'uncaughtException']);
52
        return $this;
53
    }
54
55
    /**
56
     * @param Response $response
57
     */
58
    public function writeTiming(Response $response)
59
    {
60
        $output = $this->context->getConfig()->get('response-timing', true);
61
        if ($output) {
62
            $timing = [];
63
            foreach ($this->timing as $name => $info) {
64
                $time = $info['time'];
65
                $desc = $info['description'];
66
                $ms = number_format($time * 1000, 3, '.', '');
67
                if (strlen($desc)) {
68
                    $timing[] = $name . ';desc="' . $desc . '";dur=' . $ms;
69
                } else {
70
                    $timing[] = $name . ';dur=' . $ms;
71
                }
72
            }
73
            $response->setHeader('server-timing', implode(',', $timing));
74
        }
75
    }
76
77
    /**
78
     * 获取原始记录器
79
     *
80
     * @return LoggerInterface
81
     */
82
    public function getLogger(): LoggerInterface
83
    {
84
        return $this->logger;
85
    }
86
87
    /**
88
     * 末异常处理
89
     *
90
     * @return void
91
     */
92
    public function uncaughtFatalError()
93
    {
94
        if (function_exists('fastcgi_finish_request')) {
95
            fastcgi_finish_request();
96
        }
97
        if ($e = error_get_last()) {
98
            $isFatalError = E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING;
99
            if ($e['type'] === ($e['type'] & $isFatalError)) {
100
                $handler = set_error_handler(null);
101
                if ($handler !== null) {
102
                    $handler($e['type'], $e['message'], $e['file'], $e['line']);
103
                }
104
                restore_error_handler();
105
            } else {
106
                $this->error($e['message'], ['exception' => $e]);
107
            }
108
        }
109
    }
110
111
    /**
112
     * 异常托管
113
     *
114
     * @param Throwable $exception
115
     * @return void
116
     */
117
    public function uncaughtException($exception)
118
    {
119
        $this->error($exception->getMessage(), ['exception' => $exception]);
120
    }
121
122
    /**
123
     * 错误托管
124
     *
125
     * @param int $errno
126
     * @param string $errstr
127
     * @param string $errfile
128
     * @param int $errline
129
     * @return void
130
     * @throws ErrorException
131
     */
132
    public function uncaughtError($errno, $errstr, $errfile, $errline)
133
    {
134
        $isFatalError = E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING;
135
        $exception = new ErrorException($errstr, $errno, $errno, $errfile, $errline);
136
        if ($exception->getSeverity() & $isFatalError === 0) {
137
            $exceptionHandler = set_exception_handler(null);
138
            // 有上一级非默认处理器
139
            if ($exceptionHandler !== null) {
140
                $exceptionHandler($exception);
141
                restore_exception_handler();
142
            } else {
143
                throw $exception;
144
            }
145
        } else {
146
            $this->warning($errstr, ['exception' => $exception]);
147
        }
148
    }
149
150
    public function getDefaultConfig(): array
151
    {
152
        return [
153
            'log-format' => '%time-format% - %memory-format% [%level%] %file%:%line% %message%',
154
            'start-time' => 0,
155
            'start-memory' => 0,
156
        ];
157
    }
158
159
    /**
160
     * 设置忽略前缀
161
     *
162
     * @return array
163
     */
164
    public function getIgnoreTraces(): array
165
    {
166
        $trace = parent::getIgnoreTraces();
167
        $trace[] = __FILE__;
168
        return $trace;
169
    }
170
}
171