]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - lib/cache.php
Merge branch '0.9.x'
[quix0rs-gnu-social.git] / lib / cache.php
index d1ba65dab8b1506002cf3d6066342f2832cb1133..bf0603c62d0ce15c1f1401351668a7d7b48946b6 100644 (file)
 /**
  * Interface for caching
  *
- * An abstract interface for caching.
+ * An abstract interface for caching. Because we originally used the
+ * Memcache plugin directly, the interface uses a small subset of the
+ * Memcache interface.
  *
+ * @category  Cache
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
  */
-
 class Cache
 {
-    var $_items = array();
+    var $_items   = array();
     static $_inst = null;
 
+    const COMPRESSED = 1;
+
+    /**
+     * Singleton constructor
+     *
+     * Use this to get the singleton instance of Cache.
+     *
+     * @return Cache cache object
+     */
     static function instance()
     {
         if (is_null(self::$_inst)) {
@@ -48,9 +64,20 @@ class Cache
         return self::$_inst;
     }
 
+    /**
+     * Create a cache key from input text
+     *
+     * Builds a cache key from input text. Helps to namespace
+     * the cache area (if shared with other applications or sites)
+     * and prevent conflicts.
+     *
+     * @param string $extra the real part of the key
+     *
+     * @return string full key
+     */
     static function key($extra)
     {
-        $base_key = common_config('memcached', 'base');
+        $base_key = common_config('cache', 'base');
 
         if (empty($base_key)) {
             $base_key = common_keyize(common_config('site', 'name'));
@@ -59,6 +86,64 @@ class Cache
         return 'statusnet:' . $base_key . ':' . $extra;
     }
 
+    /**
+     * Create a cache key for data dependent on code
+     *
+     * For cache elements that are dependent on changes in code, this creates
+     * a more-or-less fingerprint of the current running code and adds it to
+     * the cache key. In the case of an upgrade of core, or addition or
+     * removal of plugins, a new unique fingerprint is generated and used.
+     * 
+     * There can still be problems with a) differences in versions of the 
+     * plugins and b) people running code between official versions. This is
+     * usually a problem only for experienced users like developers, who know
+     * how to clear their cache.
+     *
+     * For sites that run code between versions (like the status.net cloud),
+     * there's an additional build number configuration setting.
+     * 
+     * @param string $extra the real part of the key
+     *
+     * @return string full key
+     */
+    
+    static function codeKey($extra)
+    {
+        static $prefix = null;
+       
+        if (empty($prefix)) {
+           
+            $plugins     = StatusNet::getActivePlugins();
+            $names       = array();
+           
+            foreach ($plugins as $plugin) {
+                $names[] = $plugin[0];
+            }
+           
+            $names = array_unique($names);
+            asort($names);
+           
+            // Unique enough.
+       
+            $uniq = crc32(implode(',', $names));
+
+            $build = common_config('site', 'build');
+
+            $prefix = STATUSNET_VERSION.':'.$build.':'.$uniq;
+        }
+       
+        return Cache::key($prefix.':'.$extra);
+    }
+    
+    /**
+     * Make a string suitable for use as a key
+     *
+     * Useful for turning primary keys of tables into cache keys.
+     *
+     * @param string $str string to turn into a key
+     *
+     * @return string keyized string
+     */
     static function keyize($str)
     {
         $str = strtolower($str);
@@ -66,16 +151,23 @@ class Cache
         return $str;
     }
 
+    /**
+     * Get a value associated with a key
+     *
+     * The value should have been set previously.
+     *
+     * @param string $key Lookup key
+     *
+     * @return string retrieved value or null if unfound
+     */
     function get($key)
     {
-        $value = null;
-
-        if (!Event::handle('StartCacheGet', array(&$key, &$value))) {
-            if (array_key_exists($_items, $key)) {
-                common_log(LOG_INFO, 'Cache HIT for key ' . $key);
-                $value = $_items[$key];
-            } else {
-                common_log(LOG_INFO, 'Cache MISS for key ' . $key);
+        $value = false;
+
+        common_perf_counter('Cache::get', $key);
+        if (Event::handle('StartCacheGet', array(&$key, &$value))) {
+            if (array_key_exists($key, $this->_items)) {
+                $value = unserialize($this->_items[$key]);
             }
             Event::handle('EndCacheGet', array($key, &$value));
         }
@@ -83,31 +175,100 @@ class Cache
         return $value;
     }
 
+    /**
+     * Set the value associated with a key
+     *
+     * @param string  $key    The key to use for lookups
+     * @param string  $value  The value to store
+     * @param integer $flag   Flags to use, may include Cache::COMPRESSED
+     * @param integer $expiry Expiry value, mostly ignored
+     *
+     * @return boolean success flag
+     */
     function set($key, $value, $flag=null, $expiry=null)
     {
         $success = false;
 
-        if (!Event::handle('StartCacheSet', array(&$key, &$value, &$flag, &$expiry, &$success))) {
-            common_log(LOG_INFO, 'Setting cache value for key ' . $key);
-            $_items[$key] = $value;
+        common_perf_counter('Cache::set', $key);
+        if (Event::handle('StartCacheSet', array(&$key, &$value, &$flag,
+                                                 &$expiry, &$success))) {
+
+            $this->_items[$key] = serialize($value);
+
             $success = true;
-            Event::handle('EndCacheSet', array($key, $value, $flag, $expiry));
+
+            Event::handle('EndCacheSet', array($key, $value, $flag,
+                                               $expiry));
         }
 
         return $success;
     }
 
+    /**
+     * Atomically increment an existing numeric value.
+     * Existing expiration time should remain unchanged, if any.
+     *
+     * @param string  $key    The key to use for lookups
+     * @param int     $step   Amount to increment (default 1)
+     *
+     * @return mixed incremented value, or false if not set.
+     */
+    function increment($key, $step=1)
+    {
+        $value = false;
+        common_perf_counter('Cache::increment', $key);
+        if (Event::handle('StartCacheIncrement', array(&$key, &$step, &$value))) {
+            // Fallback is not guaranteed to be atomic,
+            // and may original expiry value.
+            $value = $this->get($key);
+            if ($value !== false) {
+                $value += $step;
+                $ok = $this->set($key, $value);
+                $got = $this->get($key);
+            }
+            Event::handle('EndCacheIncrement', array($key, $step, $value));
+        }
+        return $value;
+    }
+
+    /**
+     * Delete the value associated with a key
+     *
+     * @param string $key Key to delete
+     *
+     * @return boolean success flag
+     */
     function delete($key)
     {
         $success = false;
 
-        if (!Event::handle('StartCacheDelete', array(&$key, &$success))) {
-            common_log(LOG_INFO, 'Deleting cache value for key ' . $key);
-            unset($_items[$key]);
+        common_perf_counter('Cache::delete', $key);
+        if (Event::handle('StartCacheDelete', array(&$key, &$success))) {
+            if (array_key_exists($key, $this->_items)) {
+                unset($this->_items[$key]);
+            }
             $success = true;
             Event::handle('EndCacheDelete', array($key));
         }
 
         return $success;
     }
+
+    /**
+     * Close or reconnect any remote connections, such as to give
+     * daemon processes a chance to reconnect on a fresh socket.
+     *
+     * @return boolean success flag
+     */
+    function reconnect()
+    {
+        $success = false;
+
+        if (Event::handle('StartCacheReconnect', array(&$success))) {
+            $success = true;
+            Event::handle('EndCacheReconnect', array());
+        }
+
+        return $success;
+    }
 }