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.

235 lines
6.4 KiB

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