Passed
Push — 6.0 ( 2c3770...97220f )
by liu
02:24
created

Session::push()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 11
rs 10
c 0
b 0
f 0
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
     * 是否初始化
27
     * @var bool
28
     */
29
    protected $init = null;
30
31
    /**
32
     * 锁驱动
33
     * @var object
34
     */
35
    protected $lockDriver = null;
36
37
    /**
38
     * 锁key
39
     * @var string
40
     */
41
    protected $sessKey = 'PHPSESSID';
42
43
    /**
44
     * 锁超时时间
45
     * @var integer
46
     */
47
    protected $lockTimeout = 3;
48
49
    /**
50
     * 是否启用锁机制
51
     * @var bool
52
     */
53
    protected $lock = false;
54
55
    public function __construct(array $config = [])
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
56
    {
57
        $this->config = $config;
58
    }
59
60
    public static function __make(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...
61
    {
62
        return new static($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

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