Cache.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <?php
  2. namespace Grav\Common;
  3. use \Doctrine\Common\Cache\Cache as DoctrineCache;
  4. use Grav\Common\Config\Config;
  5. use Grav\Common\Filesystem\Folder;
  6. /**
  7. * The GravCache object is used throughout Grav to store and retrieve cached data.
  8. * It uses DoctrineCache library and supports a variety of caching mechanisms. Those include:
  9. *
  10. * APC
  11. * XCache
  12. * RedisCache
  13. * MemCache
  14. * MemCacheD
  15. * FileSystem
  16. *
  17. * @author RocketTheme
  18. * @license MIT
  19. */
  20. class Cache extends Getters
  21. {
  22. /**
  23. * @var string Cache key.
  24. */
  25. protected $key;
  26. protected $lifetime;
  27. protected $now;
  28. protected $config;
  29. /**
  30. * @var DoctrineCache
  31. */
  32. protected $driver;
  33. /**
  34. * @var bool
  35. */
  36. protected $enabled;
  37. protected $cache_dir;
  38. protected static $standard_remove = [
  39. 'cache/twig/',
  40. 'cache/doctrine/',
  41. 'cache/compiled/',
  42. 'cache/validated-',
  43. 'images/',
  44. 'assets/',
  45. ];
  46. protected static $all_remove = [
  47. 'cache/',
  48. 'images/',
  49. 'assets/'
  50. ];
  51. protected static $assets_remove = [
  52. 'assets/'
  53. ];
  54. protected static $images_remove = [
  55. 'images/'
  56. ];
  57. protected static $cache_remove = [
  58. 'cache/'
  59. ];
  60. /**
  61. * Constructor
  62. *
  63. * @params Grav $grav
  64. */
  65. public function __construct(Grav $grav)
  66. {
  67. $this->init($grav);
  68. }
  69. /**
  70. * Initialization that sets a base key and the driver based on configuration settings
  71. *
  72. * @param Grav $grav
  73. * @return void
  74. */
  75. public function init(Grav $grav)
  76. {
  77. /** @var Config $config */
  78. $this->config = $grav['config'];
  79. $this->now = time();
  80. $this->cache_dir = $grav['locator']->findResource('cache://doctrine', true, true);
  81. /** @var Uri $uri */
  82. $uri = $grav['uri'];
  83. $prefix = $this->config->get('system.cache.prefix');
  84. $this->enabled = (bool) $this->config->get('system.cache.enabled');
  85. // Cache key allows us to invalidate all cache on configuration changes.
  86. $this->key = ($prefix ? $prefix : 'g') . '-' . substr(md5($uri->rootUrl(true) . $this->config->key() . GRAV_VERSION), 2, 8);
  87. $this->driver = $this->getCacheDriver();
  88. // Set the cache namespace to our unique key
  89. $this->driver->setNamespace($this->key);
  90. }
  91. /**
  92. * Automatically picks the cache mechanism to use. If you pick one manually it will use that
  93. * If there is no config option for $driver in the config, or it's set to 'auto', it will
  94. * pick the best option based on which cache extensions are installed.
  95. *
  96. * @return DoctrineCacheDriver The cache driver to use
  97. */
  98. public function getCacheDriver()
  99. {
  100. $setting = $this->config->get('system.cache.driver');
  101. $driver_name = 'file';
  102. if (!$setting || $setting == 'auto') {
  103. if (extension_loaded('apc')) {
  104. $driver_name = 'apc';
  105. } elseif (extension_loaded('wincache')) {
  106. $driver_name = 'wincache';
  107. } elseif (extension_loaded('xcache')) {
  108. $driver_name = 'xcache';
  109. }
  110. } else {
  111. $driver_name = $setting;
  112. }
  113. switch ($driver_name) {
  114. case 'apc':
  115. $driver = new \Doctrine\Common\Cache\ApcCache();
  116. break;
  117. case 'wincache':
  118. $driver = new \Doctrine\Common\Cache\WinCacheCache();
  119. break;
  120. case 'xcache':
  121. $driver = new \Doctrine\Common\Cache\XcacheCache();
  122. break;
  123. case 'memcache':
  124. $memcache = new \Memcache();
  125. $memcache->connect($this->config->get('system.cache.memcache.server','localhost'),
  126. $this->config->get('system.cache.memcache.port', 11211));
  127. $driver = new \Doctrine\Common\Cache\MemcacheCache();
  128. $driver->setMemcache($memcache);
  129. break;
  130. case 'redis':
  131. $redis = new \Redis();
  132. $redis->connect($this->config->get('system.cache.redis.server','localhost'),
  133. $this->config->get('system.cache.redis.port', 6379));
  134. $driver = new \Doctrine\Common\Cache\RedisCache();
  135. $driver->setRedis($redis);
  136. break;
  137. default:
  138. $driver = new \Doctrine\Common\Cache\FilesystemCache($this->cache_dir);
  139. break;
  140. }
  141. return $driver;
  142. }
  143. /**
  144. * Gets a cached entry if it exists based on an id. If it does not exist, it returns false
  145. *
  146. * @param string $id the id of the cached entry
  147. * @return object returns the cached entry, can be any type, or false if doesn't exist
  148. */
  149. public function fetch($id)
  150. {
  151. if ($this->enabled) {
  152. return $this->driver->fetch($id);
  153. } else {
  154. return false;
  155. }
  156. }
  157. /**
  158. * Stores a new cached entry.
  159. *
  160. * @param string $id the id of the cached entry
  161. * @param array|object $data the data for the cached entry to store
  162. * @param int $lifetime the lifetime to store the entry in seconds
  163. */
  164. public function save($id, $data, $lifetime = null)
  165. {
  166. if ($this->enabled) {
  167. if ($lifetime === null) {
  168. $lifetime = $this->getLifetime();
  169. }
  170. $this->driver->save($id, $data, $lifetime);
  171. }
  172. }
  173. /**
  174. * Getter method to get the cache key
  175. */
  176. public function getKey()
  177. {
  178. return $this->key;
  179. }
  180. /**
  181. * Helper method to clear all Grav caches
  182. *
  183. * @param string $remove standard|all|assets-only|images-only|cache-only
  184. *
  185. * @return array
  186. */
  187. public static function clearCache($remove = 'standard')
  188. {
  189. $output = [];
  190. $user_config = USER_DIR . 'config/system.yaml';
  191. switch($remove) {
  192. case 'all':
  193. $remove_paths = self::$all_remove;
  194. break;
  195. case 'assets-only':
  196. $remove_paths = self::$assets_remove;
  197. break;
  198. case 'images-only':
  199. $remove_paths = self::$images_remove;
  200. break;
  201. case 'cache-only':
  202. $remove_paths = self::$cache_remove;
  203. break;
  204. default:
  205. $remove_paths = self::$standard_remove;
  206. }
  207. foreach ($remove_paths as $path) {
  208. $anything = false;
  209. $files = glob(ROOT_DIR . $path . '*');
  210. if (is_array($files)) {
  211. foreach ($files as $file) {
  212. if (is_file($file)) {
  213. if (@unlink($file)) {
  214. $anything = true;
  215. }
  216. } elseif (is_dir($file)) {
  217. if (@Folder::delete($file)) {
  218. $anything = true;
  219. }
  220. }
  221. }
  222. }
  223. if ($anything) {
  224. $output[] = '<red>Cleared: </red>' . $path . '*';
  225. }
  226. }
  227. $output[] = '';
  228. if (($remove == 'all' || $remove == 'standard') && file_exists($user_config)) {
  229. touch($user_config);
  230. $output[] = '<red>Touched: </red>' . $user_config;
  231. $output[] = '';
  232. }
  233. return $output;
  234. }
  235. /**
  236. * Set the cache lifetime programmatically
  237. *
  238. * @param int $future timestamp
  239. */
  240. public function setLifetime($future)
  241. {
  242. if (!$future) {
  243. return;
  244. }
  245. $interval = $future - $this->now;
  246. if ($interval > 0 && $interval < $this->getLifetime()) {
  247. $this->lifetime = $interval;
  248. }
  249. }
  250. /**
  251. * Retrieve the cache lifetime (in seconds)
  252. *
  253. * @return mixed
  254. */
  255. public function getLifetime()
  256. {
  257. if ($this->lifetime === null) {
  258. $this->lifetime = $this->config->get('system.cache.lifetime') ?: 604800; // 1 week default
  259. }
  260. return $this->lifetime;
  261. }
  262. }