Completed
Branch 6.0 (d30585)
by yun
04:17
created

Driver::push()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.072

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 2
dl 0
loc 18
rs 9.6666
c 0
b 0
f 0
ccs 8
cts 10
cp 0.8
crap 3.072
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 Closure;
16
use DateInterval;
17
use DateTime;
18
use DateTimeInterface;
19
use Exception;
20
use Psr\SimpleCache\CacheInterface;
21
use think\Container;
22
use think\contract\CacheHandlerInterface;
23
use think\exception\InvalidArgumentException;
24
use throwable;
25
26
/**
27
 * 缓存基础类
28
 */
29
abstract class Driver implements CacheInterface, 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|DateTimeInterface|DateInterval $expire 有效期
65
     * @return int
66
     */
67 3
    protected function getExpireTime($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 (int) $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
     * @return mixed
96
     */
97
    public function pull(string $name)
98
    {
99
        $result = $this->get($name, false);
100
101
        if ($result) {
102
            $this->delete($name);
103
            return $result;
104
        }
105
    }
106
107
    /**
108
     * 追加(数组)缓存
109
     * @access public
110
     * @param string $name  缓存变量名
111
     * @param mixed  $value 存储数据
112
     * @return void
113
     */
114 3
    public function push(string $name, $value): void
115
    {
116 3
        $item = $this->get($name, []);
117
118 3
        if (!is_array($item)) {
119
            throw new InvalidArgumentException('only array cache can be push');
120
        }
121
122 3
        $item[] = $value;
123
124 3
        if (count($item) > 1000) {
125
            array_shift($item);
126
        }
127
128 3
        $item = array_unique($item);
129
130 3
        $this->set($name, $item);
131 3
    }
132
133
    /**
134
     * 如果不存在则写入缓存
135
     * @access public
136
     * @param string $name   缓存变量名
137
     * @param mixed  $value  存储数据
138
     * @param int    $expire 有效时间 0为永久
139
     * @return mixed
140
     */
141
    public function remember(string $name, $value, $expire = null)
142
    {
143
        if ($this->has($name)) {
144
            return $this->get($name);
145
        }
146
147
        $time = time();
148
149
        while ($time + 5 > time() && $this->has($name . '_lock')) {
150
            // 存在锁定则等待
151
            usleep(200000);
152
        }
153
154
        try {
155
            // 锁定
156
            $this->set($name . '_lock', true);
157
158
            if ($value instanceof Closure) {
159
                // 获取缓存数据
160
                $value = Container::getInstance()->invokeFunction($value);
161
            }
162
163
            // 缓存数据
164
            $this->set($name, $value, $expire);
165
166
            // 解锁
167
            $this->delete($name . '_lock');
168
        } catch (Exception | throwable $e) {
169
            $this->delete($name . '_lock');
170
            throw $e;
171
        }
172
173
        return $value;
174
    }
175
176
    /**
177
     * 缓存标签
178
     * @access public
179
     * @param string|array $name 标签名
180
     * @return TagSet
181
     */
182 3
    public function tag($name): TagSet
183
    {
184 3
        $name = (array) $name;
185 3
        $key  = implode('-', $name);
186
187 3
        if (!isset($this->tag[$key])) {
188
            $name = array_map(function ($val) {
189 3
                return $this->getTagKey($val);
190 3
            }, $name);
191 3
            $this->tag[$key] = new TagSet($name, $this);
192
        }
193
194 3
        return $this->tag[$key];
195
    }
196
197
    /**
198
     * 获取标签包含的缓存标识
199
     * @access public
200
     * @param string $tag 标签标识
201
     * @return array
202
     */
203 3
    public function getTagItems(string $tag): array
204
    {
205 3
        return $this->get($tag, []);
206
    }
207
208
    /**
209
     * 获取实际标签名
210
     * @access public
211
     * @param string $tag 标签名
212
     * @return string
213
     */
214 3
    public function getTagKey(string $tag): string
215
    {
216 3
        return $this->options['tag_prefix'] . md5($tag);
217
    }
218
219
    /**
220
     * 序列化数据
221
     * @access protected
222
     * @param mixed $data 缓存数据
223
     * @return string
224
     */
225 3
    protected function serialize($data): string
226
    {
227 3
        if (is_numeric($data)) {
228 3
            return (string) $data;
229
        }
230
231 3
        $serialize = $this->options['serialize'][0] ?? "serialize";
232
233 3
        return $serialize($data);
234
    }
235
236
    /**
237
     * 反序列化数据
238
     * @access protected
239
     * @param string $data 缓存数据
240
     * @return mixed
241
     */
242 3
    protected function unserialize(string $data)
243
    {
244 3
        if (is_numeric($data)) {
245 3
            return $data;
246
        }
247
248 3
        $unserialize = $this->options['serialize'][1] ?? "unserialize";
249
250 3
        return $unserialize($data);
251
    }
252
253
    /**
254
     * 返回句柄对象,可执行其它高级方法
255
     *
256
     * @access public
257
     * @return object
258
     */
259
    public function handler()
260
    {
261
        return $this->handler;
262
    }
263
264
    /**
265
     * 返回缓存读取次数
266
     * @access public
267
     * @return int
268
     */
269
    public function getReadTimes(): int
270
    {
271
        return $this->readTimes;
272
    }
273
274
    /**
275
     * 返回缓存写入次数
276
     * @access public
277
     * @return int
278
     */
279
    public function getWriteTimes(): int
280
    {
281
        return $this->writeTimes;
282
    }
283
284
    /**
285
     * 读取缓存
286
     * @access public
287
     * @param iterable $keys    缓存变量名
288
     * @param mixed    $default 默认值
289
     * @return iterable
290
     * @throws InvalidArgumentException
291
     */
292 3
    public function getMultiple($keys, $default = null): iterable
293
    {
294 3
        $result = [];
295
296 3
        foreach ($keys as $key) {
297 3
            $result[$key] = $this->get($key, $default);
298
        }
299
300 3
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $result; (array) is incompatible with the return type declared by the interface Psr\SimpleCache\CacheInterface::getMultiple of type Psr\SimpleCache\iterable.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
301
    }
302
303
    /**
304
     * 写入缓存
305
     * @access public
306
     * @param iterable               $values 缓存数据
307
     * @param null|int|\DateInterval $ttl    有效时间 0为永久
308
     * @return bool
309
     */
310 3
    public function setMultiple($values, $ttl = null): bool
311
    {
312 3
        foreach ($values as $key => $val) {
313 3
            $result = $this->set($key, $val, $ttl);
314
315 3
            if (false === $result) {
316 1
                return false;
317
            }
318
        }
319
320 3
        return true;
321
    }
322
323
    /**
324
     * 删除缓存
325
     * @access public
326
     * @param iterable $keys 缓存变量名
327
     * @return bool
328
     * @throws InvalidArgumentException
329
     */
330 3
    public function deleteMultiple($keys): bool
331
    {
332 3
        foreach ($keys as $key) {
333 3
            $result = $this->delete($key);
334
335 3
            if (false === $result) {
336 1
                return false;
337
            }
338
        }
339
340 3
        return true;
341
    }
342
343
    public function __call($method, $args)
344
    {
345
        return call_user_func_array([$this->handler, $method], $args);
346
    }
347
}
348