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