Passed
Push — 8.0 ( 7db137...8b2282 )
by liu
02:57
created

Driver   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 355
Duplicated Lines 0 %

Test Coverage

Coverage 69.39%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 88
dl 0
loc 355
ccs 68
cts 98
cp 0.6939
rs 8.96
c 4
b 0
f 0
wmc 43

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getCacheKey() 0 3 1
A getExpireTime() 0 11 3
A append() 0 3 1
A setMultiple() 0 11 3
A getTagKey() 0 3 1
A __call() 0 3 1
A getReadTimes() 0 3 1
A tag() 0 10 2
A serialize() 0 9 2
A handler() 0 3 1
A deleteMultiple() 0 11 3
A unserialize() 0 15 4
A push() 0 17 3
A getMultiple() 0 9 2
B remember() 0 35 7
A getTagItems() 0 4 1
A pull() 0 8 2
A getDefaultValue() 0 6 4
A getWriteTimes() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Driver often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Driver, and based on these observations, apply Extract Interface, too.

1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2023 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 Closure;
16
use DateInterval;
17
use DateTime;
18
use DateTimeInterface;
19
use Exception;
20
use think\Container;
21
use think\contract\CacheHandlerInterface;
22
use think\exception\InvalidArgumentException;
23
use think\exception\InvalidCacheException;
24
use Throwable;
25
26
/**
27
 * 缓存基础类
28
 */
29
abstract class Driver implements CacheHandlerInterface
30
{
31
    /**
32
     * 驱动句柄
33
     * @var object
34
     */
35
    protected $handler = null;
36
37
    /**
38
     * 缓存读取次数
39
     * @var integer
40
     */
41
    protected $readTimes = 0;
42
43
    /**
44
     * 缓存写入次数
45
     * @var integer
46
     */
47
    protected $writeTimes = 0;
48
49
    /**
50
     * 缓存参数
51
     * @var array
52
     */
53
    protected $options = [];
54
55
    /**
56
     * 缓存标签
57
     * @var array
58
     */
59
    protected $tag = [];
60
61
    /**
62
     * 获取有效期
63
     * @access protected
64
     * @param integer|DateInterval|DateTimeInterface $expire 有效期
65
     * @return int
66
     */
67 3
    protected function getExpireTime(int | DateInterval | DateTimeInterface $expire): int
68
    {
69 3
        if ($expire instanceof DateTimeInterface) {
70
            $expire = $expire->getTimestamp() - time();
71 3
        } elseif ($expire instanceof DateInterval) {
72
            $expire = DateTime::createFromFormat('U', (string) time())
73
                ->add($expire)
74
                ->format('U') - time();
75
        }
76
77 3
        return $expire;
78
    }
79
80
    /**
81
     * 获取实际的缓存标识
82
     * @access public
83
     * @param string $name 缓存名
84
     * @return string
85
     */
86
    public function getCacheKey(string $name): string
87
    {
88
        return $this->options['prefix'] . $name;
89
    }
90
91
    /**
92
     * 读取缓存并删除
93
     * @access public
94
     * @param string $name 缓存变量名
95
     * @param mixed  $default 默认值
96
     * @return mixed
97
     */
98
    public function pull($name, $default = null)
99
    {
100
        if ($this->has($name)) {
101
            $result = $this->get($name, $default);
102
            $this->delete($name);
103
            return $result;
104
        }
105
        return $this->getDefaultValue($name, $default);
106
    }
107
108
    /**
109
     * 追加(数组)缓存
110
     * @access public
111
     * @param string $name  缓存变量名
112
     * @param mixed  $value 存储数据
113
     * @return void
114
     */
115 3
    public function push($name, $value): void
116
    {
117 3
        $item = $this->get($name, []);
118
119 3
        if (!is_array($item)) {
120
            throw new InvalidArgumentException('only array cache can be push');
121
        }
122
123 3
        $item[] = $value;
124
125 3
        if (count($item) > 1000) {
126
            array_shift($item);
127
        }
128
129 3
        $item = array_unique($item);
130
131 3
        $this->set($name, $item);
132
    }
133
134
    /**
135
     * 追加TagSet数据
136
     * @access public
137
     * @param string $name  缓存变量名
138
     * @param mixed  $value 存储数据
139
     * @return void
140
     */
141 3
    public function append($name, $value): void
142
    {
143 3
        $this->push($name, $value);
144
    }
145
146
    /**
147
     * 如果不存在则写入缓存
148
     * @access public
149
     * @param string                             $name   缓存变量名
150
     * @param mixed                              $value  存储数据
151
     * @param int|DateInterval|DateTimeInterface $expire 有效时间 0为永久
152
     * @return mixed
153
     */
154 3
    public function remember($name, $value, $expire = null)
155
    {
156 3
        if ($this->has($name)) {
157
            if (($hit = $this->get($name)) !== null) {
158
                return $hit;
159
            }
160
        }
161
162 3
        $time = time();
163
164 3
        while ($time + 5 > time() && $this->has($name . '_lock')) {
165
            // 存在锁定则等待
166
            usleep(200000);
167
        }
168
169
        try {
170
            // 锁定
171 3
            $this->set($name . '_lock', true);
172
173 3
            if ($value instanceof Closure) {
174
                // 获取缓存数据
175
                $value = Container::getInstance()->invokeFunction($value);
176
            }
177
178
            // 缓存数据
179 3
            $this->set($name, $value, $expire);
0 ignored issues
show
Bug introduced by
It seems like $expire can also be of type DateTimeInterface; however, parameter $ttl of Psr\SimpleCache\CacheInterface::set() does only seem to accept DateInterval|integer|null, 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

179
            $this->set($name, $value, /** @scrutinizer ignore-type */ $expire);
Loading history...
180
181
            // 解锁
182 3
            $this->delete($name . '_lock');
183
        } catch (Exception | Throwable $e) {
184
            $this->delete($name . '_lock');
185
            throw $e;
186
        }
187
188 3
        return $value;
189
    }
190
191
    /**
192
     * 缓存标签
193
     * @access public
194
     * @param string|array $name 标签名
195
     * @return TagSet
196
     */
197 3
    public function tag($name)
198
    {
199 3
        $name = (array) $name;
200 3
        $key  = implode('-', $name);
201
202 3
        if (!isset($this->tag[$key])) {
203 3
            $this->tag[$key] = new TagSet($name, $this);
204
        }
205
206 3
        return $this->tag[$key];
207
    }
208
209
    /**
210
     * 获取标签包含的缓存标识
211
     * @access public
212
     * @param string $tag 标签标识
213
     * @return array
214
     */
215 3
    public function getTagItems(string $tag): array
216
    {
217 3
        $name = $this->getTagKey($tag);
218 3
        return $this->get($name, []);
219
    }
220
221
    /**
222
     * 获取实际标签名
223
     * @access public
224
     * @param string $tag 标签名
225
     * @return string
226
     */
227 3
    public function getTagKey(string $tag): string
228
    {
229 3
        return $this->options['tag_prefix'] . md5($tag);
230
    }
231
232
    /**
233
     * 序列化数据
234
     * @access protected
235
     * @param mixed $data 缓存数据
236
     * @return string
237
     */
238 3
    protected function serialize($data): string
239
    {
240 3
        if (is_numeric($data)) {
241 3
            return (string) $data;
242
        }
243
244 3
        $serialize = $this->options['serialize'][0] ?? "serialize";
245
246 3
        return $serialize($data);
247
    }
248
249
    /**
250
     * 反序列化数据
251
     * @access protected
252
     * @param string $data 缓存数据
253
     * @return mixed
254
     */
255 3
    protected function unserialize(string $data)
256
    {
257 3
        if (is_numeric($data)) {
258 3
            return $data;
259
        }
260
        try {
261 3
            $unserialize = $this->options['serialize'][1] ?? "unserialize";
262 3
            $content     = $unserialize($data);
263 3
            if (is_null($content)) {
264 3
                throw new InvalidCacheException;
265
            } else {
266 3
                return $content;
267
            }
268 3
        } catch (Exception | Throwable $e) {
269 3
            throw new InvalidCacheException;
270
        }
271
    }
272
273
    /**
274
     * 获取默认值
275
     * @access protected
276
     * @param string $name 缓存标识
277
     * @param mixed $default 默认值
278
     * @param bool $fail 是否有异常
279
     * @return mixed
280
     */
281 3
    protected function getDefaultValue($name, $default, $fail = false)
282
    {
283 3
        if ($fail && $this->options['fail_delete']) {
284
            $this->delete($name);
285
        }
286 3
        return $default instanceof Closure ? $default() : $default;
287
    }
288
289
    /**
290
     * 返回句柄对象,可执行其它高级方法
291
     *
292
     * @access public
293
     * @return object
294
     */
295
    public function handler()
296
    {
297
        return $this->handler;
298
    }
299
300
    /**
301
     * 返回缓存读取次数
302
     * @return int
303
     * @deprecated
304
     * @access public
305
     */
306
    public function getReadTimes(): int
307
    {
308
        return $this->readTimes;
309
    }
310
311
    /**
312
     * 返回缓存写入次数
313
     * @return int
314
     * @deprecated
315
     * @access public
316
     */
317 3
    public function getWriteTimes(): int
318
    {
319 3
        return $this->writeTimes;
320
    }
321
322
    /**
323
     * 读取缓存
324
     * @access public
325
     * @param iterable $keys    缓存变量名
326
     * @param mixed    $default 默认值
327
     * @return iterable
328
     * @throws InvalidArgumentException
329
     */
330 3
    public function getMultiple($keys, $default = null): iterable
331
    {
332 3
        $result = [];
333
334 3
        foreach ($keys as $key) {
335 3
            $result[$key] = $this->get($key, $default);
336
        }
337
338 3
        return $result;
339
    }
340
341
    /**
342
     * 写入缓存
343
     * @access public
344
     * @param iterable                                 $values 缓存数据
345
     * @param null|int|\DateInterval|DateTimeInterface $ttl    有效时间 0为永久
346
     * @return bool
347
     */
348 3
    public function setMultiple($values, $ttl = null): bool
349
    {
350 3
        foreach ($values as $key => $val) {
351 3
            $result = $this->set($key, $val, $ttl);
0 ignored issues
show
Bug introduced by
It seems like $ttl can also be of type DateTimeInterface; however, parameter $ttl of Psr\SimpleCache\CacheInterface::set() does only seem to accept DateInterval|integer|null, 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

351
            $result = $this->set($key, $val, /** @scrutinizer ignore-type */ $ttl);
Loading history...
352
353 3
            if (false === $result) {
354
                return false;
355
            }
356
        }
357
358 3
        return true;
359
    }
360
361
    /**
362
     * 删除缓存
363
     * @access public
364
     * @param iterable $keys 缓存变量名
365
     * @return bool
366
     * @throws InvalidArgumentException
367
     */
368 3
    public function deleteMultiple($keys): bool
369
    {
370 3
        foreach ($keys as $key) {
371 3
            $result = $this->delete($key);
372
373 3
            if (false === $result) {
374
                return false;
375
            }
376
        }
377
378 3
        return true;
379
    }
380
381
    public function __call($method, $args)
382
    {
383
        return call_user_func_array([$this->handler, $method], $args);
384
    }
385
}
386