Completed
Push — 6.0 ( d6c8d7...cb8a0b )
by liu
05:58
created

Driver   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 322
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 7
Bugs 0 Features 0
Metric Value
eloc 84
c 7
b 0
f 0
dl 0
loc 322
ccs 0
cts 95
cp 0
rs 9.76
wmc 33

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getTagKey() 0 3 1
A setMultiple() 0 11 3
A __call() 0 3 1
A getReadTimes() 0 3 1
A tag() 0 13 2
A serialize() 0 11 1
A getCacheKey() 0 3 1
A handler() 0 3 1
A deleteMultiple() 0 11 3
A unserialize() 0 11 1
A push() 0 17 3
A getMultiple() 0 9 2
A remember() 0 33 6
A getTagItems() 0 4 1
A pull() 0 7 2
A getExpireTime() 0 11 3
A getWriteTimes() 0 3 1
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;
14
15
use DateInterval;
16
use DateTime;
17
use DateTimeInterface;
18
use Opis\Closure\SerializableClosure;
19
use Psr\SimpleCache\CacheInterface;
20
use think\cache\exception\InvalidArgumentException;
0 ignored issues
show
Bug introduced by
The type think\cache\exception\InvalidArgumentException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use think\Container;
22
23
/**
24
 * 缓存基础类
25
 */
26
abstract class Driver implements CacheInterface
27
{
28
    /**
29
     * 驱动句柄
30
     * @var object
31
     */
32
    protected $handler = null;
33
34
    /**
35
     * 缓存读取次数
36
     * @var integer
37
     */
38
    protected $readTimes = 0;
39
40
    /**
41
     * 缓存写入次数
42
     * @var integer
43
     */
44
    protected $writeTimes = 0;
45
46
    /**
47
     * 缓存参数
48
     * @var array
49
     */
50
    protected $options = [];
51
52
    /**
53
     * 缓存标签
54
     * @var array
55
     */
56
    protected $tag = [];
57
58
    /**
59
     * 获取有效期
60
     * @access protected
61
     * @param  integer|DateTimeInterface|DateInterval $expire 有效期
62
     * @return int
63
     */
64
    protected function getExpireTime($expire): int
65
    {
66
        if ($expire instanceof DateTimeInterface) {
67
            $expire = $expire->getTimestamp() - time();
68
        } elseif ($expire instanceof DateInterval) {
69
            $expire = DateTime::createFromFormat('U', (string) time())
70
                ->add($expire)
71
                ->format('U') - time();
72
        }
73
74
        return (int) $expire;
75
    }
76
77
    /**
78
     * 获取实际的缓存标识
79
     * @access public
80
     * @param  string $name 缓存名
81
     * @return string
82
     */
83
    public function getCacheKey(string $name): string
84
    {
85
        return $this->options['prefix'] . $name;
86
    }
87
88
    /**
89
     * 读取缓存并删除
90
     * @access public
91
     * @param  string $name 缓存变量名
92
     * @return mixed
93
     */
94
    public function pull(string $name)
95
    {
96
        $result = $this->get($name, false);
97
98
        if ($result) {
99
            $this->delete($name);
100
            return $result;
101
        }
102
    }
103
104
    /**
105
     * 追加(数组)缓存
106
     * @access public
107
     * @param  string $name 缓存变量名
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
108
     * @param  mixed  $value  存储数据
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 2 found
Loading history...
109
     * @return void
110
     */
111
    public function push(string $name, $value): void
112
    {
113
        $item = $this->get($name, []);
114
115
        if (!is_array($item)) {
116
            throw new InvalidArgumentException('only array cache can be push');
117
        }
118
119
        $item[] = $value;
120
121
        if (count($item) > 1000) {
122
            array_shift($item);
123
        }
124
125
        $item = array_unique($item);
126
127
        $this->set($name, $item);
128
    }
129
130
    /**
131
     * 如果不存在则写入缓存
132
     * @access public
133
     * @param  string $name 缓存变量名
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
134
     * @param  mixed  $value  存储数据
135
     * @param  int    $expire  有效时间 0为永久
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 2 found
Loading history...
136
     * @return mixed
137
     */
138
    public function remember(string $name, $value, $expire = null)
139
    {
140
        if ($this->has($name)) {
141
            return $this->get($name);
142
        }
143
144
        $time = time();
145
146
        while ($time + 5 > time() && $this->has($name . '_lock')) {
147
            // 存在锁定则等待
148
            usleep(200000);
149
        }
150
151
        try {
152
            // 锁定
153
            $this->set($name . '_lock', true);
154
155
            if ($value instanceof \Closure) {
156
                // 获取缓存数据
157
                $value = Container::getInstance()->invokeFunction($value);
158
            }
159
160
            // 缓存数据
161
            $this->set($name, $value, $expire);
162
163
            // 解锁
164
            $this->delete($name . '_lock');
165
        } catch (\Exception | \throwable $e) {
166
            $this->delete($name . '_lock');
167
            throw $e;
168
        }
169
170
        return $value;
171
    }
172
173
    /**
174
     * 缓存标签
175
     * @access public
176
     * @param  string|array $name 标签名
177
     * @return $this
178
     */
179
    public function tag($name)
180
    {
181
        $name = (array) $name;
182
        $key  = implode('-', $name);
183
184
        if (!isset($this->tag[$key])) {
185
            $name = array_map(function ($val) {
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...
186
                return $this->getTagKey($val);
187
            }, $name);
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...
188
            $this->tag[$key] = new TagSet($name, $this);
189
        }
190
191
        return $this->tag[$key];
192
    }
193
194
    /**
195
     * 获取标签包含的缓存标识
196
     * @access public
197
     * @param  string $tag 标签标识
198
     * @return array
199
     */
200
    public function getTagItems(string $tag): array
201
    {
202
        $name = $this->getTagKey($tag);
203
        return $this->get($name, []);
204
    }
205
206
    /**
207
     * 获取实际标签名
208
     * @access public
209
     * @param  string $tag 标签名
210
     * @return string
211
     */
212
    public function getTagKey(string $tag): string
213
    {
214
        return $this->options['tag_prefix'] . md5($tag);
215
    }
216
217
    /**
218
     * 序列化数据
219
     * @access protected
220
     * @param  mixed $data 缓存数据
221
     * @return string
222
     */
223
    protected function serialize($data): string
224
    {
225
        $serialize = $this->options['serialize'][0] ?? function ($data) {
226
            SerializableClosure::enterContext();
227
            SerializableClosure::wrapClosures($data);
228
            $data = \serialize($data);
229
            SerializableClosure::exitContext();
230
            return $data;
231
        };
232
233
        return $serialize($data);
234
    }
235
236
    /**
237
     * 反序列化数据
238
     * @access protected
239
     * @param  string $data 缓存数据
240
     * @return mixed
241
     */
242
    protected function unserialize(string $data)
243
    {
244
        $unserialize = $this->options['serialize'][1] ?? function ($data) {
245
            SerializableClosure::enterContext();
246
            $data = \unserialize($data);
247
            SerializableClosure::unwrapClosures($data);
248
            SerializableClosure::exitContext();
249
            return $data;
250
        };
251
252
        return $unserialize($data);
253
    }
254
255
    /**
256
     * 返回句柄对象,可执行其它高级方法
257
     *
258
     * @access public
259
     * @return object
260
     */
261
    public function handler()
262
    {
263
        return $this->handler;
264
    }
265
266
    /**
267
     * 返回缓存读取次数
268
     * @access public
269
     * @return int
270
     */
271
    public function getReadTimes(): int
272
    {
273
        return $this->readTimes;
274
    }
275
276
    /**
277
     * 返回缓存写入次数
278
     * @access public
279
     * @return int
280
     */
281
    public function getWriteTimes(): int
282
    {
283
        return $this->writeTimes;
284
    }
285
286
    /**
287
     * 读取缓存
288
     * @access public
289
     * @param  iterable $keys 缓存变量名
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
290
     * @param  mixed    $default 默认值
291
     * @return iterable
292
     * @throws InvalidArgumentException
293
     */
294
    public function getMultiple($keys, $default = null): iterable
295
    {
296
        $result = [];
297
298
        foreach ($keys as $key) {
299
            $result[$key] = $this->get($key, $default);
300
        }
301
302
        return $result;
303
    }
304
305
    /**
306
     * 写入缓存
307
     * @access public
308
     * @param  iterable               $values 缓存数据
309
     * @param  null|int|\DateInterval $ttl    有效时间 0为永久
310
     * @return bool
311
     */
312
    public function setMultiple($values, $ttl = null): bool
313
    {
314
        foreach ($values as $key => $val) {
315
            $result = $this->set($key, $val, $ttl);
316
317
            if (false === $result) {
318
                return false;
319
            }
320
        }
321
322
        return true;
323
    }
324
325
    /**
326
     * 删除缓存
327
     * @access public
328
     * @param iterable $keys 缓存变量名
329
     * @return bool
330
     * @throws InvalidArgumentException
331
     */
332
    public function deleteMultiple($keys): bool
333
    {
334
        foreach ($keys as $key) {
335
            $result = $this->delete($key);
336
337
            if (false === $result) {
338
                return false;
339
            }
340
        }
341
342
        return true;
343
    }
344
345
    public function __call($method, $args)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __call()
Loading history...
346
    {
347
        return call_user_func_array([$this->handler, $method], $args);
348
    }
349
}
350