Passed
Push — 6.0 ( 1fbc9a...13b6d9 )
by liu
03:06
created

Session::setData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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