Passed
Push — 6.0 ( 351e06...85e367 )
by liu
02:30
created

Session::init()   B

Complexity

Conditions 10
Paths 64

Size

Total Lines 35
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 18
nc 64
nop 0
dl 0
loc 35
rs 7.6666
c 0
b 0
f 0

How to fix   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
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: liu21st <[email protected]>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think;
14
15
use think\exception\ClassNotFoundException;
16
17
class Session
1 ignored issue
show
Coding Style introduced by
Missing class doc comment
Loading history...
18
{
19
    /**
20
     * 配置参数
21
     * @var array
22
     */
23
    protected $config = [];
24
25
    /**
26
     * Session数据
27
     * @var array
28
     */
29
    protected $data = [];
30
31
    /**
32
     * 是否初始化
33
     * @var bool
34
     */
35
    protected $init = null;
36
37
    /**
38
     * 锁驱动
39
     * @var object
40
     */
41
    protected $lockDriver = null;
42
43
    /**
44
     * 锁key
45
     * @var string
46
     */
47
    protected $sessKey = 'PHPSESSID';
48
49
    /**
50
     * 锁超时时间
51
     * @var integer
52
     */
53
    protected $lockTimeout = 3;
54
55
    /**
56
     * 是否启用锁机制
57
     * @var bool
58
     */
59
    protected $lock = false;
60
61
    /**
62
     * App实例
63
     * @var App
64
     */
65
    protected $app;
66
67
    public function __construct(App $app, array $config = [])
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
68
    {
69
        $this->config = $config;
70
        $this->app    = $app;
71
    }
72
73
    public static function __make(App $app, Config $config)
1 ignored issue
show
Coding Style introduced by
Missing function doc comment
Loading history...
Coding Style introduced by
Method name "Session::__make" is invalid; only PHP magic methods should be prefixed with a double underscore
Loading history...
74
    {
75
        return new static($app, $config->get('session'));
0 ignored issues
show
Bug introduced by
It seems like $config->get('session') can also be of type null; however, parameter $config of think\Session::__construct() does only seem to accept array, 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

75
        return new static($app, /** @scrutinizer ignore-type */ $config->get('session'));
Loading history...
76
    }
77
78
    /**
79
     * 配置
80
     * @access public
81
     * @param  array $config
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
82
     * @return void
83
     */
84
    public function setConfig(array $config = []): void
85
    {
86
        $this->config = array_merge($this->config, array_change_key_case($config));
87
88
        if (isset($config['use_lock'])) {
89
            $this->lock = $config['use_lock'];
90
        }
91
    }
92
93
    /**
94
     * 设置数据
95
     * @access public
96
     * @param  array $data
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
97
     * @return void
98
     */
99
    public function setData(array $data): void
100
    {
101
        $this->data = $data;
102
    }
103
104
    /**
105
     * session初始化
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
106
     * @access public
107
     * @return void
108
     * @throws \think\Exception
109
     */
110
    public function init(): void
111
    {
112
        $config = $this->config;
113
114
        if (isset($config['use_lock'])) {
115
            $this->lock = $config['use_lock'];
116
        }
117
118
        if (!empty($config['id'])) {
119
            session_id($config['id']);
120
        }
121
122
        if (!empty($config['type'])) {
123
            // 读取session驱动
124
            $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']);
125
126
            // 检查驱动类
127
            if (!class_exists($class) || !session_set_save_handler(new $class($config))) {
128
                throw new ClassNotFoundException('error session handler:' . $class, $class);
129
            }
130
        }
131
132
        if (!empty($config['auto_start'])) {
133
            try {
134
                session_start($config['options'] ?? []);
135
            } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
136
            }
137
138
            if (empty($this->data)) {
139
                $this->data = $_SESSION;
140
            }
141
142
            $this->init = true;
143
        } else {
144
            $this->init = false;
145
        }
146
    }
147
148
    /**
149
     * session自动启动或者初始化
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
150
     * @access public
151
     * @return void
152
     */
153
    public function boot(): void
154
    {
155
        if (is_null($this->init)) {
0 ignored issues
show
introduced by
The condition is_null($this->init) is always false.
Loading history...
156
            $this->init();
157
        }
158
159
        if (false === $this->init) {
160
            if (PHP_SESSION_ACTIVE != session_status()) {
161
                session_start();
162
            }
163
            $this->init = true;
164
        }
165
    }
166
167
    /**
168
     * session设置
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
169
     * @access public
170
     * @param  string        $name session名称
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
171
     * @param  mixed         $value session值
172
     * @return void
173
     */
174
    public function set(string $name, $value): void
175
    {
176
        $this->lock();
177
178
        empty($this->init) && $this->boot();
179
180
        if (strpos($name, '.')) {
181
            // 二维数组赋值
182
            list($name1, $name2) = explode('.', $name);
183
184
            $this->data[$name1][$name2] = $value;
185
        } else {
186
            $this->data[$name] = $value;
187
        }
188
189
        $this->unlock();
190
    }
191
192
    /**
193
     * session获取
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
194
     * @access public
195
     * @param  string        $name session名称
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
196
     * @param  mixed         $default 默认值
197
     * @return mixed
198
     */
199
    public function get(string $name = '', $default = null)
200
    {
201
        $this->lock();
202
203
        empty($this->init) && $this->boot();
204
205
        $value = $this->data;
206
207
        if ('' != $name) {
208
            $name = explode('.', $name);
209
210
            foreach ($name as $val) {
211
                if (isset($value[$val])) {
212
                    $value = $value[$val];
213
                } else {
214
                    $value = $default;
215
                    break;
216
                }
217
            }
218
        }
219
220
        $this->unlock();
221
222
        return $value;
223
    }
224
225
    /**
226
     * session 读写锁驱动实例化
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
227
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
228
    protected function initDriver(): void
229
    {
230
        // 不在 init 方法中实例化lockDriver,是因为 init 方法不一定先于 set 或 get 方法调用
231
        $config = $this->config;
232
233
        if (!empty($config['type']) && !empty($config['use_lock'])) {
234
            // 读取session驱动
235
            $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']);
236
237
            // 检查驱动类及类中是否存在 lock 和 unlock 函数
238
            if (class_exists($class) && method_exists($class, 'lock') && method_exists($class, 'unlock')) {
239
                $this->lockDriver = new $class($config);
240
            }
241
        }
242
243
        // 通过cookie获得session_id
244
        if (!empty($config['name'])) {
245
            $this->sessKey = $config['name'];
246
        }
247
248
        if (isset($config['lock_timeout']) && $config['lock_timeout'] > 0) {
249
            $this->lockTimeout = $config['lock_timeout'];
250
        }
251
    }
252
253
    /**
254
     * session 读写加锁
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
255
     * @access protected
256
     * @return void
257
     */
258
    protected function lock()
259
    {
260
        if (empty($this->lock)) {
261
            return;
262
        }
263
264
        $this->initDriver();
265
266
        if (null !== $this->lockDriver && method_exists($this->lockDriver, 'lock')) {
267
            $t = time();
268
            // 使用 session_id 作为互斥条件,即只对同一 session_id 的会话互斥。第一次请求没有 session_id
269
            $sessID = $this->app->cookie->get($this->sessKey) ?: '';
270
271
            do {
272
                if (time() - $t > $this->lockTimeout) {
273
                    $this->unlock();
274
                }
275
            } while (!$this->lockDriver->lock($sessID, $this->lockTimeout));
276
        }
277
    }
278
279
    /**
280
     * session 读写解锁
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
281
     * @access protected
282
     * @return void
283
     */
284
    protected function unlock()
285
    {
286
        if (empty($this->lock)) {
287
            return;
288
        }
289
290
        $this->pause();
291
292
        if ($this->lockDriver && method_exists($this->lockDriver, 'unlock')) {
293
            $sessID = $this->app->cookie->get($this->sessKey) ?: '';
294
            $this->lockDriver->unlock($sessID);
295
        }
296
    }
297
298
    /**
299
     * session获取并删除
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
300
     * @access public
301
     * @param  string        $name session名称
302
     * @return mixed
303
     */
304
    public function pull(string $name)
305
    {
306
        $result = $this->get($name);
307
308
        if ($result) {
309
            $this->delete($name);
310
            return $result;
311
        }
312
    }
313
314
    /**
315
     * session设置 下一次请求有效
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
316
     * @access public
317
     * @param  string        $name session名称
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
318
     * @param  mixed         $value session值
319
     * @return void
320
     */
321
    public function flash(string $name, $value): void
322
    {
323
        $this->set($name, $value);
324
325
        if (!$this->has('__flash__.__time__')) {
326
            $this->set('__flash__.__time__', $this->app->request->server('REQUEST_TIME_FLOAT'));
327
        }
328
329
        $this->push('__flash__', $name);
330
    }
331
332
    /**
333
     * 清空当前请求的session数据
334
     * @access public
335
     * @return void
336
     */
337
    public function flush()
338
    {
339
        if (!$this->init) {
340
            return;
341
        }
342
343
        $item = $this->get('__flash__');
344
345
        if (!empty($item)) {
346
            $time = $item['__time__'];
347
348
            if ($this->app->request->server('REQUEST_TIME_FLOAT') > $time) {
349
                unset($item['__time__']);
350
                $this->delete($item);
351
                $this->set('__flash__', []);
352
            }
353
        }
354
    }
355
356
    /**
357
     * 删除session数据
358
     * @access public
359
     * @param  string|array  $name session名称
360
     * @return void
361
     */
362
    public function delete($name): void
363
    {
364
        empty($this->init) && $this->boot();
365
366
        if (is_array($name)) {
367
            foreach ($name as $key) {
368
                $this->delete($key);
369
            }
370
        } elseif (strpos($name, '.')) {
371
            list($name1, $name2) = explode('.', $name);
372
373
            unset($this->data[$name1][$name2]);
374
        } else {
375
            unset($this->data[$name]);
376
        }
377
    }
378
379
    /**
380
     * 清空session数据
381
     * @access public
382
     * @return void
383
     */
384
    public function clear(): void
385
    {
386
        empty($this->init) && $this->boot();
387
388
        $this->data = [];
389
    }
390
391
    /**
392
     * 判断session数据
393
     * @access public
394
     * @param  string       $name session名称
395
     * @return bool
396
     */
397
    public function has(string $name): bool
398
    {
399
        empty($this->init) && $this->boot();
400
401
        $value = $this->data;
402
403
        $name = explode('.', $name);
404
405
        foreach ($name as $val) {
406
            if (!isset($value[$val])) {
407
                return false;
408
            } else {
409
                $value = $value[$val];
410
            }
411
        }
412
413
        return true;
414
    }
415
416
    /**
417
     * 添加数据到一个session数组
418
     * @access public
419
     * @param  string  $key
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
420
     * @param  mixed   $value
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
421
     * @return void
422
     */
423
    public function push(string $key, $value): void
424
    {
425
        $array = $this->get($key);
426
427
        if (is_null($array)) {
428
            $array = [];
429
        }
430
431
        $array[] = $value;
432
433
        $this->set($key, $array);
434
    }
435
436
    /**
437
     * 启动session
438
     * @access public
439
     * @return void
440
     */
441
    public function start(): void
442
    {
443
        session_start();
444
445
        if (empty($this->data)) {
446
            $this->data = $_SESSION;
447
        }
448
449
        $this->init = true;
450
    }
451
452
    /**
453
     * 销毁session
454
     * @access public
455
     * @return void
456
     */
457
    public function destroy(): void
458
    {
459
        if (!empty($this->data)) {
460
            $this->data = [];
461
        }
462
463
        session_unset();
464
        session_destroy();
465
466
        $this->init       = null;
467
        $this->lockDriver = null;
468
    }
469
470
    /**
471
     * 重新生成session_id
472
     * @access public
473
     * @param  bool $delete 是否删除关联会话文件
474
     * @return string
475
     */
476
    public function regenerate(bool $delete = false): string
477
    {
478
        session_regenerate_id($delete);
479
        return session_id();
480
    }
481
482
    /**
483
     * 暂停session
484
     * @access public
485
     * @return void
486
     */
487
    public function pause(): void
488
    {
489
        // 暂停session
490
        session_write_close();
491
        $this->init = false;
492
    }
493
494
    /**
495
     * 析构方法
496
     * @access public
497
     */
498
    public function __destruct()
499
    {
500
        $_SESSION = $this->data;
501
    }
502
}
503