Completed
Push — 6.0 ( b72c2e...58dbb9 )
by yun
05:58
created

File::findFiles()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 7
c 0
b 0
f 0
nc 4
nop 2
dl 0
loc 11
ccs 7
cts 7
cp 1
crap 5
rs 9.6111
1
<?php
2
// +----------------------------------------------------------------------
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\session\driver;
14
15
use Closure;
16
use Exception;
17
use FilesystemIterator;
18
use Generator;
19
use SplFileInfo;
20
use think\App;
21
use think\contract\SessionHandlerInterface;
22
23
/**
24
 * Session 文件驱动
25
 */
26
class File implements SessionHandlerInterface
27
{
28
    protected $config = [
29
        'path'           => '',
30
        'expire'         => 1440,
31
        'prefix'         => '',
32
        'data_compress'  => false,
33
        'gc_probability' => 1,
34
        'gc_divisor'     => 100,
35
    ];
36
37 1
    public function __construct(App $app, array $config = [])
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
38
    {
39 1
        $this->config = array_merge($this->config, $config);
40
41 1
        if (empty($this->config['path'])) {
42
            $this->config['path'] = $app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . 'session' . DIRECTORY_SEPARATOR;
43 1
        } elseif (substr($this->config['path'], -1) != DIRECTORY_SEPARATOR) {
44 1
            $this->config['path'] .= DIRECTORY_SEPARATOR;
45
        }
46
47 1
        $this->init();
48 1
    }
49
50
    /**
51
     * 打开Session
52
     * @access protected
53
     * @throws Exception
54
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
55 1
    protected function init(): void
56
    {
57
        try {
58 1
            !is_dir($this->config['path']) && mkdir($this->config['path'], 0755, true);
59
        } catch (\Exception $e) {
60
            // 写入失败
61
        }
62
63
        // 垃圾回收
64 1
        if (random_int(1, $this->config['gc_divisor']) <= $this->config['gc_probability']) {
65 1
            $this->gc();
66
        }
67 1
    }
68
69
    /**
70
     * Session 垃圾回收
71
     * @access public
72
     * @return void
73
     */
74 1
    public function gc(): void
75
    {
76 1
        $lifetime = $this->config['expire'];
77 1
        $now      = time();
78
79
        $files = $this->findFiles($this->config['path'], function (SplFileInfo $item) use ($lifetime, $now) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
80 1
            return $now > $item->getMTime() + $lifetime;
81 1
        });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
82
83 1
        foreach ($files as $file) {
84 1
            $this->unlink($file->getPathname());
85
        }
86 1
    }
87
88
    /**
89
     * 查找文件
90
     * @param string  $root
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
91
     * @param Closure $filter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
92
     * @return Generator
93
     */
94 1
    protected function findFiles(string $root, Closure $filter)
95
    {
96 1
        $items = new FilesystemIterator($root);
97
98
        /** @var SplFileInfo $item */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
99 1
        foreach ($items as $item) {
100 1
            if ($item->isDir() && !$item->isLink()) {
101 1
                yield from $this->findFiles($item->getPathname(), $filter);
102
            } else {
103 1
                if ($filter($item)) {
104 1
                    yield $item;
105
                }
106
            }
107
        }
108 1
    }
109
110
    /**
111
     * 取得变量的存储文件名
112
     * @access protected
113
     * @param string $name 缓存变量名
114
     * @param bool   $auto 是否自动创建目录
115
     * @return string
116
     */
117 1
    protected function getFileName(string $name, bool $auto = false): string
118
    {
119 1
        if ($this->config['prefix']) {
120
            // 使用子目录
121
            $name = $this->config['prefix'] . DIRECTORY_SEPARATOR . 'sess_' . $name;
122
        } else {
123 1
            $name = 'sess_' . $name;
124
        }
125
126 1
        $filename = $this->config['path'] . $name;
127 1
        $dir      = dirname($filename);
128
129 1
        if ($auto && !is_dir($dir)) {
130
            try {
131
                mkdir($dir, 0755, true);
132
            } catch (\Exception $e) {
133
                // 创建失败
134
            }
135
        }
136
137 1
        return $filename;
138
    }
139
140
    /**
141
     * 读取Session
142
     * @access public
143
     * @param string $sessID
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
144
     * @return string
145
     */
146 1
    public function read(string $sessID): string
147
    {
148 1
        $filename = $this->getFileName($sessID);
149
150 1
        if (is_file($filename) && filemtime($filename) >= time() - $this->config['expire']) {
151 1
            $content = $this->readFile($filename);
152
153 1
            if ($this->config['data_compress'] && function_exists('gzcompress')) {
154
                //启用数据压缩
155
                $content = gzuncompress($content);
156
            }
157
158 1
            return $content;
159
        }
160
161
        return '';
162
    }
163
164
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $path should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $content should have a doc-comment as per coding-style.
Loading history...
165
     * 写文件(加锁)
166
     * @param $path
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
167
     * @param $content
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
168
     * @return bool|int
169
     */
170
    protected function writeFile($path, $content)
171
    {
172
        return (bool) file_put_contents($path, $content, LOCK_EX);
173
    }
174
175
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $path should have a doc-comment as per coding-style.
Loading history...
176
     * 读取文件内容(加锁)
177
     * @param $path
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
178
     * @return string
179
     */
180 1
    protected function readFile($path): string
181
    {
182 1
        $contents = '';
183
184 1
        $handle = fopen($path, 'rb');
185
186 1
        if ($handle) {
0 ignored issues
show
introduced by
$handle is of type false|resource, thus it always evaluated to false.
Loading history...
187
            try {
188 1
                if (flock($handle, LOCK_SH)) {
189 1
                    clearstatcache(true, $path);
190
191 1
                    $contents = fread($handle, filesize($path) ?: 1);
192
193 1
                    flock($handle, LOCK_UN);
194
                }
195 1
            } finally {
196 1
                fclose($handle);
197
            }
198
        }
199
200 1
        return $contents;
201
    }
202
203
    /**
204
     * 写入Session
205
     * @access public
206
     * @param string $sessID
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
207
     * @param string $sessData
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
208
     * @return bool
209
     */
210 1
    public function write(string $sessID, string $sessData): bool
211
    {
212 1
        $filename = $this->getFileName($sessID, true);
213 1
        $data     = $sessData;
214
215 1
        if ($this->config['data_compress'] && function_exists('gzcompress')) {
216
            //数据压缩
217
            $data = gzcompress($data, 3);
218
        }
219
220 1
        return $this->writeFile($filename, $data);
221
    }
222
223
    /**
224
     * 删除Session
225
     * @access public
226
     * @param string $sessID
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
227
     * @return bool
228
     */
229 1
    public function delete(string $sessID): bool
230
    {
231
        try {
232 1
            return $this->unlink($this->getFileName($sessID));
233
        } catch (\Exception $e) {
234
            return false;
235
        }
236
    }
237
238
    /**
239
     * 判断文件是否存在后,删除
240
     * @access private
241
     * @param string $file
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
242
     * @return bool
243
     */
244 1
    private function unlink(string $file): bool
0 ignored issues
show
Coding Style introduced by
Private method name "File::unlink" must be prefixed with an underscore
Loading history...
245
    {
246 1
        return is_file($file) && unlink($file);
247
    }
248
249
}
250