Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

248 lines
6.5 KiB

  1. <?php
  2. /**
  3. * @file src/Core/Cache.php
  4. */
  5. namespace Friendica\Core;
  6. use Friendica\Core\Config;
  7. use Friendica\Core\PConfig;
  8. use Friendica\Database\DBM;
  9. /**
  10. * @brief Class for storing data for a short time
  11. */
  12. class Cache
  13. {
  14. /**
  15. * @brief Check for memcache and open a connection if configured
  16. *
  17. * @return object|boolean The memcache object - or "false" if not successful
  18. */
  19. public static function memcache()
  20. {
  21. if (!function_exists('memcache_connect')) {
  22. return false;
  23. }
  24. if (!Config::get('system', 'memcache')) {
  25. return false;
  26. }
  27. $memcache_host = Config::get('system', 'memcache_host', '127.0.0.1');
  28. $memcache_port = Config::get('system', 'memcache_port', 11211);
  29. $memcache = new Memcache;
  30. if (!$memcache->connect($memcache_host, $memcache_port)) {
  31. return false;
  32. }
  33. return $memcache;
  34. }
  35. /**
  36. * @brief Return the duration for a given cache level
  37. *
  38. * @param integer $level Cache level
  39. *
  40. * @return integer The cache duration in seconds
  41. */
  42. private static function duration($level)
  43. {
  44. switch ($level) {
  45. case CACHE_MONTH:
  46. $seconds = 2592000;
  47. break;
  48. case CACHE_WEEK:
  49. $seconds = 604800;
  50. break;
  51. case CACHE_DAY:
  52. $seconds = 86400;
  53. break;
  54. case CACHE_HOUR:
  55. $seconds = 3600;
  56. break;
  57. case CACHE_HALF_HOUR:
  58. $seconds = 1800;
  59. break;
  60. case CACHE_QUARTER_HOUR:
  61. $seconds = 900;
  62. break;
  63. case CACHE_FIVE_MINUTES:
  64. $seconds = 300;
  65. break;
  66. case CACHE_MINUTE:
  67. $seconds = 60;
  68. break;
  69. }
  70. return $seconds;
  71. }
  72. /**
  73. * @brief Fetch cached data according to the key
  74. *
  75. * @param string $key The key to the cached data
  76. *
  77. * @return mixed Cached $value or "null" if not found
  78. */
  79. public static function get($key)
  80. {
  81. $memcache = self::memcache();
  82. if (is_object($memcache)) {
  83. // We fetch with the hostname as key to avoid problems with other applications
  84. $cached = $memcache->get(get_app()->get_hostname().":".$key);
  85. $value = @unserialize($cached);
  86. // Only return a value if the serialized value is valid.
  87. // We also check if the db entry is a serialized
  88. // boolean 'false' value (which we want to return).
  89. if ($cached === serialize(false) || $value !== false) {
  90. return $value;
  91. }
  92. return null;
  93. }
  94. // Frequently clear cache
  95. self::clear($duration);
  96. $r = q(
  97. "SELECT `v` FROM `cache` WHERE `k`='%s' LIMIT 1",
  98. dbesc($key)
  99. );
  100. if (DBM::is_result($r)) {
  101. $cached = $r[0]['v'];
  102. $value = @unserialize($cached);
  103. // Only return a value if the serialized value is valid.
  104. // We also check if the db entry is a serialized
  105. // boolean 'false' value (which we want to return).
  106. if ($cached === serialize(false) || $value !== false) {
  107. return $value;
  108. }
  109. }
  110. return null;
  111. }
  112. /**
  113. * @brief Put data in the cache according to the key
  114. *
  115. * The input $value can have multiple formats.
  116. *
  117. * @param string $key The key to the cached data
  118. * @param mixed $value The value that is about to be stored
  119. * @param integer $duration The cache lifespan
  120. *
  121. * @return void
  122. */
  123. public static function set($key, $value, $duration = CACHE_MONTH)
  124. {
  125. // Do we have an installed memcache? Use it instead.
  126. $memcache = self::memcache();
  127. if (is_object($memcache)) {
  128. // We store with the hostname as key to avoid problems with other applications
  129. $memcache->set(get_app()->get_hostname().":".$key, serialize($value), MEMCACHE_COMPRESSED, self::duration($duration));
  130. return;
  131. }
  132. /// @todo store the cache data in the same way like the config data
  133. q(
  134. "REPLACE INTO `cache` (`k`,`v`,`expire_mode`,`updated`) VALUES ('%s','%s',%d,'%s')",
  135. dbesc($key),
  136. dbesc(serialize($value)),
  137. intval($duration),
  138. dbesc(datetime_convert())
  139. );
  140. }
  141. /**
  142. * @brief Remove outdated data from the cache
  143. *
  144. * @param integer $max_level The maximum cache level that is to be cleared
  145. *
  146. * @return void
  147. */
  148. public static function clear($max_level = CACHE_MONTH)
  149. {
  150. // Clear long lasting cache entries only once a day
  151. if (Config::get("system", "cache_cleared_day") < time() - self::duration(CACHE_DAY)) {
  152. if ($max_level == CACHE_MONTH) {
  153. q(
  154. "DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
  155. dbesc(datetime_convert('UTC', 'UTC', "now - 30 days")),
  156. intval(CACHE_MONTH)
  157. );
  158. }
  159. if ($max_level <= CACHE_WEEK) {
  160. q(
  161. "DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
  162. dbesc(datetime_convert('UTC', 'UTC', "now - 7 days")),
  163. intval(CACHE_WEEK)
  164. );
  165. }
  166. if ($max_level <= CACHE_DAY) {
  167. q(
  168. "DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
  169. dbesc(datetime_convert('UTC', 'UTC', "now - 1 days")),
  170. intval(CACHE_DAY)
  171. );
  172. }
  173. Config::set("system", "cache_cleared_day", time());
  174. }
  175. if (($max_level <= CACHE_HOUR) && (Config::get("system", "cache_cleared_hour")) < time() - self::duration(CACHE_HOUR)) {
  176. q(
  177. "DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
  178. dbesc(datetime_convert('UTC', 'UTC', "now - 1 hours")),
  179. intval(CACHE_HOUR)
  180. );
  181. Config::set("system", "cache_cleared_hour", time());
  182. }
  183. if (($max_level <= CACHE_HALF_HOUR) && (Config::get("system", "cache_cleared_half_hour")) < time() - self::duration(CACHE_HALF_HOUR)) {
  184. q(
  185. "DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
  186. dbesc(datetime_convert('UTC', 'UTC', "now - 30 minutes")),
  187. intval(CACHE_HALF_HOUR)
  188. );
  189. Config::set("system", "cache_cleared_half_hour", time());
  190. }
  191. if (($max_level <= CACHE_QUARTER_HOUR) && (Config::get("system", "cache_cleared_quarter_hour")) < time() - self::duration(CACHE_QUARTER_HOUR)) {
  192. q(
  193. "DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
  194. dbesc(datetime_convert('UTC', 'UTC', "now - 15 minutes")),
  195. intval(CACHE_QUARTER_HOUR)
  196. );
  197. Config::set("system", "cache_cleared_quarter_hour", time());
  198. }
  199. if (($max_level <= CACHE_FIVE_MINUTES) && (Config::get("system", "cache_cleared_five_minute")) < time() - self::duration(CACHE_FIVE_MINUTES)) {
  200. q(
  201. "DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
  202. dbesc(datetime_convert('UTC', 'UTC', "now - 5 minutes")),
  203. intval(CACHE_FIVE_MINUTES)
  204. );
  205. Config::set("system", "cache_cleared_five_minute", time());
  206. }
  207. if (($max_level <= CACHE_MINUTE) && (Config::get("system", "cache_cleared_minute")) < time() - self::duration(CACHE_MINUTE)) {
  208. q(
  209. "DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
  210. dbesc(datetime_convert('UTC', 'UTC', "now - 1 minutes")),
  211. intval(CACHE_MINUTE)
  212. );
  213. Config::set("system", "cache_cleared_minute", time());
  214. }
  215. }
  216. }