]> git.mxchange.org Git - friendica.git/blobdiff - include/cache.php
Merge pull request #3463 from friendica/develop
[friendica.git] / include / cache.php
index 3a18fe2a5a45b237a3a3b605d3a79a082786bbaf..b56906c54492d95033e073df544ab61505b6029a 100644 (file)
 <?php
+/**
+ * @file include/cache.php
+ *
+ * @brief Class for storing data for a short time
+ */
+
+use Friendica\Core\Config;
+use Friendica\Core\PConfig;
+
+class Cache {
        /**
-        *  cache api
+        * @brief Check for memcache and open a connection if configured
+        *
+        * @return object|boolean The memcache object - or "false" if not successful
         */
+       public static function memcache() {
+               if (!function_exists('memcache_connect')) {
+                       return false;
+               }
+
+               if (!Config::get('system', 'memcache')) {
+                       return false;
+               }
+
+               $memcache_host = Config::get('system', 'memcache_host', '127.0.0.1');
+               $memcache_port = Config::get('system', 'memcache_port', 11211);
+
+               $memcache = new Memcache;
+
+               if (!$memcache->connect($memcache_host, $memcache_port)) {
+                       return false;
+               }
+
+               return $memcache;
+       }
 
-       class Cache {
-               public static function get($key) {
-                       /*if (function_exists("apc_fetch") AND function_exists("apc_exists"))
-                               if (apc_exists($key))
-                                       return(apc_fetch($key));*/
+       /**
+        * @brief Return the duration for a given cache level
+        *
+        * @param integer $level Cache level
+        *
+        * @return integer The cache duration in seconds
+        */
+       private function duration($level) {
+               switch($level) {
+                       case CACHE_MONTH;
+                               $seconds = 2592000;
+                               break;
+                       case CACHE_WEEK;
+                               $seconds = 604800;
+                               break;
+                       case CACHE_DAY;
+                               $seconds = 86400;
+                               break;
+                       case CACHE_HOUR;
+                               $seconds = 3600;
+                               break;
+                       case CACHE_HALF_HOUR;
+                               $seconds = 1800;
+                               break;
+                       case CACHE_QUARTER_HOUR;
+                               $seconds = 900;
+                               break;
+                       case CACHE_FIVE_MINUTES;
+                               $seconds = 300;
+                               break;
+                       case CACHE_MINUTE;
+                               $seconds = 60;
+                               break;
+               }
+               return $seconds;
+       }
 
-                       $r = q("SELECT `v` FROM `cache` WHERE `k`='%s' limit 1",
-                               dbesc($key)
-                       );
+       /**
+        * @brief Fetch cached data according to the key
+        *
+        * @param string $key The key to the cached data
+        *
+        * @return mixed Cached $value or "null" if not found
+        */
+       public static function get($key) {
 
-                       if (count($r)) {
-                               /*if (function_exists("apc_store"))
-                                       apc_store($key, $r[0]['v'], 600);*/
+               $memcache = self::memcache();
+               if (is_object($memcache)) {
+                       // We fetch with the hostname as key to avoid problems with other applications
+                       $cached = $memcache->get(get_app()->get_hostname().":".$key);
+                       $value = @unserialize($cached);
 
-                               return $r[0]['v'];
+                       // Only return a value if the serialized value is valid.
+                       // We also check if the db entry is a serialized
+                       // boolean 'false' value (which we want to return).
+                       if ($cached === serialize(false) || $value !== false) {
+                               return $value;
                        }
+
                        return null;
                }
 
-               public static function set($key,$value) {
+               // Frequently clear cache
+               self::clear($duration);
 
-                       q("REPLACE INTO `cache` (`k`,`v`,`updated`) VALUES ('%s','%s','%s')",
-                                       dbesc($key),
-                                       dbesc($value),
-                                       dbesc(datetime_convert()));
+               $r = q("SELECT `v` FROM `cache` WHERE `k`='%s' LIMIT 1",
+                       dbesc($key)
+               );
 
-                       /*if (function_exists("apc_store"))
-                               apc_store($key, $value, 600);*/
+               if (dbm::is_result($r)) {
+                       $cached = $r[0]['v'];
+                       $value = @unserialize($cached);
 
+                       // Only return a value if the serialized value is valid.
+                       // We also check if the db entry is a serialized
+                       // boolean 'false' value (which we want to return).
+                       if ($cached === serialize(false) || $value !== false) {
+                               return $value;
+                       }
                }
 
+               return null;
+       }
 
-/*
- *
- * Leaving this legacy code temporaily to see how REPLACE fares
- * as opposed to non-atomic checks when faced with fast moving key duplication.
- * As a MySQL extension it isn't portable, but we're not yet very portable.
- */
+       /**
+        * @brief Put data in the cache according to the key
+        *
+        * The input $value can have multiple formats.
+        *
+        * @param string $key The key to the cached data
+        * @param mixed $valie The value that is about to be stored
+        * @param integer $duration The cache lifespan
+        */
+       public static function set($key, $value, $duration = CACHE_MONTH) {
 
-/*
- *                     $r = q("SELECT * FROM `cache` WHERE `k`='%s' limit 1",
- *                             dbesc($key)
- *                     );
- *                     if(count($r)) {
- *                             q("UPDATE `cache` SET `v` = '%s', `updated = '%s' WHERE `k` = '%s'",
- *                                     dbesc($value),
- *                                     dbesc(datetime_convert()),
- *                                     dbesc($key));
- *                     }
- *                     else {
- *                             q("INSERT INTO `cache` (`k`,`v`,`updated`) VALUES ('%s','%s','%s')",
- *                                     dbesc($key),
- *                                     dbesc($value),
- *                                     dbesc(datetime_convert()));
- *                     }
- *             }
- */
+               // Do we have an installed memcache? Use it instead.
+               $memcache = self::memcache();
+               if (is_object($memcache)) {
+                       // We store with the hostname as key to avoid problems with other applications
+                       $memcache->set(get_app()->get_hostname().":".$key, serialize($value), MEMCACHE_COMPRESSED, self::duration($duration));
+                       return;
+               }
 
+               /// @todo store the cache data in the same way like the config data
+               q("REPLACE INTO `cache` (`k`,`v`,`expire_mode`,`updated`) VALUES ('%s','%s',%d,'%s')",
+                               dbesc($key),
+                               dbesc(serialize($value)),
+                               intval($duration),
+                               dbesc(datetime_convert()));
+       }
 
-               public static function clear(){
-                       q("DELETE FROM `cache` WHERE `updated` < '%s'",
-                               dbesc(datetime_convert('UTC','UTC',"now - 30 days")));
+       /**
+        * @brief Remove outdated data from the cache
+        *
+        * @param integer $maxlevel The maximum cache level that is to be cleared
+        */
+       public static function clear($max_level = CACHE_MONTH) {
+
+               // Clear long lasting cache entries only once a day
+               if (get_config("system", "cache_cleared_day") < time() - self::duration(CACHE_DAY)) {
+                       if ($max_level == CACHE_MONTH) {
+                               q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
+                                       dbesc(datetime_convert('UTC','UTC',"now - 30 days")), intval(CACHE_MONTH));
+                       }
+
+                       if ($max_level <= CACHE_WEEK) {
+                               q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
+                                       dbesc(datetime_convert('UTC','UTC',"now - 7 days")), intval(CACHE_WEEK));
+                       }
+
+                       if ($max_level <= CACHE_DAY) {
+                               q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
+                               dbesc(datetime_convert('UTC','UTC',"now - 1 days")), intval(CACHE_DAY));
+                       }
+                       set_config("system", "cache_cleared_day", time());
                }
 
-       }
+               if (($max_level <= CACHE_HOUR) AND (get_config("system", "cache_cleared_hour")) < time() - self::duration(CACHE_HOUR)) {
+                       q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
+                               dbesc(datetime_convert('UTC','UTC',"now - 1 hours")), intval(CACHE_HOUR));
+
+                       set_config("system", "cache_cleared_hour", time());
+               }
+
+               if (($max_level <= CACHE_HALF_HOUR) AND (get_config("system", "cache_cleared_half_hour")) < time() - self::duration(CACHE_HALF_HOUR)) {
+                       q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
+                               dbesc(datetime_convert('UTC','UTC',"now - 30 minutes")), intval(CACHE_HALF_HOUR));
+
+                       set_config("system", "cache_cleared_half_hour", time());
+               }
+
+               if (($max_level <= CACHE_QUARTER_HOUR) AND (get_config("system", "cache_cleared_quarter_hour")) < time() - self::duration(CACHE_QUARTER_HOUR)) {
+                       q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
+                               dbesc(datetime_convert('UTC','UTC',"now - 15 minutes")), intval(CACHE_QUARTER_HOUR));
+
+                       set_config("system", "cache_cleared_quarter_hour", time());
+               }
 
+               if (($max_level <= CACHE_FIVE_MINUTES) AND (get_config("system", "cache_cleared_five_minute")) < time() - self::duration(CACHE_FIVE_MINUTES)) {
+                       q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
+                               dbesc(datetime_convert('UTC','UTC',"now - 5 minutes")), intval(CACHE_FIVE_MINUTES));
+
+                       set_config("system", "cache_cleared_five_minute", time());
+               }
+
+               if (($max_level <= CACHE_MINUTE) AND (get_config("system", "cache_cleared_minute")) < time() - self::duration(CACHE_MINUTE)) {
+                       q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
+                               dbesc(datetime_convert('UTC','UTC',"now - 1 minutes")), intval(CACHE_MINUTE));
+
+                       set_config("system", "cache_cleared_minute", time());
+               }
+       }
+}