[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Cache/Storage/ -> MemcachedStorage.php (source)

   1  <?php
   2  /**
   3   * Joomla! Content Management System
   4   *
   5   * @copyright  (C) 2012 Open Source Matters, Inc. <https://www.joomla.org>
   6   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   7   */
   8  
   9  namespace Joomla\CMS\Cache\Storage;
  10  
  11  \defined('JPATH_PLATFORM') or die;
  12  
  13  use Joomla\CMS\Cache\Cache;
  14  use Joomla\CMS\Cache\CacheStorage;
  15  use Joomla\CMS\Cache\Exception\CacheConnectingException;
  16  use Joomla\CMS\Factory;
  17  
  18  /**
  19   * Memcached cache storage handler
  20   *
  21   * @link   https://www.php.net/manual/en/book.memcached.php
  22   * @since  3.0.0
  23   */
  24  class MemcachedStorage extends CacheStorage
  25  {
  26      /**
  27       * Memcached connection object
  28       *
  29       * @var    \Memcached
  30       * @since  3.0.0
  31       */
  32      protected static $_db = null;
  33  
  34      /**
  35       * Payload compression level
  36       *
  37       * @var    integer
  38       * @since  3.0.0
  39       */
  40      protected $_compress = 0;
  41  
  42      /**
  43       * Constructor
  44       *
  45       * @param   array  $options  Optional parameters.
  46       *
  47       * @since   3.0.0
  48       */
  49  	public function __construct($options = array())
  50      {
  51          parent::__construct($options);
  52  
  53          $this->_compress = Factory::getApplication()->get('memcached_compress', false) ? \Memcached::OPT_COMPRESSION : 0;
  54  
  55          if (static::$_db === null)
  56          {
  57              $this->getConnection();
  58          }
  59      }
  60  
  61      /**
  62       * Create the Memcached connection
  63       *
  64       * @return  void
  65       *
  66       * @since   3.0.0
  67       * @throws  \RuntimeException
  68       */
  69  	protected function getConnection()
  70      {
  71          if (!static::isSupported())
  72          {
  73              throw new \RuntimeException('Memcached Extension is not available');
  74          }
  75  
  76          $app = Factory::getApplication();
  77  
  78          $host = $app->get('memcached_server_host', 'localhost');
  79          $port = $app->get('memcached_server_port', 11211);
  80  
  81  
  82          // Create the memcached connection
  83          if ($app->get('memcached_persist', true))
  84          {
  85              static::$_db = new \Memcached($this->_hash);
  86              $servers = static::$_db->getServerList();
  87  
  88              if ($servers && ($servers[0]['host'] != $host || $servers[0]['port'] != $port))
  89              {
  90                  static::$_db->resetServerList();
  91                  $servers = array();
  92              }
  93  
  94              if (!$servers)
  95              {
  96                  static::$_db->addServer($host, $port);
  97              }
  98          }
  99          else
 100          {
 101              static::$_db = new \Memcached;
 102              static::$_db->addServer($host, $port);
 103          }
 104  
 105          static::$_db->setOption(\Memcached::OPT_COMPRESSION, $this->_compress);
 106  
 107          $stats  = static::$_db->getStats();
 108          $result = !empty($stats["$host:$port"]) && $stats["$host:$port"]['pid'] > 0;
 109  
 110          if (!$result)
 111          {
 112              // Null out the connection to inform the constructor it will need to attempt to connect if this class is instantiated again
 113              static::$_db = null;
 114  
 115              throw new CacheConnectingException('Could not connect to memcached server');
 116          }
 117      }
 118  
 119      /**
 120       * Get a cache_id string from an id/group pair
 121       *
 122       * @param   string  $id     The cache data id
 123       * @param   string  $group  The cache data group
 124       *
 125       * @return  string   The cache_id string
 126       *
 127       * @since   1.7.0
 128       */
 129  	protected function _getCacheId($id, $group)
 130      {
 131          $prefix   = Cache::getPlatformPrefix();
 132          $length   = \strlen($prefix);
 133          $cache_id = parent::_getCacheId($id, $group);
 134  
 135          if ($length)
 136          {
 137              // Memcached use suffix instead of prefix
 138              $cache_id = substr($cache_id, $length) . strrev($prefix);
 139          }
 140  
 141          return $cache_id;
 142      }
 143  
 144      /**
 145       * Check if the cache contains data stored by ID and group
 146       *
 147       * @param   string  $id     The cache data ID
 148       * @param   string  $group  The cache data group
 149       *
 150       * @return  boolean
 151       *
 152       * @since   3.7.0
 153       */
 154  	public function contains($id, $group)
 155      {
 156          static::$_db->get($this->_getCacheId($id, $group));
 157  
 158          return static::$_db->getResultCode() !== \Memcached::RES_NOTFOUND;
 159      }
 160  
 161      /**
 162       * Get cached data by ID and group
 163       *
 164       * @param   string   $id         The cache data ID
 165       * @param   string   $group      The cache data group
 166       * @param   boolean  $checkTime  True to verify cache time expiration threshold
 167       *
 168       * @return  mixed  Boolean false on failure or a cached data object
 169       *
 170       * @since   3.0.0
 171       */
 172  	public function get($id, $group, $checkTime = true)
 173      {
 174          return static::$_db->get($this->_getCacheId($id, $group));
 175      }
 176  
 177      /**
 178       * Get all cached data
 179       *
 180       * @return  mixed  Boolean false on failure or a cached data object
 181       *
 182       * @since   3.0.0
 183       */
 184  	public function getAll()
 185      {
 186          $keys   = static::$_db->get($this->_hash . '-index');
 187          $secret = $this->_hash;
 188  
 189          $data = array();
 190  
 191          if (\is_array($keys))
 192          {
 193              foreach ($keys as $key)
 194              {
 195                  if (empty($key))
 196                  {
 197                      continue;
 198                  }
 199  
 200                  $namearr = explode('-', $key->name);
 201  
 202                  if ($namearr !== false && $namearr[0] == $secret && $namearr[1] === 'cache')
 203                  {
 204                      $group = $namearr[2];
 205  
 206                      if (!isset($data[$group]))
 207                      {
 208                          $item = new CacheStorageHelper($group);
 209                      }
 210                      else
 211                      {
 212                          $item = $data[$group];
 213                      }
 214  
 215                      $item->updateSize($key->size);
 216  
 217                      $data[$group] = $item;
 218                  }
 219              }
 220          }
 221  
 222          return $data;
 223      }
 224  
 225      /**
 226       * Store the data to cache by ID and group
 227       *
 228       * @param   string  $id     The cache data ID
 229       * @param   string  $group  The cache data group
 230       * @param   string  $data   The data to store in cache
 231       *
 232       * @return  boolean
 233       *
 234       * @since   3.0.0
 235       */
 236  	public function store($id, $group, $data)
 237      {
 238          $cache_id = $this->_getCacheId($id, $group);
 239  
 240          if (!$this->lockindex())
 241          {
 242              return false;
 243          }
 244  
 245          $index = static::$_db->get($this->_hash . '-index');
 246  
 247          if (!\is_array($index))
 248          {
 249              $index = array();
 250          }
 251  
 252          $tmparr       = new \stdClass;
 253          $tmparr->name = $cache_id;
 254          $tmparr->size = \strlen($data);
 255  
 256          $index[] = $tmparr;
 257          static::$_db->set($this->_hash . '-index', $index, 0);
 258          $this->unlockindex();
 259  
 260          static::$_db->set($cache_id, $data, $this->_lifetime);
 261  
 262          return true;
 263      }
 264  
 265      /**
 266       * Remove a cached data entry by ID and group
 267       *
 268       * @param   string  $id     The cache data ID
 269       * @param   string  $group  The cache data group
 270       *
 271       * @return  boolean
 272       *
 273       * @since   3.0.0
 274       */
 275  	public function remove($id, $group)
 276      {
 277          $cache_id = $this->_getCacheId($id, $group);
 278  
 279          if (!$this->lockindex())
 280          {
 281              return false;
 282          }
 283  
 284          $index = static::$_db->get($this->_hash . '-index');
 285  
 286          if (\is_array($index))
 287          {
 288              foreach ($index as $key => $value)
 289              {
 290                  if ($value->name == $cache_id)
 291                  {
 292                      unset($index[$key]);
 293                      static::$_db->set($this->_hash . '-index', $index, 0);
 294                      break;
 295                  }
 296              }
 297          }
 298  
 299          $this->unlockindex();
 300  
 301          return static::$_db->delete($cache_id);
 302      }
 303  
 304      /**
 305       * Clean cache for a group given a mode.
 306       *
 307       * group mode    : cleans all cache in the group
 308       * notgroup mode : cleans all cache not in the group
 309       *
 310       * @param   string  $group  The cache data group
 311       * @param   string  $mode   The mode for cleaning cache [group|notgroup]
 312       *
 313       * @return  boolean
 314       *
 315       * @since   3.0.0
 316       */
 317  	public function clean($group, $mode = null)
 318      {
 319          if (!$this->lockindex())
 320          {
 321              return false;
 322          }
 323  
 324          $index = static::$_db->get($this->_hash . '-index');
 325  
 326          if (\is_array($index))
 327          {
 328              $prefix = $this->_hash . '-cache-' . $group . '-';
 329  
 330              foreach ($index as $key => $value)
 331              {
 332                  if (strpos($value->name, $prefix) === 0 xor $mode !== 'group')
 333                  {
 334                      static::$_db->delete($value->name);
 335                      unset($index[$key]);
 336                  }
 337              }
 338  
 339              static::$_db->set($this->_hash . '-index', $index, 0);
 340          }
 341  
 342          $this->unlockindex();
 343  
 344          return true;
 345      }
 346  
 347      /**
 348       * Flush all existing items in storage.
 349       *
 350       * @return  boolean
 351       *
 352       * @since   3.6.3
 353       */
 354  	public function flush()
 355      {
 356          if (!$this->lockindex())
 357          {
 358              return false;
 359          }
 360  
 361          return static::$_db->flush();
 362      }
 363  
 364      /**
 365       * Test to see if the storage handler is available.
 366       *
 367       * @return  boolean
 368       *
 369       * @since   3.0.0
 370       */
 371  	public static function isSupported()
 372      {
 373          /*
 374           * GAE and HHVM have both had instances where Memcached the class was defined but no extension was loaded.
 375           * If the class is there, we can assume support.
 376           */
 377          return class_exists('Memcached');
 378      }
 379  
 380      /**
 381       * Lock cached item
 382       *
 383       * @param   string   $id        The cache data ID
 384       * @param   string   $group     The cache data group
 385       * @param   integer  $locktime  Cached item max lock time
 386       *
 387       * @return  mixed  Boolean false if locking failed or an object containing properties lock and locklooped
 388       *
 389       * @since   3.0.0
 390       */
 391  	public function lock($id, $group, $locktime)
 392      {
 393          $returning = new \stdClass;
 394          $returning->locklooped = false;
 395  
 396          $looptime = $locktime * 10;
 397  
 398          $cache_id = $this->_getCacheId($id, $group);
 399  
 400          $data_lock = static::$_db->add($cache_id . '_lock', 1, $locktime);
 401  
 402          if ($data_lock === false)
 403          {
 404              $lock_counter = 0;
 405  
 406              // Loop until you find that the lock has been released.
 407              // That implies that data get from other thread has finished.
 408              while ($data_lock === false)
 409              {
 410                  if ($lock_counter > $looptime)
 411                  {
 412                      break;
 413                  }
 414  
 415                  usleep(100);
 416                  $data_lock = static::$_db->add($cache_id . '_lock', 1, $locktime);
 417                  $lock_counter++;
 418              }
 419  
 420              $returning->locklooped = true;
 421          }
 422  
 423          $returning->locked = $data_lock;
 424  
 425          return $returning;
 426      }
 427  
 428      /**
 429       * Unlock cached item
 430       *
 431       * @param   string  $id     The cache data ID
 432       * @param   string  $group  The cache data group
 433       *
 434       * @return  boolean
 435       *
 436       * @since   3.0.0
 437       */
 438  	public function unlock($id, $group = null)
 439      {
 440          $cache_id = $this->_getCacheId($id, $group) . '_lock';
 441          return static::$_db->delete($cache_id);
 442      }
 443  
 444      /**
 445       * Lock cache index
 446       *
 447       * @return  boolean
 448       *
 449       * @since   3.0.0
 450       */
 451  	protected function lockindex()
 452      {
 453          $looptime  = 300;
 454          $data_lock = static::$_db->add($this->_hash . '-index_lock', 1, 30);
 455  
 456          if ($data_lock === false)
 457          {
 458              $lock_counter = 0;
 459  
 460              // Loop until you find that the lock has been released.  that implies that data get from other thread has finished
 461              while ($data_lock === false)
 462              {
 463                  if ($lock_counter > $looptime)
 464                  {
 465                      return false;
 466                  }
 467  
 468                  usleep(100);
 469                  $data_lock = static::$_db->add($this->_hash . '-index_lock', 1, 30);
 470                  $lock_counter++;
 471              }
 472          }
 473  
 474          return true;
 475      }
 476  
 477      /**
 478       * Unlock cache index
 479       *
 480       * @return  boolean
 481       *
 482       * @since   3.0.0
 483       */
 484  	protected function unlockindex()
 485      {
 486          return static::$_db->delete($this->_hash . '-index_lock');
 487      }
 488  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer