This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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 | * Memcached cache wrapper. |
||
16 | * |
||
17 | * @see http://code.google.com/p/memcached/wiki/NewProgrammingTricks |
||
18 | * @see http://dustin.github.com/2011/02/17/memcached-set.html |
||
19 | * |
||
20 | * @package Apix\Cache |
||
21 | * @author Franck Cassedanne <franck at ouarz.net> |
||
22 | */ |
||
23 | class Memcached extends AbstractCache |
||
24 | { |
||
25 | /** |
||
26 | * Holds an injected adapter. |
||
27 | * @var \Memcached |
||
28 | */ |
||
29 | protected $adapter = null; |
||
30 | |||
31 | /** |
||
32 | * Holds the array of TTLs. |
||
33 | * @var array |
||
34 | */ |
||
35 | protected $ttls = array(); |
||
36 | |||
37 | /** |
||
38 | * Constructor. |
||
39 | * |
||
40 | * @param \Memcached $memcached A Memcached instance. |
||
41 | * @param array $options Array of options. |
||
42 | */ |
||
43 | 66 | public function __construct(\Memcached $memcached, array $options = null) |
|
44 | { |
||
45 | // default options |
||
46 | 66 | $this->options['prefix_key'] = 'key_'; // prefix cache keys |
|
47 | 66 | $this->options['prefix_tag'] = 'tag_'; // prefix cache tags |
|
48 | 66 | $this->options['prefix_idx'] = 'idx_'; // prefix cache indexes |
|
49 | 66 | $this->options['prefix_nsp'] = 'nsp_'; // prefix cache namespaces |
|
50 | |||
51 | 66 | // 'auto' is igbinary or msgpack if available, php otherwise. |
|
52 | $this->options['serializer'] = 'auto'; // auto, php, json, json_array |
||
53 | 66 | // igBinary and msgpack |
|
54 | |||
55 | 66 | parent::__construct($memcached, $options); |
|
56 | |||
57 | 66 | $memcached->setOption(\Memcached::OPT_COMPRESSION, false); |
|
58 | 66 | ||
59 | 66 | if ($this->options['tag_enable']) { |
|
60 | 66 | $memcached->setOption(\Memcached::OPT_BINARY_PROTOCOL, false); |
|
61 | 66 | $this->setSerializer($this->options['serializer']); |
|
62 | 66 | $this->setNamespace($this->options['prefix_nsp']); |
|
63 | } |
||
64 | } |
||
65 | |||
66 | /** |
||
67 | 18 | * {@inheritdoc} |
|
68 | */ |
||
69 | 18 | public function loadKey($key) |
|
70 | { |
||
71 | return $this->get($this->mapKey($key)); |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | 22 | * {@inheritdoc} |
|
76 | */ |
||
77 | 22 | public function loadTag($tag) |
|
78 | { |
||
79 | return $this->getIndex($this->mapTag($tag))->load(); |
||
0 ignored issues
–
show
|
|||
80 | } |
||
81 | |||
82 | /** |
||
83 | 36 | * {@inheritdoc} |
|
84 | */ |
||
85 | 36 | public function save($data, $key, array $tags = null, $ttl = null) |
|
86 | { |
||
87 | 36 | $ttl = $this->sanitiseTtl($ttl); |
|
88 | |||
89 | 36 | $mKey = $this->mapKey($key); |
|
90 | 36 | ||
91 | $data = array('data' => $data, 'ttl' => $ttl); |
||
92 | $this->ttls[$mKey] = $ttl; |
||
93 | 36 | ||
94 | // add the item |
||
95 | 36 | $success = $this->adapter->set($mKey, $data, $ttl); |
|
96 | |||
97 | if ($success && $this->options['tag_enable'] && !empty($tags)) { |
||
98 | 20 | ||
99 | // add all the tags to the index key. |
||
100 | $this->getIndex($this->mapIdx($key))->add($tags); |
||
101 | 20 | ||
102 | 20 | // append the key to each tag. |
|
103 | 20 | foreach ($tags as $tag) { |
|
104 | 20 | $this->getIndex($this->mapTag($tag))->add($mKey); |
|
105 | } |
||
106 | 36 | } |
|
107 | |||
108 | return $success; |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * Alias to `Memcached::deleteMulti` or loop `Memcached::delete`. |
||
113 | * |
||
114 | * @param array $items The items to be deleted. |
||
115 | * |
||
116 | 8 | * @return bool Returns TRUE on success or FALSE on failure. |
|
117 | */ |
||
118 | 8 | protected function deleteMulti($items) |
|
119 | 8 | { |
|
120 | if (method_exists($this->adapter, 'deleteMulti')) { |
||
121 | 8 | $this->adapter->deleteMulti($items); |
|
122 | |||
123 | return (boolean) $this->adapter->getResultCode() != \Memcached::RES_FAILURE; |
||
124 | } |
||
125 | |||
126 | // Fix environments (some HHVM versions) that don't handle deleteMulti. |
||
127 | // @see https://github.com/facebook/hhvm/issues/4602 |
||
128 | // @codeCoverageIgnoreStart |
||
129 | $success = true; |
||
130 | foreach ($items as $item) { |
||
131 | $success = $this->adapter->delete($item) && $success; |
||
132 | } |
||
133 | |||
134 | return $success; |
||
135 | // @codeCoverageIgnoreEnd |
||
136 | } |
||
137 | 2 | ||
138 | /** |
||
139 | 2 | * {@inheritdoc} |
|
140 | 2 | */ |
|
141 | 2 | public function clean(array $tags) |
|
142 | 2 | { |
|
143 | 2 | $items = array(); |
|
144 | 2 | foreach ($tags as $tag) { |
|
145 | $keys = $this->loadTag($tag); |
||
146 | 2 | if (null !== $keys) { |
|
147 | 2 | foreach ($keys as $key) { |
|
148 | $items[] = $key; |
||
149 | 2 | // $items[] = $this->mapIdx($key); |
|
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
65% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
150 | } |
||
151 | } |
||
152 | // add the tag to deletion |
||
153 | 2 | $items[] = $this->mapTag($tag); |
|
154 | |||
155 | 2 | // add the index key for deletion |
|
156 | // $items[] = $this->mapTag($tag); |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
65% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
157 | } |
||
158 | |||
159 | return $this->deleteMulti($items); |
||
160 | } |
||
161 | 6 | ||
162 | /** |
||
163 | 6 | * {@inheritdoc} |
|
164 | 6 | */ |
|
165 | public function delete($key) |
||
166 | 6 | { |
|
167 | 4 | $_key = $this->mapKey($key); |
|
168 | $items = array($_key); |
||
169 | |||
170 | 4 | if ($this->options['tag_enable']) { |
|
171 | $idx_key = $this->mapIdx($key); |
||
172 | 4 | ||
173 | // load the tags from the index key |
||
174 | 2 | $tags = $this->getIndex($idx_key)->load(); |
|
175 | 2 | ||
176 | 2 | if (is_array($tags)) { |
|
177 | // mark the key as deleted in the tags. |
||
178 | 2 | foreach ($tags as $tag) { |
|
179 | 2 | $this->getIndex($this->mapTag($tag))->remove($_key); |
|
180 | 4 | } |
|
181 | // delete that index key |
||
182 | 6 | $items[] = $idx_key; |
|
183 | } |
||
184 | } |
||
185 | |||
186 | return $this->deleteMulti($items); |
||
187 | } |
||
188 | 66 | ||
189 | /** |
||
190 | 66 | * {@inheritdoc} |
|
191 | 66 | */ |
|
192 | public function flush($all = false) |
||
193 | 4 | { |
|
194 | if (true === $all) { |
||
195 | return $this->adapter->flush(); |
||
196 | 4 | } |
|
197 | $nsKey = $this->options['prefix_nsp']; |
||
198 | 4 | ||
199 | // set a new namespace |
||
200 | $success = $this->setNamespace($nsKey, true); |
||
201 | |||
202 | return (boolean) $success; |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | 66 | * {@inheritdoc} |
|
207 | * |
||
208 | * @param string $serializer |
||
209 | */ |
||
210 | public function setSerializer($serializer) |
||
211 | { |
||
212 | switch ($serializer) { |
||
213 | |||
214 | case 'php': |
||
215 | $opt = \Memcached::SERIALIZER_PHP; |
||
216 | break; |
||
217 | |||
218 | // @codeCoverageIgnoreStart |
||
219 | case 'igBinary': |
||
220 | if (!\Memcached::HAVE_IGBINARY) { |
||
221 | continue; |
||
222 | } |
||
223 | $opt = \Memcached::SERIALIZER_IGBINARY; |
||
224 | break; |
||
225 | |||
226 | case 'json': |
||
227 | if (!\Memcached::HAVE_JSON) { |
||
228 | 66 | continue; |
|
229 | 66 | } |
|
230 | 66 | $opt = \Memcached::SERIALIZER_JSON; |
|
231 | 66 | break; |
|
232 | |||
233 | 66 | case 'json_array': |
|
234 | 66 | if (!\Memcached::HAVE_JSON_ARRAY) { |
|
235 | 66 | continue; |
|
236 | 66 | } |
|
237 | $opt = \Memcached::SERIALIZER_JSON_ARRAY; |
||
238 | break; |
||
239 | |||
240 | case 'msgpack': |
||
241 | 4 | if (!\Memcached::HAVE_MSGPACK) { |
|
242 | continue; |
||
243 | 4 | } |
|
244 | $opt = \Memcached::SERIALIZER_MSGPACK; |
||
245 | break; |
||
246 | // @codeCoverageIgnoreEnd |
||
247 | |||
248 | default: |
||
249 | } |
||
250 | |||
251 | if (isset($opt)) { |
||
252 | $this->adapter->setOption(\Memcached::OPT_SERIALIZER, $opt); |
||
253 | } |
||
254 | 50 | } |
|
255 | |||
256 | 50 | /** |
|
257 | 50 | * {@inheritdoc} |
|
258 | 36 | */ |
|
259 | public function getSerializer() |
||
260 | 36 | { |
|
261 | return $this->adapter->getOption(\Memcached::OPT_SERIALIZER); |
||
262 | } |
||
263 | 25 | ||
264 | /** |
||
265 | * Retrieves the cache item for the given id. |
||
266 | * |
||
267 | * @param string $id The cache id to retrieve. |
||
268 | * @param float $cas_token The variable to store the CAS token in. |
||
269 | * |
||
270 | * @return mixed|null Returns the cached data or null. |
||
271 | */ |
||
272 | public function get($id, &$cas_token = null) |
||
273 | { |
||
274 | $data = $this->adapter->get($id, null, $cas_token); |
||
275 | if ($this->adapter->getResultCode() == \Memcached::RES_SUCCESS) { |
||
276 | $this->ttls[$id] = isset($data['ttl']) ? $data['ttl'] : 0; |
||
277 | 36 | ||
278 | return isset($data['data']) ? $data['data'] : $data; |
||
279 | 36 | } |
|
280 | |||
281 | return; |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * Returns the ttl sanitased for this cache adapter. |
||
286 | * |
||
287 | * The number of seconds may not exceed 60*60*24*30 = 2,592,000 (30 days). |
||
288 | * |
||
289 | 66 | * @see http://php.net/manual/en/memcached.expiration.php |
|
290 | * |
||
291 | 66 | * @param int|null $ttl The time-to-live in seconds. |
|
292 | * |
||
293 | * @return int |
||
294 | */ |
||
295 | public function sanitiseTtl($ttl) |
||
296 | { |
||
297 | return $ttl > 2592000 ? time() + $ttl : $ttl; |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * Returns the named indexer. |
||
302 | * |
||
303 | * @param string $name The name of the index. |
||
304 | 66 | * |
|
305 | * @return Indexer\Adapter |
||
306 | */ |
||
307 | 66 | public function getIndex($name) |
|
308 | { |
||
309 | return new Indexer\MemcachedIndexer($name, $this); |
||
310 | 66 | } |
|
311 | |||
312 | 66 | /** |
|
313 | * Sets the namespace prefix. |
||
314 | 4 | * Specific to memcache; this sets as 'ns'+integer (incremented). |
|
315 | 4 | * |
|
316 | 66 | * @param string $ns |
|
317 | 66 | * @param bool $renew |
|
318 | 66 | * @param string $suffix |
|
319 | 66 | * |
|
320 | 66 | * @return int |
|
321 | */ |
||
322 | public function setNamespace($ns, $renew = false, $suffix = '_') |
||
323 | 66 | { |
|
324 | 66 | // temporally set the namespace to null |
|
325 | $this->adapter->setOption(\Memcached::OPT_PREFIX_KEY, null); |
||
326 | 66 | ||
327 | // mark the current namespace for future deletion |
||
328 | $this->getIndex($this->mapIdx($ns))->remove($this->getNamespace()); |
||
329 | |||
330 | if ($renew) { |
||
331 | // increment the namespace counter |
||
332 | $counter = $this->increment($ns); |
||
333 | } else { |
||
334 | 66 | $counter = $this->adapter->get($ns); |
|
335 | if (false === $counter) { |
||
336 | 66 | $counter = 1; |
|
337 | $this->adapter->set($ns, $counter); |
||
338 | } |
||
339 | } |
||
340 | |||
341 | $ns .= $counter.$suffix; |
||
342 | $this->adapter->setOption(\Memcached::OPT_PREFIX_KEY, $ns); |
||
343 | |||
344 | return $counter; |
||
345 | } |
||
346 | 66 | ||
347 | /** |
||
348 | 66 | * Returns the namespace. |
|
349 | * |
||
350 | * @return string |
||
351 | */ |
||
352 | public function getNamespace() |
||
353 | { |
||
354 | return $this->adapter->getOption(\Memcached::OPT_PREFIX_KEY); |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | 6 | * Returns a prefixed and sanitased cache id. |
|
359 | * |
||
360 | * @param string $key The base key to prefix. |
||
361 | * |
||
362 | * @return string |
||
363 | */ |
||
364 | public function mapIdx($key) |
||
365 | 6 | { |
|
366 | 6 | return $this->sanitise($this->options['prefix_idx'].$key); |
|
367 | 2 | } |
|
368 | 2 | ||
369 | 2 | /** |
|
370 | 6 | * Increments the value of the given key. |
|
371 | * |
||
372 | * @param string $key The key to increment. |
||
373 | 6 | * |
|
374 | * @return int|bool Returns the new item's value on success or FALSE on failure. |
||
375 | */ |
||
376 | public function increment($key) |
||
377 | { |
||
378 | // if (true === \Memcached::OPT_BINARY_PROTOCOL) { |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
43% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
379 | // // Increment will initialize the value (if not available) |
||
380 | // // only when OPT_BINARY_PROTOCOL is set to true! |
||
381 | // return $this->adapter->increment($key, 1); |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
67% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
382 | // } |
||
383 | 4 | $counter = $this->adapter->get($key); |
|
384 | if (false === $counter) { |
||
385 | 4 | $counter = 1; |
|
386 | $this->adapter->set($key, $counter); |
||
387 | 4 | } else { |
|
388 | 4 | $counter = $this->adapter->increment($key); |
|
389 | 4 | } |
|
390 | 4 | ||
391 | 4 | return $counter; |
|
392 | 4 | } |
|
393 | 4 | ||
394 | /** |
||
395 | 4 | * {@inheritdoc} |
|
396 | * |
||
397 | * The number of seconds may not exceed 60*60*24*30 = 2,592,000 (30 days). |
||
398 | 2 | * |
|
399 | * @see http://php.net/manual/en/memcached.expiration.php |
||
400 | */ |
||
401 | public function getTtl($key) |
||
402 | { |
||
403 | $mKey = $this->mapKey($key); |
||
404 | |||
405 | if (!isset($this->ttls[$mKey])) { |
||
406 | $data = $this->adapter->get($mKey, null, $cas_token); |
||
407 | $this->ttls[$mKey] = |
||
408 | $this->adapter->getResultCode() == \Memcached::RES_SUCCESS |
||
409 | ? (isset($data['ttl']) ? $data['ttl'] : 0) |
||
410 | : false; |
||
411 | } |
||
412 | |||
413 | return $this->ttls[$mKey]; |
||
414 | } |
||
415 | } |
||
416 |
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.