Completed
Push — 6.0 ( 736b1f...4d1329 )
by liu
05:35
created

Socket::save()   F

Complexity

Conditions 14
Paths 393

Size

Total Lines 101
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 210

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 14
eloc 60
c 3
b 0
f 0
nc 393
nop 1
dl 0
loc 101
ccs 0
cts 46
cp 0
crap 210
rs 3.0208

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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: luofei614 <weibo.com/luofei614>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think\log\driver;
14
15
use Psr\Container\NotFoundExceptionInterface;
16
use think\App;
17
use think\contract\LogHandlerInterface;
18
19
/**
20
 * github: https://github.com/luofei614/SocketLog
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
21
 * @author luofei614<weibo.com/luofei614>
0 ignored issues
show
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
22
 */
23
class Socket implements LogHandlerInterface
24
{
25
    protected $app;
26
27
    protected $config = [
28
        // socket服务器地址
29
        'host'                => 'localhost',
30
        // socket服务器端口
31
        'port'                => 1116,
32
        // 是否显示加载的文件列表
33
        'show_included_files' => false,
34
        // 日志强制记录到配置的client_id
35
        'force_client_ids'    => [],
36
        // 限制允许读取日志的client_id
37
        'allow_client_ids'    => [],
38
        // 调试开关
39
        'debug'               => false,
40
        // 输出到浏览器时默认展开的日志级别
41
        'expand_level'        => ['debug'],
42
        // 日志头渲染回调
43
        'format_head'         => null,
44
    ];
45
46
    protected $css = [
47
        'sql'      => 'color:#009bb4;',
48
        'sql_warn' => 'color:#009bb4;font-size:14px;',
49
        'error'    => 'color:#f4006b;font-size:14px;',
50
        'page'     => 'color:#40e2ff;background:#171717;',
51
        'big'      => 'font-size:20px;color:red;',
52
    ];
53
54
    protected $allowForceClientIds = []; //配置强制推送且被授权的client_id
55
56
    protected $clientArg = [];
57
58
    /**
59
     * 架构函数
60
     * @access public
61
     * @param App   $app
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
62
     * @param array $config 缓存参数
63
     */
64
    public function __construct(App $app, array $config = [])
65
    {
66
        $this->app = $app;
67
68
        if (!empty($config)) {
69
            $this->config = array_merge($this->config, $config);
70
        }
71
72
        if (!isset($config['debug'])) {
73
            $this->config['debug'] = $app->isDebug();
74
        }
75
    }
76
77
    /**
78
     * 调试输出接口
79
     * @access public
80
     * @param array $log 日志信息
81
     * @return bool
82
     */
83
    public function save(array $log = []): bool
84
    {
85
        if (!$this->check()) {
86
            return false;
87
        }
88
89
        $trace = [];
90
91
        if ($this->config['debug']) {
92
            if ($this->app->exists('request')) {
93
                $current_uri = $this->app->request->url(true);
94
            } else {
95
                $current_uri = 'cmd:' . implode(' ', $_SERVER['argv'] ?? []);
96
            }
97
98
            if (!empty($this->config['format_head'])) {
99
                try {
100
                    $current_uri = $this->app->invoke($this->config['format_head'], [$current_uri]);
101
                } catch (NotFoundExceptionInterface $notFoundException) {
102
                    // Ignore exception
103
                }
104
            }
105
106
            // 基本信息
107
            $trace[] = [
108
                'type' => 'group',
109
                'msg'  => $current_uri,
110
                'css'  => $this->css['page'],
111
            ];
112
        }
113
114
        $expand_level = array_flip($this->config['expand_level']);
115
116
        foreach ($log as $type => $val) {
117
            $trace[] = [
118
                'type' => ($expand_level[$type] ?? false) ? 'group' : 'groupCollapsed',
119
                'msg'  => '[ ' . $type . ' ]',
120
                'css'  => $this->css[$type] ?? '',
121
            ];
122
123
            foreach ($val as $msg) {
124
                if (!is_string($msg)) {
125
                    $msg = var_export($msg, true);
126
                }
127
                $trace[] = [
128
                    'type' => 'log',
129
                    'msg'  => $msg,
130
                    'css'  => '',
131
                ];
132
            }
133
134
            $trace[] = [
135
                'type' => 'groupEnd',
136
                'msg'  => '',
137
                'css'  => '',
138
            ];
139
        }
140
141
        if ($this->config['show_included_files']) {
142
            $trace[] = [
143
                'type' => 'groupCollapsed',
144
                'msg'  => '[ file ]',
145
                'css'  => '',
146
            ];
147
148
            $trace[] = [
149
                'type' => 'log',
150
                'msg'  => implode("\n", get_included_files()),
151
                'css'  => '',
152
            ];
153
154
            $trace[] = [
155
                'type' => 'groupEnd',
156
                'msg'  => '',
157
                'css'  => '',
158
            ];
159
        }
160
161
        $trace[] = [
162
            'type' => 'groupEnd',
163
            'msg'  => '',
164
            'css'  => '',
165
        ];
166
167
        $tabid = $this->getClientArg('tabid');
168
169
        if (!$client_id = $this->getClientArg('client_id')) {
170
            $client_id = '';
171
        }
172
173
        if (!empty($this->allowForceClientIds)) {
174
            //强制推送到多个client_id
175
            foreach ($this->allowForceClientIds as $force_client_id) {
176
                $client_id = $force_client_id;
177
                $this->sendToClient($tabid, $client_id, $trace, $force_client_id);
178
            }
179
        } else {
180
            $this->sendToClient($tabid, $client_id, $trace, '');
181
        }
182
183
        return true;
184
    }
185
186
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $tabid should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $client_id should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $logs should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $force_client_id should have a doc-comment as per coding-style.
Loading history...
187
     * 发送给指定客户端
188
     * @access protected
189
     * @author Zjmainstay
190
     * @param  $tabid
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
191
     * @param  $client_id
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
192
     * @param  $logs
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
193
     * @param  $force_client_id
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
194
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
195
    protected function sendToClient($tabid, $client_id, $logs, $force_client_id)
196
    {
197
        $logs = [
198
            'tabid'           => $tabid,
199
            'client_id'       => $client_id,
200
            'logs'            => $logs,
201
            'force_client_id' => $force_client_id,
202
        ];
203
204
        $msg     = json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR);
205
        $address = '/' . $client_id; //将client_id作为地址, server端通过地址判断将日志发布给谁
206
207
        $this->send($this->config['host'], $this->config['port'], $msg, $address);
208
    }
209
210
    /**
211
     * 检测客户授权
212
     * @access protected
213
     * @return bool
214
     */
215
    protected function check()
216
    {
217
        $tabid = $this->getClientArg('tabid');
218
219
        //是否记录日志的检查
220
        if (!$tabid && !$this->config['force_client_ids']) {
221
            return false;
222
        }
223
224
        //用户认证
225
        $allow_client_ids = $this->config['allow_client_ids'];
226
227
        if (!empty($allow_client_ids)) {
228
            //通过数组交集得出授权强制推送的client_id
229
            $this->allowForceClientIds = array_intersect($allow_client_ids, $this->config['force_client_ids']);
230
            if (!$tabid && count($this->allowForceClientIds)) {
231
                return true;
232
            }
233
234
            $client_id = $this->getClientArg('client_id');
235
            if (!in_array($client_id, $allow_client_ids)) {
236
                return false;
237
            }
238
        } else {
239
            $this->allowForceClientIds = $this->config['force_client_ids'];
240
        }
241
242
        return true;
243
    }
244
245
    /**
246
     * 获取客户参数
247
     * @access protected
248
     * @param string $name
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
249
     * @return string
250
     */
251
    protected function getClientArg(string $name)
252
    {
253
        if (!$this->app->exists('request')) {
254
            return '';
255
        }
256
257
        if (empty($this->clientArg)) {
258
            if (empty($socketLog = $this->app->request->header('socketlog'))) {
259
                if (empty($socketLog = $this->app->request->header('User-Agent'))) {
260
                    return '';
261
                }
262
            }
263
264
            if (!preg_match('/SocketLog\((.*?)\)/', $socketLog, $match)) {
0 ignored issues
show
Bug introduced by
It seems like $socketLog can also be of type array; however, parameter $subject of preg_match() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

264
            if (!preg_match('/SocketLog\((.*?)\)/', /** @scrutinizer ignore-type */ $socketLog, $match)) {
Loading history...
265
                $this->clientArg = ['tabid' => null, 'client_id' => null];
266
                return '';
267
            }
268
            parse_str($match[1] ?? '', $this->clientArg);
269
        }
270
271
        if (isset($this->clientArg[$name])) {
272
            return $this->clientArg[$name];
273
        }
274
275
        return '';
276
    }
277
278
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
279
     * @access protected
280
     * @param string $host    - $host of socket server
281
     * @param int    $port    - $port of socket server
282
     * @param string $message - 发送的消息
283
     * @param string $address - 地址
284
     * @return bool
285
     */
286
    protected function send($host, $port, $message = '', $address = '/')
287
    {
288
        $url = 'http://' . $host . ':' . $port . $address;
289
        $ch  = curl_init();
290
291
        curl_setopt($ch, CURLOPT_URL, $url);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

291
        curl_setopt(/** @scrutinizer ignore-type */ $ch, CURLOPT_URL, $url);
Loading history...
292
        curl_setopt($ch, CURLOPT_POST, true);
293
        curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
294
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
295
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
296
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
297
298
        $headers = [
299
            "Content-Type: application/json;charset=UTF-8",
300
        ];
301
302
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //设置header
303
304
        return curl_exec($ch);
0 ignored issues
show
Bug Best Practice introduced by
The expression return curl_exec($ch) also could return the type string which is incompatible with the documented return type boolean.
Loading history...
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

304
        return curl_exec(/** @scrutinizer ignore-type */ $ch);
Loading history...
305
    }
306
}
307