xml(), strtotime($ttl)); } else { try { $xml = $data->xml; $dom = new DOMDocument(); $dom->loadXML($xml); // TODO: Find out why QP will not load a DB file directly. $qp = qp($dom); } catch (Exception $e) { drupal_set_message('Error: ' . check_plain($e->getMessage()), 'status'); } } return $qp; } /** * This is a special-purpose XML cache. * * It performs a different role than the built-in Drupal cache. It caches * XML documents for any given period. It is not cleared with the Drupal * cache. It uses a hashkey for optimal search speed across multiple database * types. * * The qpcache_* functions in this module are not mere wrappers around the * methods here. They add substantial logic on top of the bare caching layer. * Part of the logic is the key encoding logic. The QPCache class assumes that * keys are strings. The qpcache_* functions provide facilities for use objects * and arrays as keys, too. */ class QPCache { /** * Return true if the cache has this key. */ public static function has($key) { list($crc, $hash) = self::genMultiKey($key); $now = time(); $sql = 'SELECT COUNT(hashkey) FROM {qpcache_xmlcache} WHERE crckey=%d AND hashkey=\'%s\' AND (expire = 0 OR expire > %d)'; return db_result(db_query($sql, $crc, $hash, $now)) > 0; } /** * Return the value of the given key, if it exists in the database. * * If no value is found, this will return NULL. */ public static function get($key) { list($crc, $hash) = self::genMultiKey($key); $now = time(); // if (is_array($key)) { // drupal_set_message('Retrieval key is array.', 'status'); // } $sql = 'SELECT clearkey, expire, body FROM {qpcache_xmlcache} WHERE crckey=%d AND hashkey=\'%s\' AND (expire = 0 OR expire > %d)'; $object = db_fetch_object(db_query($sql, $crc, $hash, $now)); if (empty($object)) { return; } $object->xml = $object->body; return $object; } /** * Put a value in the cache. * * This will overwrite any existing entry with the same key. * * @param $key * A string value. * @param $body * The text value to store. * @param $expire * An expiration date in UNIX timestamp format. */ public static function set($key, $body, $expire = 0) { list($crckey, $hashkey) = self::genMultiKey($key); // foreach (array($key, $body, $expire) as $f) { // if (is_array($f)) { // drupal_set_message('WARNING: Array: ' . print_r($f, TRUE), 'status'); // } // } db_query("DELETE FROM {qpcache_xmlcache} WHERE hashkey = '%s'", $hashkey); db_query("INSERT INTO {qpcache_xmlcache} (crckey, hashkey, clearkey, body, expire) VALUES (%d, '%s', '%s', '%s', %d)", $crckey, $hashkey, $key, $body, $expire); } /** * Remove a single entry from the cache. * * This will remove the item whether it has expired or note. * * @param $key * The key of the item to be removed. */ public static function remove($key) { list($crckey, $hashkey) = self::genMultiKey($key); db_query("DELETE FROM {qpcache_xmlcache} WHERE hashkey = '%s'", $hashkey); } /** * Remove expired keys. */ public static function prune() { $now = time(); db_query('DELETE FROM {qpcache_xmlcache} WHERE expire BETWEEN 1 AND %d', $now); } /** * Empty the cache. */ public static function clear() { db_query('TRUNCATE TABLE {qpcache_xmlcache}'); } /** * Given the original key, generate a multi-key. * * For performance reasons, we use a composite key for retrieving entries. * This key uses a CRC-32 checksum against the original key plus an MD5 of * the original key. A CRC can be looked up in the database about 30 times faster * than an MD5. However, it has a much broader collision space. * So if indexing is done correctly, we can use the CRC to very quickly narrow, * and then use the MD5 to select the appropriate result from a very small set. *. * @return * List where position 0 is CRC and 1 is MD5 */ protected function genMultiKey($key) { return array( crc32($key), md5($key), ); } }