1 <?php
2 3 4 5 6 7 8
9
10 defined('JPATH_PLATFORM') or die;
11
12 13 14 15 16 17
18 class JCacheStorageMemcached extends JCacheStorage
19 {
20 21 22 23 24 25
26 protected static $_db = null;
27
28 29 30 31 32 33
34 protected $_compress = 0;
35
36 37 38 39 40 41 42
43 public function __construct($options = array())
44 {
45 parent::__construct($options);
46
47 $this->_compress = JFactory::getConfig()->get('memcached_compress', false) ? Memcached::OPT_COMPRESSION : 0;
48
49 if (static::$_db === null)
50 {
51 $this->getConnection();
52 }
53 }
54
55 56 57 58 59 60 61 62
63 protected function getConnection()
64 {
65 if (!static::isSupported())
66 {
67 throw new RuntimeException('Memcached Extension is not available');
68 }
69
70 $config = JFactory::getConfig();
71
72 $host = $config->get('memcached_server_host', 'localhost');
73 $port = $config->get('memcached_server_port', 11211);
74
75
76
77 if ($config->get('memcached_persist', true))
78 {
79 static::$_db = new Memcached($this->_hash);
80 $servers = static::$_db->getServerList();
81
82 if ($servers && ($servers[0]['host'] != $host || $servers[0]['port'] != $port))
83 {
84 static::$_db->resetServerList();
85 $servers = array();
86 }
87
88 if (!$servers)
89 {
90 static::$_db->addServer($host, $port);
91 }
92 }
93 else
94 {
95 static::$_db = new Memcached;
96 static::$_db->addServer($host, $port);
97 }
98
99 static::$_db->setOption(Memcached::OPT_COMPRESSION, $this->_compress);
100
101 $stats = static::$_db->getStats();
102 $result = !empty($stats["$host:$port"]) && $stats["$host:$port"]['pid'] > 0;
103
104 if (!$result)
105 {
106
107 static::$_db = null;
108
109 throw new JCacheExceptionConnecting('Could not connect to memcached server');
110 }
111 }
112
113 114 115 116 117 118 119 120 121 122
123 protected function _getCacheId($id, $group)
124 {
125 $prefix = JCache::getPlatformPrefix();
126 $length = strlen($prefix);
127 $cache_id = parent::_getCacheId($id, $group);
128
129 if ($length)
130 {
131
132 $cache_id = substr($cache_id, $length) . strrev($prefix);
133 }
134
135 return $cache_id;
136 }
137
138 139 140 141 142 143 144 145 146 147
148 public function contains($id, $group)
149 {
150 static::$_db->get($this->_getCacheId($id, $group));
151
152 return static::$_db->getResultCode() !== Memcached::RES_NOTFOUND;
153 }
154
155 156 157 158 159 160 161 162 163 164 165
166 public function get($id, $group, $checkTime = true)
167 {
168 return static::$_db->get($this->_getCacheId($id, $group));
169 }
170
171 172 173 174 175 176 177
178 public function getAll()
179 {
180 $keys = static::$_db->get($this->_hash . '-index');
181 $secret = $this->_hash;
182
183 $data = array();
184
185 if (is_array($keys))
186 {
187 foreach ($keys as $key)
188 {
189 if (empty($key))
190 {
191 continue;
192 }
193
194 $namearr = explode('-', $key->name);
195
196 if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
197 {
198 $group = $namearr[2];
199
200 if (!isset($data[$group]))
201 {
202 $item = new JCacheStorageHelper($group);
203 }
204 else
205 {
206 $item = $data[$group];
207 }
208
209 $item->updateSize($key->size);
210
211 $data[$group] = $item;
212 }
213 }
214 }
215
216 return $data;
217 }
218
219 220 221 222 223 224 225 226 227 228 229
230 public function store($id, $group, $data)
231 {
232 $cache_id = $this->_getCacheId($id, $group);
233
234 if (!$this->lockindex())
235 {
236 return false;
237 }
238
239 $index = static::$_db->get($this->_hash . '-index');
240
241 if (!is_array($index))
242 {
243 $index = array();
244 }
245
246 $tmparr = new stdClass;
247 $tmparr->name = $cache_id;
248 $tmparr->size = strlen($data);
249
250 $index[] = $tmparr;
251 static::$_db->set($this->_hash . '-index', $index, 0);
252 $this->unlockindex();
253
254 static::$_db->set($cache_id, $data, $this->_lifetime);
255
256 return true;
257 }
258
259 260 261 262 263 264 265 266 267 268
269 public function remove($id, $group)
270 {
271 $cache_id = $this->_getCacheId($id, $group);
272
273 if (!$this->lockindex())
274 {
275 return false;
276 }
277
278 $index = static::$_db->get($this->_hash . '-index');
279
280 if (is_array($index))
281 {
282 foreach ($index as $key => $value)
283 {
284 if ($value->name == $cache_id)
285 {
286 unset($index[$key]);
287 static::$_db->set($this->_hash . '-index', $index, 0);
288 break;
289 }
290 }
291 }
292
293 $this->unlockindex();
294
295 return static::$_db->delete($cache_id);
296 }
297
298 299 300 301 302 303 304 305 306 307 308 309 310
311 public function clean($group, $mode = null)
312 {
313 if (!$this->lockindex())
314 {
315 return false;
316 }
317
318 $index = static::$_db->get($this->_hash . '-index');
319
320 if (is_array($index))
321 {
322 $prefix = $this->_hash . '-cache-' . $group . '-';
323
324 foreach ($index as $key => $value)
325 {
326 if (strpos($value->name, $prefix) === 0 xor $mode != 'group')
327 {
328 static::$_db->delete($value->name);
329 unset($index[$key]);
330 }
331 }
332
333 static::$_db->set($this->_hash . '-index', $index, 0);
334 }
335
336 $this->unlockindex();
337
338 return true;
339 }
340
341 342 343 344 345 346 347
348 public function flush()
349 {
350 if (!$this->lockindex())
351 {
352 return false;
353 }
354
355 return static::$_db->flush();
356 }
357
358 359 360 361 362 363 364
365 public static function isSupported()
366 {
367 368 369 370
371 return class_exists('Memcached');
372 }
373
374 375 376 377 378 379 380 381 382 383 384
385 public function lock($id, $group, $locktime)
386 {
387 $returning = new stdClass;
388 $returning->locklooped = false;
389
390 $looptime = $locktime * 10;
391
392 $cache_id = $this->_getCacheId($id, $group);
393
394 $data_lock = static::$_db->add($cache_id . '_lock', 1, $locktime);
395
396 if ($data_lock === false)
397 {
398 $lock_counter = 0;
399
400
401
402 while ($data_lock === false)
403 {
404 if ($lock_counter > $looptime)
405 {
406 break;
407 }
408
409 usleep(100);
410 $data_lock = static::$_db->add($cache_id . '_lock', 1, $locktime);
411 $lock_counter++;
412 }
413
414 $returning->locklooped = true;
415 }
416
417 $returning->locked = $data_lock;
418
419 return $returning;
420 }
421
422 423 424 425 426 427 428 429 430 431
432 public function unlock($id, $group = null)
433 {
434 $cache_id = $this->_getCacheId($id, $group) . '_lock';
435 return static::$_db->delete($cache_id);
436 }
437
438 439 440 441 442 443 444
445 protected function lockindex()
446 {
447 $looptime = 300;
448 $data_lock = static::$_db->add($this->_hash . '-index_lock', 1, 30);
449
450 if ($data_lock === false)
451 {
452 $lock_counter = 0;
453
454
455 while ($data_lock === false)
456 {
457 if ($lock_counter > $looptime)
458 {
459 return false;
460 }
461
462 usleep(100);
463 $data_lock = static::$_db->add($this->_hash . '-index_lock', 1, 30);
464 $lock_counter++;
465 }
466 }
467
468 return true;
469 }
470
471 472 473 474 475 476 477
478 protected function unlockindex()
479 {
480 return static::$_db->delete($this->_hash . '-index_lock');
481 }
482 }
483