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; |
|
|
|
|
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
|
|
|
|
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:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.