You are here

class APDQCache in Asynchronous Prefetch Database Query Cache 7

A pretty darn quick cache implementation of Drupal's default cache backend.

This cache backend extends Drupal's default database cache implementation by altering how drupal interacts with the database.

Hierarchy

Expanded class hierarchy of APDQCache

See also

http://drupal.org/node/891600

20 string references to 'APDQCache'
APDQCache::getCaller in ./apdqc.cache.inc
Get the last 3 items from debug_backtrace.
ApdqcCacheClearCase::setUp in ./apdqc.test
Install the apdqc module and setup the cache.
ApdqcCacheTestCase::setUp in ./apdqc.test
Setup for tests.
ApdqcPrefetchDrupalDefaultEntityController::__call in ./apdqc.module
Magic method; forward all calls to the original base class.
apdqc_async_data in ./apdqc.mysql.inc
Used to get & parse async cached data.

... See full list

File

./apdqc.cache.inc, line 261
Extends Drupal's default database cache so async queries happen.

View source
class APDQCache extends DrupalDatabaseCache {
  protected $compressed;
  protected $compression_level;

  /**
   * Constructs a DrupalDatabaseCache object.
   *
   * @param string $bin
   *   The cache bin for which the object is created.
   */
  public function __construct($bin) {

    // Load the correct database backend.
    $db_type = apdqc_fast_get_db_type();

    // Async queries are only available in PHP 5.3+.
    if ($db_type === 'mysql' && defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 50300) {
      require_once 'apdqc.mysql.inc';
    }
    $this->compressed = $this
      ->apdqcCacheCompressBin($bin);
    if ($this->compressed) {
      $this->compression_level = $this
        ->apdqcCacheCompressionLevelBin($bin);
    }
    parent::__construct($bin);
  }

  /**
   * Checks if this cache bin is compressed or not.
   *
   * @param string $bin
   *   The cache bin name.
   *
   * @return bool
   *   TRUE if this cache bin is compressed.
   */
  protected function apdqcCacheCompressBin($bin) {
    $compress = variable_get('apdqc_cache_compress_' . $bin);
    if (!isset($compress)) {
      $compress = variable_get('apdqc_cache_default_compress', APDQC_CACHE_DEFAULT_COMPRESS);
    }
    return $compress;
  }

  /**
   * Checks if this cache bin is compressed or not.
   *
   * @param string $bin
   *   The cache bin name.
   *
   * @return bool
   *   TRUE if this cache bin is compressed.
   */
  protected function apdqcCacheCompressionLevelBin($bin) {
    $compress = variable_get('apdqc_cache_compression_level_' . $bin);
    if (!isset($compress)) {
      $compress = variable_get('apdqc_cache_default_compression_level', APDQC_CACHE_DEFAULT_COMPRESSION_LEVEL);
    }
    return $compress;
  }

  /**
   * Implements DrupalCacheInterface::getMultiple().
   *
   * We handle garbage collection differently; this method is reimplemented only
   * to remove a call to the garbageCollection() method.
   */
  public function getMultiple(&$cids) {
    try {
      $gc_frequency = variable_get('cache_garbage_collection_frequency', CACHE_GARBAGE_COLLECTION_FREQUENCY);
      if (empty($gc_frequency)) {

        // Garbage collection necessary when enforcing a minimum cache lifetime.
        $this
          ->garbageCollection($this->bin);
      }
      $query_cids = $cids;
      $prefetch = array();
      $result = array();
      if (variable_get('cache_bootstrap_prefetch', CACHE_BOOTSTRAP_PREFETCH) || defined('APDQC_PREFETCH') && variable_get('apdqc_prefetch', APDQC_PREFETCH)) {
        $prefetch = apdqc_async_data(FALSE, $this->bin, $cids);
        $query_cids = array_diff($cids, array_keys($prefetch));
        $prefetch = array_values($prefetch);
      }
      $result = array();
      if (!empty($query_cids)) {

        // Build query.
        $query = Database::getConnection()
          ->prefixTables("SELECT cid, data, created, expire, serialized FROM {" . db_escape_table($this->bin) . "}");

        // If this is the cache_bootstrap bin and the cid is variables, get
        // the bootstrap_modules & lookup_cache ids as well.
        if ($this->bin === 'cache_bootstrap' && $query_cids[0] === 'variables' && count($query_cids) == 1) {
          $query_cids[] = 'bootstrap_modules';
          $query_cids[] = 'lookup_cache';
          $query_cids[] = 'system_list';
          $query_cids[] = 'module_implements';
          $bootstrap = TRUE;
        }
        foreach ($query_cids as $cid) {
          $escaped_cids[] = apdqc_escape_string($cid);
        }
        $escaped_cids = "'" . implode("', '", $escaped_cids) . "'";
        $query .= " WHERE cid IN ({$escaped_cids})";

        // Run query.
        $result = apdqc_query(array(
          $this->bin,
        ), $query_cids, $query, array(
          'fetch_all' => TRUE,
        ));
      }
      if (is_string($result) && $result === 'NO DB') {

        // When serving cached pages, the overhead of using db_select() was
        // found to add around 30% overhead to the request. Since $this->bin is
        // a variable, this means the call to db_query() here uses a
        // concatenated string. This is highly discouraged under any other
        // circumstances, and is used here only due to the performance overhead
        // we would incur otherwise. When serving an uncached page, the overhead
        // of using db_select() is a much smaller proportion of the request.
        // @codingStandardsIgnoreLine
        $result = db_query('SELECT cid, data, created, expire, serialized FROM {' . db_escape_table($this->bin) . '} WHERE cid IN (:cids)', array(
          ':cids' => $cids,
        ));
      }
      else {
        $result = array_merge($result, $prefetch);
      }
      $cache = array();
      if (!empty($result)) {
        foreach ($result as $item) {

          // Skip empty items.
          if (empty($item)) {
            continue;
          }
          if (!empty($bootstrap) && is_array($item) && ($item['cid'] === 'bootstrap_modules' || $item['cid'] === 'lookup_cache' || $item['cid'] === 'system_list' || $item['cid'] === 'module_implements')) {
            $local_storage =& drupal_static('apdqc_async_data');
            $item['bin'] = $this->bin;
            $local_storage[$this->bin][$item['cid']] = $item;
            continue;
          }

          // Convert to an object to mirror db_query if needed.
          if (is_array($item)) {
            $item = (object) $item;
          }
          $item = $this
            ->prepareItem($item);
          if ($item) {
            $cache[$item->cid] = $item;
          }
        }
      }
      $cids = array_diff($cids, array_keys($cache));
      return $cache;
    } catch (Exception $e) {

      // If the database is never going to be available, cache requests should
      // return FALSE in order to allow exception handling to occur.
      return array();
    }
  }

  /**
   * Prepares a cached item.
   *
   * Checks that items are either permanent or did not expire, and unserializes
   * data as appropriate.
   *
   * @param object $cache
   *   An item loaded from cache_get() or cache_get_multiple().
   *
   * @return mixed
   *   The item with data unserialized as appropriate or FALSE if there is no
   *   valid item to load.
   */
  protected function prepareItem($cache) {
    if (!isset($cache->data)) {
      return FALSE;
    }

    // If the cached data is temporary and subject to a per-user minimum
    // lifetime, compare the cache entry timestamp with the user session
    // cache_expiration timestamp. If the cache entry is too old, ignore it.
    if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && isset($_SESSION['cache_expiration'][$this->bin]) && $_SESSION['cache_expiration'][$this->bin] > $cache->created) {

      // Ignore cache data that is too old and thus not valid for this user.
      return FALSE;
    }

    // Inflate and unserialize data.
    apdqc_inflate_unserialize($cache);
    return $cache;
  }

  /**
   * Generic garbage collection method.
   *
   * Rather than respecting minimum cache lifetime at the bin-level, we respect
   * it on an entry-by-entry basis.
   *
   * Additionally, we've moved session cache handling to a separate method.
   */
  protected function garbageCollection() {
    $cache_lifetime = variable_get('cache_lifetime', 0);
    $gc_frequency = variable_get('cache_garbage_collection_frequency', CACHE_GARBAGE_COLLECTION_FREQUENCY);
    if (empty($gc_frequency)) {
      $cache_lifetime = variable_get('cache_lifetime', 0);

      // Clean-up the per-user cache expiration session data, so that the
      // session handler can properly clean-up the session data for anonymous
      // users.
      if (isset($_SESSION['cache_expiration'])) {
        $expire = REQUEST_TIME - $cache_lifetime;
        foreach ($_SESSION['cache_expiration'] as $bin => $timestamp) {
          if ($timestamp < $expire) {
            unset($_SESSION['cache_expiration'][$bin]);
          }
        }
        if (!$_SESSION['cache_expiration']) {
          unset($_SESSION['cache_expiration']);
        }
      }

      // Garbage collection of temporary items is only necessary when enforcing
      // a minimum cache lifetime.
      if (!$cache_lifetime) {
        return;
      }

      // When cache lifetime is in force, avoid running garbage collection too
      // often since this will remove temporary cache items indiscriminately.
      $cache_flush = variable_get('cache_flush_' . $this->bin, 0);
      if ($cache_flush && $cache_flush + $cache_lifetime <= REQUEST_TIME) {

        // Reset the variable immediately to prevent a meltdown in heavy load
        // situations.
        variable_set('cache_flush_' . $this->bin, 0);

        // Time to flush old cache data
        // Build query.
        $query = Database::getConnection()
          ->prefixTables("DELETE FROM {" . db_escape_table($this->bin) . "} ");
        $query .= "WHERE (expire <> " . CACHE_PERMANENT . " AND expire <= " . $cache_flush . ")";

        // Run query.
        $result = apdqc_query(array(
          $this->bin,
        ), array(
          '*',
        ), $query, array(
          'async' => TRUE,
        ));
        if (is_string($result) && $result === 'NO DB') {

          // Use the DrupalDatabaseCache class.
          return parent::garbageCollection();
        }
      }
    }
    else {

      // Build query.
      $query = Database::getConnection()
        ->prefixTables("DELETE FROM {" . db_escape_table($this->bin) . "} ");
      if (!empty($cache_lifetime)) {

        // Clear expired items from the cache.
        $query .= "WHERE (expire > " . CACHE_PERMANENT . " AND expire < " . REQUEST_TIME . ")";

        // Clear CACHE_TEMPORARY items that are older than cache_lifetime.
        $query .= "OR (expire = " . CACHE_TEMPORARY . " AND created < " . apdqc_escape_string(REQUEST_TIME - $cache_lifetime) . ")";
      }
      else {

        // No minimum lifetime. Flush all expired and temporary cache entries.
        $query .= "WHERE (expire <> " . CACHE_PERMANENT . " AND expire < " . REQUEST_TIME . ")";
      }

      // Run query.
      $result = apdqc_query(array(
        $this->bin,
      ), array(
        '*',
      ), $query, array(
        'async' => TRUE,
      ));
      if (is_string($result) && $result === 'NO DB') {
        if (!empty($cache_lifetime)) {

          // Clear expired items from the cache.
          db_delete($this->bin)
            ->condition('expire', CACHE_PERMANENT, '>')
            ->condition('expire', REQUEST_TIME, '<')
            ->execute();

          // Clear CACHE_TEMPORARY items that are older than cache_lifetime.
          db_delete($this->bin)
            ->condition('expire', CACHE_TEMPORARY)
            ->condition('created', REQUEST_TIME - $cache_lifetime, '<')
            ->execute();
        }
        else {

          // No minimum lifetime. Flush all expired and temporary cache entries.
          db_delete($this->bin)
            ->condition('expire', CACHE_PERMANENT, '<>')
            ->condition('expire', REQUEST_TIME, '<')
            ->execute();
        }
      }
    }
  }

  /**
   * Cleans up the per-session cache expiration data.
   */
  protected function cleanSession() {
    $cache_lifetime = variable_get('cache_lifetime', 0);
    if (isset($_SESSION['cache_expiration'])) {
      $expire = REQUEST_TIME - $cache_lifetime;
      foreach ($_SESSION['cache_expiration'] as $bin => $timestamp) {
        if ($timestamp < $expire) {
          unset($_SESSION['cache_expiration'][$bin]);
        }
      }
      if (!$_SESSION['cache_expiration']) {
        unset($_SESSION['cache_expiration']);
      }
    }
  }

  /**
   * Implements DrupalCacheInterface::set().
   */
  public function set($cid, $data, $expire = CACHE_PERMANENT) {

    // When setting cache items, clean up old session data in case it is stale.
    $gc_frequency = variable_get('cache_garbage_collection_frequency', CACHE_GARBAGE_COLLECTION_FREQUENCY);
    if (!empty($gc_frequency)) {
      $this
        ->cleanSession();
    }
    $fields = array(
      'serialized' => 0,
      'created' => REQUEST_TIME,
      'expire' => $expire,
    );
    if (!is_string($data)) {
      $fields['data'] = serialize($data);
      $fields['serialized'] = 1;
    }
    else {
      $fields['data'] = $data;
      $fields['serialized'] = 0;
    }
    if ($this->compressed > 1 || $this->compressed && $fields['serialized']) {
      $deflate = gzdeflate($fields['data'], $this->compression_level);
      if (strlen($deflate) < strlen($fields['data'])) {
        $fields['data'] = $deflate;
        $fields['serialized'] += 2;
      }
    }
    try {

      // Build query.
      $escaped_cid = apdqc_escape_string($cid);
      $escaped_data = apdqc_escape_string($fields['data']);
      $query = Database::getConnection()
        ->prefixTables("INSERT INTO {" . db_escape_table($this->bin) . "}");
      $query .= " (cid, serialized, created, expire, data) VALUES ('{$escaped_cid}', '{$fields['serialized']}', '{$fields['created']}', '{$fields['expire']}', '{$escaped_data}')";
      $query .= " ON DUPLICATE KEY UPDATE serialized = '{$fields['serialized']}', created = '{$fields['created']}', expire = '{$fields['expire']}', data = '{$escaped_data}'";

      // Run query.
      $result = apdqc_query(array(
        $this->bin,
      ), array(
        $cid,
      ), $query, array(
        'async' => TRUE,
      ));
      if (is_string($result) && $result === 'NO DB') {
        return parent::set($cid, $data, $expire);
      }
    } catch (Exception $e) {

      // The database may not be available, so we'll ignore cache_set requests.
    }
  }

  /**
   * Implements DrupalCacheInterface::clear().
   *
   * Here, we ensure that cron only runs cache garbage collection at a
   * configurable frequency, defaulting to 24 hours.
   */
  public function clear($cid = NULL, $wildcard = FALSE) {
    if (variable_get('apdqc_call_hook_on_clear', APDQC_CALL_HOOK_ON_CLEAR)) {
      $apdqc_cache_clear_alter = module_implements('apdqc_cache_clear_alter');
      if (!empty($apdqc_cache_clear_alter)) {
        $caller = $this
          ->getCaller();

        // The bin variable is not alterable; use a copy.
        $bin = $this->bin;

        // Call hook_apdqc_cache_clear_alter().
        drupal_alter('apdqc_cache_clear', $cid, $wildcard, $bin, $caller);
      }
    }

    // Use default core logic for this cache clear if inside of a transaction.
    if (Database::getConnection()
      ->inTransaction()) {

      // Do not use any prefectched data.
      $apdqc_async_data =& drupal_static('apdqc_async_data');
      $do_not_use_async_data =& drupal_static('apdqc_async_data_do_not_use_async');
      $do_not_run_prefetch_array =& drupal_static('apdqc_run_prefetch_array');
      $apdqc_async_data[$this->bin] = array();
      $do_not_use_async_data[$this->bin] = TRUE;
      $do_not_run_prefetch_array[$this->bin] = TRUE;
      $output = parent::clear($cid, $wildcard);
      $this
        ->callCacheClearHooks($cid, $wildcard);
      return $output;
    }
    $gc_frequency = variable_get('cache_garbage_collection_frequency', CACHE_GARBAGE_COLLECTION_FREQUENCY);
    $cids = array(
      $cid,
    );
    if ($wildcard || is_null($cid)) {
      $cids = array(
        '*',
      );
    }
    if (!empty($gc_frequency) && empty($cid)) {

      // Use backtrace to check that the clear came from system_cron.
      $backtrace = debug_backtrace();
      if ($backtrace[2]['function'] == 'system_cron') {
        $cache_lifetime = variable_get('cache_lifetime', 0);
        $name = 'cache_garbage_collect_' . $this->bin;
        $window = max($cache_lifetime, $gc_frequency);
        if (flood_is_allowed($name, 1, $window, 'cron')) {
          $this
            ->garbageCollection();
          flood_register_event($name, $window, 'cron');
        }
      }
      else {
        $this
          ->garbageCollection();
      }
      $this
        ->callCacheClearHooks($cid, $wildcard);
      return;
    }
    if (is_null($cid)) {

      // Build query.
      $query = Database::getConnection()
        ->prefixTables("DELETE FROM {" . db_escape_table($this->bin) . "} ");
      if (variable_get('cache_lifetime', 0)) {

        // We store the time in the current user's session. We then simulate
        // that the cache was flushed for this user by not returning cached
        // data that was cached before the timestamp.
        $_SESSION['cache_expiration'][$this->bin] = REQUEST_TIME;
        $cache_flush = variable_get('cache_flush_' . $this->bin, 0);
        if ($cache_flush == 0) {

          // This is the first request to clear the cache, start a timer.
          variable_set('cache_flush_' . $this->bin, REQUEST_TIME);
        }
        elseif (REQUEST_TIME > $cache_flush + variable_get('cache_lifetime', 0)) {

          // Clear the cache for everyone, cache_lifetime seconds have
          // passed since the first request to clear the cache.
          $query .= "WHERE (expire <> " . CACHE_PERMANENT . " AND expire < " . REQUEST_TIME . ")";
          $result = apdqc_query(array(
            $this->bin,
          ), $cids, $query, array(
            'async' => TRUE,
          ));
          if (is_string($result) && $result === 'NO DB') {

            // Use core connection if the additional connection to mysql fails.
            $output = parent::clear($cid, $wildcard);
            $this
              ->callCacheClearHooks($cid, $wildcard);
            return $output;
          }
          variable_set('cache_flush_' . $this->bin, 0);
        }
      }
      else {

        // No minimum cache lifetime, flush all temporary cache entries now.
        $query .= "WHERE (expire <> " . CACHE_PERMANENT . " AND expire < " . REQUEST_TIME . ")";
        $result = apdqc_query(array(
          $this->bin,
        ), $cids, $query, array(
          'async' => TRUE,
        ));
        if (is_string($result) && $result === 'NO DB') {

          // Use core connection if an additional connection to mysql fails.
          $output = parent::clear($cid, $wildcard);
          $this
            ->callCacheClearHooks($cid, $wildcard);
          return $output;
        }
      }
    }
    else {
      if ($wildcard) {
        if ($cid == '*') {

          // Check if $this->bin is a cache table before truncating. Other
          // cache_clear_all() operations throw a PDO error in this situation,
          // so we don't need to verify them first. This ensures that non-cache
          // tables cannot be truncated accidentally.
          if ($this
            ->isValidBin()) {
            apdqc_truncate_table($this->bin);
          }
          else {
            throw new Exception(t('Invalid or missing cache bin specified: %bin', array(
              '%bin' => $this->bin,
            )));
          }
        }
        else {

          // Build query.
          $query = Database::getConnection()
            ->prefixTables("DELETE FROM {" . db_escape_table($this->bin) . "} ");
          db_delete($this->bin)
            ->condition('cid', db_like($cid) . '%', 'LIKE')
            ->execute();
          $escaped_cid = apdqc_escape_string($cid);
          $query .= " WHERE cid LIKE '{$escaped_cid}%'";

          // Run an async query.
          $result = apdqc_query(array(
            $this->bin,
          ), $cids, $query, array(
            'async' => TRUE,
          ));
          if (is_string($result) && $result === 'NO DB') {

            // Use core connection if an additional connection to mysql fails.
            $output = parent::clear($cid, $wildcard);
            $this
              ->callCacheClearHooks($cid, $wildcard);
            return $output;
          }
        }
      }
      elseif (is_array($cid)) {

        // Delete in chunks when a large array is passed.
        $chunks = array_chunk($cid, 2000);
        $last = array_pop($chunks);
        $query = Database::getConnection()
          ->prefixTables("DELETE FROM {" . db_escape_table($this->bin) . "} ");
        foreach ($chunks as $cids) {
          $escaped_cids = array();
          foreach ($cids as $id) {
            $escaped_cids[] = apdqc_escape_string($id);
          }
          $escaped_cids = "'" . implode("', '", $escaped_cids) . "'";

          // Run query.
          $result = apdqc_query(array(
            $this->bin,
          ), $cids, $query . " WHERE cid IN ({$escaped_cids})");
          if (is_string($result) && $result === 'NO DB') {

            // Use core connection if an additional connection to mysql fails.
            $output = parent::clear($cid, $wildcard);
            $this
              ->callCacheClearHooks($cid, $wildcard);
            return $output;
          }
        }
        $escaped_cids = array();
        foreach ($last as $id) {
          $escaped_cids[] = apdqc_escape_string($id);
        }
        $escaped_cids = "'" . implode("', '", $escaped_cids) . "'";

        // Run last one as an async query.
        $result = apdqc_query(array(
          $this->bin,
        ), $last, $query . " WHERE cid IN ({$escaped_cids})", array(
          'async' => TRUE,
        ));
        if (is_string($result) && $result === 'NO DB') {

          // Use core connection if an additional connection to mysql fails.
          $output = parent::clear($cid, $wildcard);
          $this
            ->callCacheClearHooks($cid, $wildcard);
          return $output;
        }
      }
      else {
        $query = Database::getConnection()
          ->prefixTables("DELETE FROM {" . db_escape_table($this->bin) . "} ");
        $result = apdqc_query(array(
          $this->bin,
        ), $cids, $query . " WHERE cid = '" . apdqc_escape_string($cid) . "'", array(
          'async' => TRUE,
        ));
        if (is_string($result) && $result === 'NO DB') {

          // Use core connection if an additional connection to mysql fails.
          $output = parent::clear($cid, $wildcard);
          $this
            ->callCacheClearHooks($cid, $wildcard);
          return $output;
        }
      }
    }
    $this
      ->callCacheClearHooks($cid, $wildcard);
  }

  /**
   * Implements DrupalCacheInterface::isEmpty().
   */
  public function isEmpty() {
    $this
      ->garbageCollection();
    $real_table_name = Database::getConnection()
      ->prefixTables("{" . db_escape_table($this->bin) . "}");
    $result = apdqc_query(array(
      $this->bin,
    ), array(), "SELECT TRUE FROM {$real_table_name} LIMIT 1");
    if (is_string($result) && $result === 'NO DB') {
      return parent::isEmpty();
    }
    if (!empty($result) && $result instanceof mysqli_result) {
      $empty_table = $result
        ->fetch_row();
    }
    if (empty($result) || empty($empty_table)) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Checks if $this->bin represents a valid cache table.
   *
   * This check is required to ensure that non-cache tables are not truncated
   * accidentally when calling cache_clear_all().
   *
   * @see https://www.drupal.org/node/2421253
   *
   * @return bool
   *   True if this is a valid bin; false if not.
   */
  public function isValidBin() {
    return parent::isValidBin();
  }

  /**
   * Allow for 3rd party modules to react to cache clear calls.
   *
   * @param string $cid
   *   Cache ID.
   * @param bool $wildcard
   *   If TRUE then anything after the CID is a match.
   */
  public function callCacheClearHooks($cid, $wildcard) {
    if (!variable_get('apdqc_call_hook_on_clear', APDQC_CALL_HOOK_ON_CLEAR)) {
      return;
    }
    $apdqc_cache_clear = module_implements('apdqc_cache_clear');
    if (empty($apdqc_cache_clear)) {

      // Bail out if no hooks will be called.
      return;
    }
    $caller = $this
      ->getCaller();

    // Call hook_apdqc_cache_clear().
    module_invoke_all('apdqc_cache_clear', $cid, $wildcard, $this->bin, $caller);
  }

  /**
   * Get the last 3 items from debug_backtrace.
   *
   * @return array
   *   An array of items from debug_backtrace.
   */
  public function getCaller() {
    $bt = debug_backtrace();
    $caller = array();
    foreach ($bt as $key => $call) {
      if (!empty($call['class']) && $call['class'] == 'APDQCache') {
        continue;
      }
      if (empty($call['function']) || $call['function'] == 'cache_clear_all') {
        continue;
      }

      // Get this and the next 2 levels from the backtrace.
      $caller[$key] = $call;
      if (isset($bt[$key + 1])) {
        $caller[$key + 1] = $bt[$key + 1];
        if (isset($bt[$key + 2])) {
          $caller[$key + 2] = $bt[$key + 2];
        }
      }
      break;
    }
    return $caller;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
APDQCache::$compressed protected property
APDQCache::$compression_level protected property
APDQCache::apdqcCacheCompressBin protected function Checks if this cache bin is compressed or not.
APDQCache::apdqcCacheCompressionLevelBin protected function Checks if this cache bin is compressed or not.
APDQCache::callCacheClearHooks public function Allow for 3rd party modules to react to cache clear calls.
APDQCache::cleanSession protected function Cleans up the per-session cache expiration data.
APDQCache::clear public function Implements DrupalCacheInterface::clear(). Overrides DrupalDatabaseCache::clear
APDQCache::garbageCollection protected function Generic garbage collection method. Overrides DrupalDatabaseCache::garbageCollection
APDQCache::getCaller public function Get the last 3 items from debug_backtrace.
APDQCache::getMultiple public function Implements DrupalCacheInterface::getMultiple(). Overrides DrupalDatabaseCache::getMultiple
APDQCache::isEmpty public function Implements DrupalCacheInterface::isEmpty(). Overrides DrupalDatabaseCache::isEmpty
APDQCache::isValidBin public function Checks if $this->bin represents a valid cache table. Overrides DrupalDatabaseCache::isValidBin
APDQCache::prepareItem protected function Prepares a cached item. Overrides DrupalDatabaseCache::prepareItem
APDQCache::set public function Implements DrupalCacheInterface::set(). Overrides DrupalDatabaseCache::set
APDQCache::__construct public function Constructs a DrupalDatabaseCache object. Overrides DrupalDatabaseCache::__construct
DrupalDatabaseCache::$bin protected property
DrupalDatabaseCache::get function Implements DrupalCacheInterface::get(). Overrides DrupalCacheInterface::get 1