Completed
Push — 6.0 ( 899fd9...6d6bd6 )
by liu
05:20
created

File::has()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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\cache\driver;
14
15
use FilesystemIterator;
16
use think\App;
17
use think\cache\Driver;
18
19
/**
20
 * 文件缓存类
21
 */
22
class File extends Driver
23
{
24
    /**
25
     * 配置参数
26
     * @var array
27
     */
28
    protected $options = [
29
        'expire'        => 0,
30
        'cache_subdir'  => true,
31
        'prefix'        => '',
32
        'path'          => '',
33
        'hash_type'     => 'md5',
34
        'data_compress' => false,
35
        'tag_prefix'    => 'tag:',
36
        'serialize'     => [],
37
    ];
38
39
    /**
40
     * 架构函数
41
     * @param App   $app
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
42
     * @param array $options 参数
43
     */
44 2
    public function __construct(App $app, array $options = [])
45
    {
46 2
        if (!empty($options)) {
47 2
            $this->options = array_merge($this->options, $options);
48
        }
49
50 2
        if (empty($this->options['path'])) {
51 1
            $this->options['path'] = $app->getRuntimePath() . 'cache';
52
        }
53
54 2
        if (substr($this->options['path'], -1) != DIRECTORY_SEPARATOR) {
55 2
            $this->options['path'] .= DIRECTORY_SEPARATOR;
56
        }
57 2
    }
58
59
    /**
60
     * 取得变量的存储文件名
61
     * @access public
62
     * @param string $name 缓存变量名
63
     * @return string
64
     */
65 1
    public function getCacheKey(string $name): string
66
    {
67 1
        $name = hash($this->options['hash_type'], $name);
68
69 1
        if ($this->options['cache_subdir']) {
70
            // 使用子目录
71 1
            $name = substr($name, 0, 2) . DIRECTORY_SEPARATOR . substr($name, 2);
72
        }
73
74 1
        if ($this->options['prefix']) {
75
            $name = $this->options['prefix'] . DIRECTORY_SEPARATOR . $name;
76
        }
77
78 1
        return $this->options['path'] . $name . '.php';
79
    }
80
81
    /**
82
     * 获取缓存数据
83
     * @param string $name 缓存标识名
84
     * @return array|null
85
     */
86 1
    protected function getRaw(string $name)
87
    {
88 1
        $filename = $this->getCacheKey($name);
89
90 1
        if (!is_file($filename)) {
91 1
            return;
92
        }
93
94 1
        $content = @file_get_contents($filename);
95
96 1
        if (false !== $content) {
97 1
            $expire = (int) substr($content, 8, 12);
98 1
            if (0 != $expire && time() > filemtime($filename) + $expire) {
99
                //缓存过期删除缓存文件
100
                $this->unlink($filename);
101
                return;
102
            }
103
104 1
            $content = substr($content, 32);
105
106 1
            if ($this->options['data_compress'] && function_exists('gzcompress')) {
107
                //启用数据压缩
108
                $content = gzuncompress($content);
109
            }
110
111 1
            return ['content' => $content, 'expire' => $expire];
112
        }
113
    }
114
115
    /**
116
     * 判断缓存是否存在
117
     * @access public
118
     * @param string $name 缓存变量名
119
     * @return bool
120
     */
121 1
    public function has($name): bool
122
    {
123 1
        return $this->getRaw($name) !== null;
124
    }
125
126
    /**
127
     * 读取缓存
128
     * @access public
129
     * @param string $name    缓存变量名
130
     * @param mixed  $default 默认值
131
     * @return mixed
132
     */
133 1
    public function get($name, $default = null)
134
    {
135 1
        $this->readTimes++;
136
137 1
        $raw = $this->getRaw($name);
138
139 1
        return is_null($raw) ? $default : $this->unserialize($raw['content']);
140
    }
141
142
    /**
143
     * 写入缓存
144
     * @access public
145
     * @param string        $name   缓存变量名
146
     * @param mixed         $value  存储数据
147
     * @param int|\DateTime $expire 有效时间 0为永久
148
     * @return bool
149
     */
150 1
    public function set($name, $value, $expire = null): bool
151
    {
152 1
        $this->writeTimes++;
153
154 1
        if (is_null($expire)) {
155 1
            $expire = $this->options['expire'];
156
        }
157
158 1
        $expire   = $this->getExpireTime($expire);
159 1
        $filename = $this->getCacheKey($name);
160
161 1
        $dir = dirname($filename);
162
163 1
        if (!is_dir($dir)) {
164
            try {
165 1
                mkdir($dir, 0755, true);
166
            } catch (\Exception $e) {
167
                // 创建失败
168
            }
169
        }
170
171 1
        $data = $this->serialize($value);
172
173 1
        if ($this->options['data_compress'] && function_exists('gzcompress')) {
174
            //数据压缩
175
            $data = gzcompress($data, 3);
176
        }
177
178 1
        $data   = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
179 1
        $result = file_put_contents($filename, $data);
180
181 1
        if ($result) {
182 1
            clearstatcache();
183 1
            return true;
184
        }
185
186
        return false;
187
    }
188
189
    /**
190
     * 自增缓存(针对数值缓存)
191
     * @access public
192
     * @param string $name 缓存变量名
193
     * @param int    $step 步长
194
     * @return false|int
195
     */
196 1
    public function inc(string $name, int $step = 1)
197
    {
198 1
        if ($raw = $this->getRaw($name)) {
199 1
            $value  = $this->unserialize($raw['content']) + $step;
200 1
            $expire = $raw['expire'];
201
        } else {
202
            $value  = $step;
203
            $expire = 0;
204
        }
205
206 1
        return $this->set($name, $value, $expire) ? $value : false;
207
    }
208
209
    /**
210
     * 自减缓存(针对数值缓存)
211
     * @access public
212
     * @param string $name 缓存变量名
213
     * @param int    $step 步长
214
     * @return false|int
215
     */
216 1
    public function dec(string $name, int $step = 1)
217
    {
218 1
        return $this->inc($name, -$step);
219
    }
220
221
    /**
222
     * 删除缓存
223
     * @access public
224
     * @param string $name 缓存变量名
225
     * @return bool
226
     */
227 1
    public function delete($name): bool
228
    {
229 1
        $this->writeTimes++;
230
231 1
        return $this->unlink($this->getCacheKey($name));
232
    }
233
234
    /**
235
     * 清除缓存
236
     * @access public
237
     * @return bool
238
     */
239 1
    public function clear(): bool
240
    {
241 1
        $this->writeTimes++;
242
243 1
        $dirname = $this->options['path'] . $this->options['prefix'];
244
245 1
        $this->rmdir($dirname);
246
247 1
        return true;
248
    }
249
250
    /**
251
     * 删除缓存标签
252
     * @access public
253
     * @param array $keys 缓存标识列表
254
     * @return void
255
     */
256 1
    public function clearTag(array $keys): void
257
    {
258 1
        foreach ($keys as $key) {
259 1
            $this->unlink($key);
260
        }
261 1
    }
262
263
    /**
264
     * 判断文件是否存在后,删除
265
     * @access private
266
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
267
     * @return bool
268
     */
269 1
    private function unlink(string $path): bool
0 ignored issues
show
Coding Style introduced by
Private method name "File::unlink" must be prefixed with an underscore
Loading history...
270
    {
271
        try {
272 1
            return is_file($path) && unlink($path);
273
        } catch (\Exception $e) {
274
            return false;
275
        }
276
    }
277
278
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $dirname should have a doc-comment as per coding-style.
Loading history...
279
     * 删除文件夹
280
     * @param $dirname
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
281
     * @return bool
282
     */
283 1
    private function rmdir($dirname)
0 ignored issues
show
Coding Style introduced by
Private method name "File::rmdir" must be prefixed with an underscore
Loading history...
284
    {
285 1
        if (!is_dir($dirname)) {
286
            return false;
287
        }
288
289 1
        $items = new FilesystemIterator($dirname);
290
291 1
        foreach ($items as $item) {
292 1
            if ($item->isDir() && !$item->isLink()) {
293 1
                $this->rmdir($item->getPathname());
294
            } else {
295 1
                $this->unlink($item->getPathname());
296
            }
297
        }
298
299 1
        @rmdir($dirname);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rmdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

299
        /** @scrutinizer ignore-unhandled */ @rmdir($dirname);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
300
301 1
        return true;
302
    }
303
304
}
305