Completed
Pull Request — 6.0 (#2072)
by nhzex
05:15
created

Socket::send()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 12
c 2
b 0
f 0
nc 1
nop 3
dl 0
loc 19
ccs 0
cts 12
cp 0
crap 2
rs 9.8666
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
    /**
200
     * 检测客户授权
201
     * @access protected
202
     * @return bool
203
     */
204
    protected function check()
205
    {
206
        $tabid = $this->getClientArg('tabid');
207
208
        //是否记录日志的检查
209
        if (!$tabid && !$this->config['force_client_ids']) {
210
            return false;
211
        }
212
213
        //用户认证
214
        $allow_client_ids = $this->config['allow_client_ids'];
215
216
        if (!empty($allow_client_ids)) {
217
            //通过数组交集得出授权强制推送的client_id
218
            $this->allowForceClientIds = array_intersect($allow_client_ids, $this->config['force_client_ids']);
219
            if (!$tabid && count($this->allowForceClientIds)) {
220
                return true;
221
            }
222
223
            $client_id = $this->getClientArg('client_id');
224
            if (!in_array($client_id, $allow_client_ids)) {
225
                return false;
226
            }
227
        } else {
228
            $this->allowForceClientIds = $this->config['force_client_ids'];
229
        }
230
231
        return true;
232
    }
233
234
    /**
235
     * 获取客户参数
236
     * @access protected
237
     * @param string $name
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
238
     * @return string
239
     */
240
    protected function getClientArg(string $name)
241
    {
242
        if (!$this->app->exists('request')) {
243
            return '';
244
        }
245
246
        if (empty($this->clientArg)) {
247
            if (empty($socketLog = $this->app->request->header('socketlog'))) {
248
                if (empty($socketLog = $this->app->request->header('User-Agent'))) {
249
                    return '';
250
                }
251
            }
252
253
            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

253
            if (!preg_match('/SocketLog\((.*?)\)/', /** @scrutinizer ignore-type */ $socketLog, $match)) {
Loading history...
254
                $this->clientArg = ['tabid' => null, 'client_id' => null];
255
                return '';
256
            }
257
            parse_str($match[1] ?? '', $this->clientArg);
258
        }
259
260
        if (isset($this->clientArg[$name])) {
261
            return $this->clientArg[$name];
262
        }
263
264
        return '';
265
    }
266
267
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
268
     * @access protected
269
     * @param string $host    - $host of socket server
270
     * @param string $message - 发送的消息
271
     * @param string $address - 地址
272
     * @return bool
273
     */
274
    protected function send($host, $message = '', $address = '/')
275
    {
276
        $url = 'http://' . $host . ':' . $this->port . $address;
277
        $ch  = curl_init();
278
279
        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

279
        curl_setopt(/** @scrutinizer ignore-type */ $ch, CURLOPT_URL, $url);
Loading history...
280
        curl_setopt($ch, CURLOPT_POST, true);
281
        curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
282
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
283
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
284
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
285
286
        $headers = [
287
            "Content-Type: application/json;charset=UTF-8",
288
        ];
289
290
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //设置header
291
292
        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

292
        return curl_exec(/** @scrutinizer ignore-type */ $ch);
Loading history...
293
    }
294
}
295