PageRenderTime 34ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Cache/DataCache.php

http://github.com/modolabs/Kurogo-Mobile-Web
PHP | 285 lines | 215 code | 61 blank | 9 comment | 39 complexity | 7e9adfcf12003344797d8c0f7b116cdc MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1
  1. <?php
  2. /*
  3. * Copyright © 2010 - 2013 Modo Labs Inc. All rights reserved.
  4. *
  5. * The license governing the contents of this file is located in the LICENSE
  6. * file located at the root directory of this distribution. If the LICENSE file
  7. * is missing, please contact sales@modolabs.com.
  8. *
  9. */
  10. class DataCache
  11. {
  12. const CACHE_STATUS_EMPTY=1;
  13. const CACHE_STATUS_EXPIRED=2;
  14. const CACHE_STATUS_FRESH=3;
  15. protected $cacheFolder=CACHE_DIR;
  16. protected $useMemoryCache = true;
  17. protected $cacheBaseKey = 'DataCache';
  18. protected $serialize = true;
  19. protected $cacheLifetime = 0;
  20. protected $cacheGroup = null;
  21. public static function factory($cacheClass, $args)
  22. {
  23. if (!class_exists($cacheClass)) {
  24. throw new KurogoConfigurationException("Data cache class $cacheClass not defined");
  25. }
  26. $cache = new $cacheClass();
  27. if (!$cache instanceOf DataCache) {
  28. throw new KurogoConfigurationException("$cacheClass is not a subclass of DataCache");
  29. }
  30. $cache->init($args);
  31. return $cache;
  32. }
  33. public function createCacheFolderIfNeeded() {
  34. $cacheFolder = $this->getCacheFolder($this->cacheGroup);
  35. if (!is_dir($cacheFolder)) {
  36. if (!@mkdir($cacheFolder, 0700, true)) {
  37. throw new KurogoDataException("Could not create cache folder $cacheFolder");
  38. }
  39. }
  40. }
  41. protected function getCacheFolder($cacheGroup=null) {
  42. $return = rtrim($this->cacheFolder, DIRECTORY_SEPARATOR);
  43. if ($cacheGroup) {
  44. $return .= DIRECTORY_SEPARATOR . $this->cacheGroup;
  45. }
  46. return $return;
  47. }
  48. public function setSerialize($serialize) {
  49. $this->serialize = (bool) $serialize;
  50. }
  51. public function setUseMemoaryCache($useMemoryCache) {
  52. $this->useMemoryCache = (bool) $useMemoryCache;
  53. }
  54. public function setCacheLifetime($cacheLifetime) {
  55. $this->cacheLifetime = intval($cacheLifetime);
  56. }
  57. public function setCacheGroup($group) {
  58. $this->cacheGroup = $group;
  59. }
  60. protected function getValueFromMemory($key) {
  61. if ($memoryCache = $this->getMemoryCache()) {
  62. return $memoryCache->get($this->getMemoryCacheKey($key));
  63. }
  64. return false;
  65. }
  66. public function cacheStatus($key) {
  67. $age = $this->getDiskAge($key);
  68. if (is_null($age)) {
  69. return self::CACHE_STATUS_EMPTY;
  70. }
  71. if ($age < $this->cacheLifetime) {
  72. return self::CACHE_STATUS_FRESH;
  73. }
  74. return self::CACHE_STATUS_EXPIRED;
  75. }
  76. protected function getValueFromDisk($key) {
  77. $path = $this->getFullPath($key);
  78. if (file_exists($path)) {
  79. if ($contents = file_get_contents($path)) {
  80. Kurogo::log(LOG_DEBUG, "Reading cache $path", 'cache');
  81. if ($this->serialize) {
  82. return unserialize($contents);
  83. } else {
  84. return $contents;
  85. }
  86. }
  87. }
  88. return FALSE;
  89. }
  90. protected function getDiskAge($key) {
  91. $modified = $this->getModified($key);
  92. if (!is_null($modified)) {
  93. return time() - $modified;
  94. }
  95. return null;
  96. }
  97. public function getModified($key) {
  98. $path = $this->getFullPath($key);
  99. if (is_readable($path)) {
  100. return filemtime($path);
  101. } else {
  102. return null;
  103. }
  104. }
  105. public function getFullPath($key) {
  106. $return = $this->getCacheFolder($this->cacheGroup) . DIRECTORY_SEPARATOR . Watchdog::safeFilename($key);
  107. return $return;
  108. }
  109. public function getStaleValue($key) {
  110. return $this->getValueFromDisk($key);
  111. }
  112. public function get($key) {
  113. if ( ($val = $this->getValueFromMemory($key)) !== false) {
  114. Kurogo::log(LOG_DEBUG, "Reading $key from memory cache", 'cache');
  115. return $val;
  116. }
  117. $val = false;
  118. switch ($this->cacheStatus($key)) {
  119. case self::CACHE_STATUS_EMPTY:
  120. Kurogo::log(LOG_DEBUG, "Cache not available for $key", 'cache');
  121. break;
  122. case self::CACHE_STATUS_EXPIRED:
  123. Kurogo::log(LOG_DEBUG, "Cache expired for $key", 'cache');
  124. break;
  125. case self::CACHE_STATUS_FRESH:
  126. Kurogo::log(LOG_DEBUG, "Reading $key from disk cache", 'cache');
  127. $val = $this->getValueFromDisk($key);
  128. break;
  129. }
  130. return $val;
  131. }
  132. protected function getMemoryCache() {
  133. if ($this->useMemoryCache) {
  134. return Kurogo::sharedInstance()->cacher();
  135. }
  136. return false;
  137. }
  138. protected function getMemoryCacheKey($key) {
  139. $return = $this->cacheBaseKey . '-';
  140. if ($this->cacheGroup) {
  141. $return .= $this->cacheGroup . '-';
  142. }
  143. $return .= $key;
  144. return $return;
  145. }
  146. public function delete($key) {
  147. if ($memoryCache = $this->getMemoryCache()) {
  148. $memoryCache->delete($this->getMemoryCacheKey($key));
  149. }
  150. $this->deleteValueFromDisk($key);
  151. }
  152. public function set($key, $data) {
  153. /* Don't cache on 0 lifetime */
  154. if ($this->cacheLifetime == 0) {
  155. return false;
  156. }
  157. if (strlen($this->cacheGroup) === 0) {
  158. if ($memoryCache = $this->getMemoryCache()) {
  159. $memoryCache->set($this->getMemoryCacheKey($key), $data, $this->cacheLifetime);
  160. }
  161. }
  162. return $this->setValueToDisk($key, $data);
  163. }
  164. public function clearCache() {
  165. $folder = $this->getCacheFolder();
  166. return Kurogo::rmdir($folder);
  167. }
  168. public function clearCacheGroup($cacheGroup) {
  169. if (!$cacheGroup) {
  170. return false;
  171. }
  172. $folder = $this->getCacheFolder($cacheGroup);
  173. return Kurogo::rmdir($folder);
  174. }
  175. protected function deleteValueFromDisk($key) {
  176. $path = $this->getFullPath($key);
  177. if (file_exists($path)) {
  178. unlink($path);
  179. }
  180. }
  181. protected function setValueToDisk($key, $data) {
  182. $this->createCacheFolderIfNeeded();
  183. $path = $this->getFullPath($key);
  184. $umask = umask(0077);
  185. Kurogo::log(LOG_DEBUG, "Saving cache to $path", 'cache');
  186. $fh = fopen($path, 'w');
  187. if ($fh !== FALSE) {
  188. if ($this->serialize) {
  189. fwrite($fh, serialize($data));
  190. } else {
  191. fwrite($fh, $data);
  192. }
  193. fclose($fh);
  194. umask($umask);
  195. return TRUE;
  196. }
  197. umask($umask);
  198. return false;
  199. }
  200. public function setCacheFolder($cacheFolder, $create = false) {
  201. $this->cacheFolder = $cacheFolder;
  202. if ($create) {
  203. $this->createCacheFolderIfNeeded();
  204. }
  205. if (!realpath_exists($this->cacheFolder)) {
  206. throw new KurogoDataException("Path $this->cacheFolder is not valid for cache");
  207. }
  208. if (!is_writable($cacheFolder)) {
  209. throw new KurogoDataException("Path $this->cacheFolder is not writable");
  210. }
  211. }
  212. protected function init($args) {
  213. if (isset($args['CACHE_FOLDER']) && !empty($args['CACHE_FOLDER'])) {
  214. $this->cacheBaseKey = $args['CACHE_FOLDER'];
  215. $this->setCacheFolder(CACHE_DIR . DIRECTORY_SEPARATOR . $args['CACHE_FOLDER'], true);
  216. }
  217. if (isset($args['USE_MEMORY_CACHE'])) {
  218. $this->useMemoryCache = (bool) $args['USE_MEMORY_CACHE'];
  219. }
  220. if (isset($args['CACHE_LIFETIME'])) {
  221. $this->cacheLifetime = intval($args['CACHE_LIFETIME']);
  222. }
  223. }
  224. }