Passed
Pull Request — 8.0 (#3068)
by wj
02:12
created

Driver::serialize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
// +----------------------------------------------------------------------
4
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
5
// +----------------------------------------------------------------------
6
// | Copyright (c) 2006~2023 http://thinkphp.cn All rights reserved.
7
// +----------------------------------------------------------------------
8
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
9
// +----------------------------------------------------------------------
10
// | Author: liu21st <[email protected]>
11
// +----------------------------------------------------------------------
12
declare (strict_types=1);
13
14
namespace think\cache;
15
16
use Closure;
17
use DateInterval;
18
use DateTime;
19
use DateTimeInterface;
20
use Exception;
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 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 6
    protected function getExpireTime(int|DateInterval|DateTimeInterface $expire): int
68
    {
69 6
        if ($expire instanceof DateTimeInterface) {
70
            $expire = $expire->getTimestamp() - time();
71 6
        } elseif ($expire instanceof DateInterval) {
72
            $expire = DateTime::createFromFormat('U', (string) time())
73
                    ->add($expire)
74
                    ->format('U') - time();
75
        }
76
77 6
        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
     * @return mixed
96
     */
97
    public function pull($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($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
    }
132
133
    /**
134
     * 追加TagSet数据
135
     * @access public
136
     * @param string $name  缓存变量名
137
     * @param mixed  $value 存储数据
138
     * @return void
139
     */
140 3
    public function append($name, $value): void
141
    {
142 3
        $this->push($name, $value);
143
    }
144
145
    /**
146
     * 如果不存在则写入缓存
147
     * @access public
148
     * @param string                             $name   缓存变量名
149
     * @param mixed                              $value  存储数据
150
     * @param int|DateInterval|DateTimeInterface $expire 有效时间 0为永久
151
     * @return mixed
152
     */
153 3
    public function remember($name, $value, $expire = null)
154
    {
155 3
        if ($this->has($name)) {
156
            if (($hit = $this->get($name)) !== null) {
157
                return $hit;
158
            }
159
        }
160
161 3
        $time = time();
162
163 3
        while ($time + 5 > time() && $this->has($name . '_lock')) {
164
            // 存在锁定则等待
165
            usleep(200000);
166
        }
167
168
        try {
169
            // 锁定
170 3
            $this->set($name . '_lock', true);
171
172 3
            if ($value instanceof Closure) {
173
                // 获取缓存数据
174
                $value = Container::getInstance()->invokeFunction($value);
175
            }
176
177
            // 缓存数据
178 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

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

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