Completed
Pull Request — 6.0 (#2072)
by nhzex
09:57
created

Socket::getClientArg()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

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

268
        curl_setopt(/** @scrutinizer ignore-type */ $ch, CURLOPT_URL, $url);
Loading history...
269
        curl_setopt($ch, CURLOPT_POST, true);
270
        curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
271
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
272
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
273
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
274
275
        $headers = [
276
            "Content-Type: application/json;charset=UTF-8",
277
        ];
278
279
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //设置header
280
281
        return curl_exec($ch);
0 ignored issues
show
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

281
        return curl_exec(/** @scrutinizer ignore-type */ $ch);
Loading history...
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...
282
    }
283
}
284