1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* |
4
|
|
|
* This file is part of the Apix Project. |
5
|
|
|
* |
6
|
|
|
* (c) Franck Cassedanne <franck at ouarz.net> |
7
|
|
|
* |
8
|
|
|
* @license http://opensource.org/licenses/BSD-3-Clause New BSD License |
9
|
|
|
* |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Apix\Cache; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Memcache cache wrapper. |
16
|
|
|
* |
17
|
|
|
* @author Franck Cassedanne <franck at ouarz.net> |
18
|
|
|
* |
19
|
|
|
*/ |
20
|
|
|
class Memcache extends AbstractCache |
21
|
|
|
{ |
22
|
|
|
/** |
23
|
|
|
* Holds an injected adapter. |
24
|
|
|
* @var \Memcache |
25
|
|
|
*/ |
26
|
|
|
protected $adapter = null; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Holds the array of TTLs. |
30
|
|
|
* @var array |
31
|
|
|
*/ |
32
|
|
|
protected $ttls = array(); |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Constructor. |
36
|
|
|
* |
37
|
|
|
* @param \Memcache $memcache A Memcache instance. |
38
|
|
|
* @param array $options Array of options. |
39
|
|
|
*/ |
40
|
|
|
public function __construct(\Memcache $memcache, array $options = null) |
41
|
|
|
{ |
42
|
|
|
// default options |
43
|
|
|
$this->options['prefix_key'] = 'key_'; // prefix cache keys |
44
|
|
|
$this->options['prefix_tag'] = 'tag_'; // prefix cache tags |
45
|
|
|
$this->options['prefix_idx'] = 'idx_'; // prefix cache indexes |
46
|
|
|
$this->options['prefix_nsp'] = 'nsp_'; // prefix cache namespaces |
47
|
|
|
|
48
|
|
|
$this->options['serializer'] = 'php'; // none, php, json, igBinary. |
49
|
|
|
|
50
|
|
|
parent::__construct($memcache, $options); |
51
|
|
|
|
52
|
|
|
if ($this->options['tag_enable']) { |
53
|
|
|
$this->setSerializer($this->options['serializer']); |
54
|
|
|
} |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* {@inheritdoc} |
59
|
|
|
*/ |
60
|
|
|
public function loadKey($key) |
61
|
|
|
{ |
62
|
|
|
return $this->get($this->mapKey($key)); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* {@inheritdoc} |
67
|
|
|
*/ |
68
|
|
|
public function loadTag($tag) |
69
|
|
|
{ |
70
|
|
|
return $this->getIndex($this->mapTag($tag))->load(); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* {@inheritdoc} |
75
|
|
|
*/ |
76
|
|
View Code Duplication |
public function save($data, $key, array $tags = null, $ttl = null) |
|
|
|
|
77
|
|
|
{ |
78
|
|
|
$ttl = $this->sanitiseTtl($ttl); |
79
|
|
|
|
80
|
|
|
$mKey = $this->mapKey($key); |
81
|
|
|
|
82
|
|
|
$data = array('data' => $data, 'ttl' => $ttl); |
83
|
|
|
$this->ttls[$mKey] = $ttl; |
84
|
|
|
|
85
|
|
|
// add the item |
86
|
|
|
$success = $this->adapter->set($mKey, $data, $ttl); |
87
|
|
|
|
88
|
|
|
if ($success && $this->options['tag_enable'] && !empty($tags)) { |
89
|
|
|
|
90
|
|
|
// add all the tags to the index key. |
91
|
|
|
$this->getIndex($this->mapIdx($key))->add($tags); |
92
|
|
|
|
93
|
|
|
// append the key to each tag. |
94
|
|
|
foreach ($tags as $tag) { |
95
|
|
|
$this->getIndex($this->mapTag($tag))->add($mKey); |
96
|
|
|
} |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
return $success; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* {@inheritdoc} |
104
|
|
|
*/ |
105
|
|
|
public function clean(array $tags) |
106
|
|
|
{ |
107
|
|
|
$items = array(); |
108
|
|
|
|
109
|
|
View Code Duplication |
foreach ($tags as $tag) { |
|
|
|
|
110
|
|
|
$keys = $this->loadTag($tag); |
111
|
|
|
if (null !== $keys) { |
112
|
|
|
foreach ($keys as $key) { |
113
|
|
|
$items[] = $key; |
114
|
|
|
} |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
// add the tag to deletion |
118
|
|
|
$items[] = $this->mapTag($tag); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
$success = true; |
122
|
|
|
|
123
|
|
|
foreach ($items as $item) { |
124
|
|
|
$success = $this->adapter->delete($item) && $success; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
return $success; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* {@inheritdoc} |
132
|
|
|
*/ |
133
|
|
|
public function delete($key) |
134
|
|
|
{ |
135
|
|
|
$_key = $this->mapKey($key); |
136
|
|
|
$items = array($_key); |
137
|
|
|
|
138
|
|
View Code Duplication |
if ($this->options['tag_enable']) { |
|
|
|
|
139
|
|
|
$idx_key = $this->mapIdx($key); |
140
|
|
|
|
141
|
|
|
// load the tags from the index key |
142
|
|
|
$tags = $this->getIndex($idx_key)->load(); |
143
|
|
|
|
144
|
|
|
if (is_array($tags)) { |
145
|
|
|
// mark the key as deleted in the tags. |
146
|
|
|
foreach ($tags as $tag) { |
147
|
|
|
$this->getIndex($this->mapTag($tag))->remove($_key); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
// delete that index key |
151
|
|
|
$items[] = $idx_key; |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
$success = true; |
156
|
|
|
|
157
|
|
|
foreach ($items as $item) { |
158
|
|
|
$success = $this->adapter->delete($item) && $success; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
return $success; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* {@inheritdoc} |
166
|
|
|
*/ |
167
|
|
|
public function flush($all = false) |
168
|
|
|
{ |
169
|
|
|
return $this->adapter->flush(); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Retrieves the cache item for the given id. |
174
|
|
|
* |
175
|
|
|
* @param string $id The cache id to retrieve. |
176
|
|
|
* @return mixed|null Returns the cached data or null. |
177
|
|
|
*/ |
178
|
|
|
public function get($id) |
179
|
|
|
{ |
180
|
|
|
$data = $this->adapter->get($id); |
181
|
|
|
|
182
|
|
|
if (false !== $data) { |
183
|
|
|
$this->ttls[$id] = isset($data['ttl']) ? $data['ttl'] : 0; |
184
|
|
|
|
185
|
|
|
return isset($data['data']) ? $data['data'] : $data; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
return null; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Returns the ttl sanitased for this cache adapter. |
193
|
|
|
* |
194
|
|
|
* The number of seconds may not exceed 60*60*24*30 = 2,592,000 (30 days). |
195
|
|
|
* @see http://php.net/manual/en/Memcache.expiration.php |
196
|
|
|
* |
197
|
|
|
* @param integer|null $ttl The time-to-live in seconds. |
198
|
|
|
* @return int |
199
|
|
|
*/ |
200
|
|
|
public function sanitiseTtl($ttl) |
201
|
|
|
{ |
202
|
|
|
return $ttl > 2592000 ? time() + $ttl : $ttl; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* Returns the named indexer. |
207
|
|
|
* |
208
|
|
|
* @param string $name The name of the index. |
209
|
|
|
* @return Indexer\Adapter |
210
|
|
|
*/ |
211
|
|
|
public function getIndex($name) |
212
|
|
|
{ |
213
|
|
|
return new Indexer\MemcacheIndexer($name, $this); |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* Returns a prefixed and sanitased cache id. |
218
|
|
|
* |
219
|
|
|
* @param string $key The base key to prefix. |
220
|
|
|
* @return string |
221
|
|
|
*/ |
222
|
|
|
public function mapIdx($key) |
223
|
|
|
{ |
224
|
|
|
return $this->sanitise($this->options['prefix_idx'] . $key); |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* Increments the value of the given key. |
229
|
|
|
* |
230
|
|
|
* @param string $key The key to increment. |
231
|
|
|
* |
232
|
|
|
* @return int|bool Returns the new item's value on success or FALSE on failure. |
233
|
|
|
*/ |
234
|
|
View Code Duplication |
public function increment($key) |
|
|
|
|
235
|
|
|
{ |
236
|
|
|
$counter = $this->adapter->get($key); |
237
|
|
|
|
238
|
|
|
if (false === $counter) { |
239
|
|
|
$counter = 1; |
240
|
|
|
$this->adapter->set($key, $counter); |
241
|
|
|
} else { |
242
|
|
|
$counter = $this->adapter->increment($key); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
return $counter; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* {@inheritdoc} |
250
|
|
|
* |
251
|
|
|
* The number of seconds may not exceed 60*60*24*30 = 2,592,000 (30 days). |
252
|
|
|
*/ |
253
|
|
|
public function getTtl($key) |
254
|
|
|
{ |
255
|
|
|
$mKey = $this->mapKey($key); |
256
|
|
|
|
257
|
|
|
if (!isset($this->ttls[$mKey])) { |
258
|
|
|
$data = $this->adapter->get($mKey); |
259
|
|
|
|
260
|
|
|
$this->ttls[$mKey] = (isset($data['ttl']) ? $data['ttl'] : 0); |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
return $this->ttls[$mKey]; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Gets the injected adapter. |
268
|
|
|
* |
269
|
|
|
* @return \Memcache |
270
|
|
|
*/ |
271
|
|
|
public function getAdapter() |
272
|
|
|
{ |
273
|
|
|
return $this->adapter; |
274
|
|
|
} |
275
|
|
|
} |
276
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.