You are here

class Redis_Cache_PhpRedis in Redis 7.2

Same name and namespace in other branches
  1. 7.3 lib/Redis/Cache/PhpRedis.php \Redis_Cache_PhpRedis
  2. 7 lib/Redis/Cache/PhpRedis.php \Redis_Cache_PhpRedis

Predis cache backend.

Hierarchy

Expanded class hierarchy of Redis_Cache_PhpRedis

File

lib/Redis/Cache/PhpRedis.php, line 6

View source
class Redis_Cache_PhpRedis extends Redis_Cache_Base {
  function get($cid) {
    $client = Redis_Client::getClient();
    $key = $this
      ->getKey($cid);
    $cached = $client
      ->hgetall($key);

    // Recent versions of PhpRedis will return the Redis instance
    // instead of an empty array when the HGETALL target key does
    // not exists. I see what you did there.
    if (empty($cached) || !is_array($cached)) {
      return FALSE;
    }
    $cached = (object) $cached;
    if ($cached->serialized) {
      $cached->data = unserialize($cached->data);
    }
    return $cached;
  }
  function getMultiple(&$cids) {
    $client = Redis_Client::getClient();
    $ret = array();
    $keys = array_map(array(
      $this,
      'getKey',
    ), $cids);
    $pipe = $client
      ->multi(Redis::PIPELINE);
    foreach ($keys as $key) {
      $pipe
        ->hgetall($key);
    }
    $replies = $pipe
      ->exec();
    foreach ($replies as $reply) {
      if (!empty($reply)) {
        $cached = (object) $reply;
        if ($cached->serialized) {
          $cached->data = unserialize($cached->data);
        }
        $ret[$cached->cid] = $cached;
      }
    }
    foreach ($cids as $index => $cid) {
      if (isset($ret[$cid])) {
        unset($cids[$index]);
      }
    }
    return $ret;
  }
  function set($cid, $data, $expire = CACHE_PERMANENT) {
    $client = Redis_Client::getClient();
    $skey = $this
      ->getKey(Redis_Cache_Base::TEMP_SET);
    $key = $this
      ->getKey($cid);
    $hash = array(
      'cid' => $cid,
      'created' => time(),
      'expire' => $expire,
      'volatile' => (int) (CACHE_TEMPORARY === $expire),
    );

    // Let Redis handle the data types itself.
    if (!is_string($data)) {
      $hash['data'] = serialize($data);
      $hash['serialized'] = 1;
    }
    else {
      $hash['data'] = $data;
      $hash['serialized'] = 0;
    }
    $pipe = $client
      ->multi(Redis::PIPELINE);
    $pipe
      ->hmset($key, $hash);
    switch ($expire) {
      case CACHE_TEMPORARY:
        $lifetime = variable_get('cache_lifetime', Redis_Cache_Base::LIFETIME_DEFAULT);
        if (0 < $lifetime) {
          $pipe
            ->expire($key, $lifetime);
        }
        $pipe
          ->sadd($skey, $cid);
        break;
      case CACHE_PERMANENT:
        if (0 !== ($ttl = $this
          ->getPermTtl())) {
          $pipe
            ->expire($key, $ttl);
        }

        // We dont need the PERSIST command, since it's the default.
        break;
      default:

        // If caller gives us an expiry timestamp in the past
        // the key will expire now and will never be read.
        $ttl = $expire - time();
        if ($ttl < 0) {

          // Behavior between Predis and PhpRedis seems to change here: when
          // setting a negative expire time, PhpRedis seems to ignore the
          // command and leave the key permanent.
          $pipe
            ->expire($key, 0);
        }
        else {
          $pipe
            ->expire($key, $ttl);
          $pipe
            ->sadd($skey, $cid);
        }
        break;
    }
    $pipe
      ->exec();
  }
  protected function clearWithEval($cid = NULL, $wildcard = FALSE) {
    $client = Redis_Client::getClient();

    // @todo Should I restore the clear mode?
    if (NULL === $cid && FALSE === $wildcard) {

      // Flush volatile keys.
      // Per Drupal core definition, do not expire volatile keys
      // when a default cache lifetime is set.
      if (Redis_Cache_Base::LIFETIME_INFINITE == variable_get('cache_lifetime', Redis_Cache_Base::LIFETIME_DEFAULT)) {
        $ret = $client
          ->eval(self::EVAL_DELETE_VOLATILE, array(
          $this
            ->getKey('*'),
        ));
        if (1 != $ret) {
          trigger_error(sprintf("EVAL failed: %s", $client
            ->getLastError()), E_USER_ERROR);
        }
      }
    }
    else {
      if ('*' !== $cid && $wildcard) {

        // Flush by prefix.
        $ret = $client
          ->eval(self::EVAL_DELETE_PREFIX, array(
          $this
            ->getKey($cid . '*'),
        ));
        if (1 != $ret) {
          trigger_error(sprintf("EVAL failed: %s", $client
            ->getLastError()), E_USER_ERROR);
        }
      }
      else {
        if ('*' === $cid) {

          // Flush everything.
          $ret = $client
            ->eval(self::EVAL_DELETE_PREFIX, array(
            $this
              ->getKey('*'),
          ));
          if (1 != $ret) {
            trigger_error(sprintf("EVAL failed: %s", $client
              ->getLastError()), E_USER_ERROR);
          }
        }
        else {
          if (!$wildcard) {
            $client
              ->del($this
              ->getKey($cid));
          }
        }
      }
    }
  }
  protected function clearWithoutEval($cid = NULL, $wildcard = FALSE) {
    $keys = array();
    $skey = $this
      ->getKey(Redis_Cache_Base::TEMP_SET);
    $client = Redis_Client::getClient();
    if (NULL === $cid) {
      switch ($this
        ->getClearMode()) {

        // One and only case of early return.
        case Redis_Cache_Base::FLUSH_NOTHING:
          return;

        // Default behavior.
        case Redis_Cache_Base::FLUSH_TEMPORARY:
          if (Redis_Cache_Base::LIFETIME_INFINITE == variable_get('cache_lifetime', Redis_Cache_Base::LIFETIME_DEFAULT)) {
            $keys[] = $skey;
            foreach ($client
              ->smembers($skey) as $tcid) {
              $keys[] = $this
                ->getKey($tcid);
            }
          }
          break;

        // Fallback on most secure mode: flush full bin.
        default:
        case Redis_Cache_Base::FLUSH_ALL:
          $keys[] = $skey;
          $cid = '*';
          $wildcard = true;
          break;
      }
    }
    if ('*' !== $cid && $wildcard) {

      // Prefix flush.
      $remoteKeys = $client
        ->keys($this
        ->getKey($cid . '*'));

      // PhpRedis seems to suffer of some bugs.
      if (!empty($remoteKeys) && is_array($remoteKeys)) {
        $keys = array_merge($keys, $remoteKeys);
      }
    }
    else {
      if ('*' === $cid) {

        // Full bin flush.
        $remoteKeys = $client
          ->keys($this
          ->getKey('*'));

        // PhpRedis seems to suffer of some bugs.
        if (!empty($remoteKeys) && is_array($remoteKeys)) {
          $keys = array_merge($keys, $remoteKeys);
        }
      }
      else {
        if (empty($keys) && !empty($cid)) {

          // Single key drop.
          $keys[] = $key = $this
            ->getKey($cid);
          $client
            ->srem($skey, $key);
        }
      }
    }
    if (!empty($keys)) {
      if (count($keys) < Redis_Cache_Base::KEY_THRESHOLD) {
        $client
          ->del($keys);
      }
      else {
        $pipe = $client
          ->multi(Redis::PIPELINE);
        do {
          $buffer = array_splice($keys, 0, Redis_Cache_Base::KEY_THRESHOLD);
          $pipe
            ->del($buffer);
        } while (!empty($keys));
        $pipe
          ->exec();
      }
    }
  }
  function clear($cid = NULL, $wildcard = FALSE) {
    if ($this
      ->canUseEval()) {
      $this
        ->clearWithEval($cid, $wildcard);
    }
    else {
      $this
        ->clearWithoutEval($cid, $wildcard);
    }
  }
  function isEmpty() {

    // FIXME: Todo.
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Redis_AbstractBackend::$globalPrefix protected static property
Redis_AbstractBackend::$prefix private property
Redis_AbstractBackend::getClient public function Get redis client
Redis_AbstractBackend::getDefaultPrefix public static function Get global default prefix
Redis_AbstractBackend::getGlobalPrefix public static function Get site default global prefix
Redis_AbstractBackend::getPrefix final public function Get prefix
Redis_AbstractBackend::KEY_SEPARATOR constant Key components name separator
Redis_AbstractBackend::setPrefix final public function Set prefix
Redis_Cache_Base::$bin protected property
Redis_Cache_Base::$clearMode protected property
Redis_Cache_Base::$permTtl protected property Default TTL for CACHE_PERMANENT items.
Redis_Cache_Base::$useEval protected property Can this instance use EVAL.
Redis_Cache_Base::canUseEval public function Tell if the backend can use EVAL commands.
Redis_Cache_Base::EVAL_DELETE_PREFIX constant Delete by prefix lua script
Redis_Cache_Base::EVAL_DELETE_VOLATILE constant Delete volatile by prefix lua script
Redis_Cache_Base::FLUSH_ALL constant Flush all on generic clear().
Redis_Cache_Base::FLUSH_NOTHING constant Flush nothing on generic clear().
Redis_Cache_Base::FLUSH_TEMPORARY constant Flush only temporary on generic clear().
Redis_Cache_Base::getClearMode public function Get clear mode.
Redis_Cache_Base::getKey public function Get full key name using the set prefix Overrides Redis_AbstractBackend::getKey
Redis_Cache_Base::getPermTtl public function Get TTL for CACHE_PERMANENT items.
Redis_Cache_Base::KEY_THRESHOLD constant Computed keys are let's say arround 60 characters length due to key prefixing, which makes 1,000 keys DEL command to be something arround 50,000 bytes length: this is huge and may not pass into Redis, let's split this off. Some recommend to…
Redis_Cache_Base::LIFETIME_DEFAULT constant Default temporary cache items lifetime.
Redis_Cache_Base::LIFETIME_INFINITE constant Temporary cache items lifetime is infinite.
Redis_Cache_Base::LIFETIME_PERM_DEFAULT constant Default lifetime for permanent items. Approximatively 1 year.
Redis_Cache_Base::TEMP_SET constant Temporary items SET name.
Redis_Cache_Base::__construct public function Default constructor Overrides Redis_AbstractBackend::__construct
Redis_Cache_PhpRedis::clear function Expires data from the cache. Overrides DrupalCacheInterface::clear
Redis_Cache_PhpRedis::clearWithEval protected function
Redis_Cache_PhpRedis::clearWithoutEval protected function
Redis_Cache_PhpRedis::get function Returns data from the persistent cache. Overrides DrupalCacheInterface::get
Redis_Cache_PhpRedis::getMultiple function Returns data from the persistent cache when given an array of cache IDs. Overrides DrupalCacheInterface::getMultiple
Redis_Cache_PhpRedis::isEmpty function Checks if a cache bin is empty. Overrides DrupalCacheInterface::isEmpty
Redis_Cache_PhpRedis::set function Stores data in the persistent cache. Overrides DrupalCacheInterface::set