Cache_File.class.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <?php
  2. /**
  3. * Cache strategy using files
  4. *
  5. * @author Christopher Han <xiphux@gmail.com>
  6. * @copyright (c) 2012 Christopher Han
  7. * @package GitPHP
  8. * @subpackage Cache
  9. */
  10. class GitPHP_Cache_File implements GitPHP_CacheStrategy_Interface
  11. {
  12. /**
  13. * Gzipped cache
  14. *
  15. * @var int
  16. */
  17. const CacheTypeGzip = 1;
  18. /**
  19. * Igbinary cache
  20. *
  21. * @var int
  22. */
  23. const CacheTypeIgbinary = 2;
  24. /**
  25. * Cache file directory
  26. *
  27. * @var string
  28. */
  29. protected $cacheDir;
  30. /**
  31. * Compression threshold
  32. *
  33. * @var int
  34. */
  35. protected $compressThreshold = 0;
  36. /**
  37. * Enable igbinary
  38. *
  39. * @var boolean
  40. */
  41. protected $igbinary = false;
  42. /**
  43. * Constructor
  44. *
  45. * @param string $cacheDir cache dir
  46. * @param int $compressThreshold threshold to start compressing data at
  47. * @param boolean $igbinary whether to use igbinary, null to autodetect
  48. */
  49. public function __construct($cacheDir, $compressThreshold = 0, $igbinary = null)
  50. {
  51. if (file_exists($cacheDir)) {
  52. if (!is_dir($cacheDir)) {
  53. throw new Exception($cacheDir . ' exists but is not a directory');
  54. } else if (!is_writable($cacheDir)) {
  55. throw new Exception($cacheDir . ' is not writable');
  56. }
  57. } else {
  58. if (!mkdir($cacheDir, 0777))
  59. throw new Exception($cacheDir . ' could not be created');
  60. chmod($cacheDir, 0777);
  61. }
  62. $this->cacheDir = GitPHP_Util::AddSlash($cacheDir, true);
  63. if (!(is_int($compressThreshold) && ($compressThreshold >= 0))) {
  64. throw new Exception('Invalid compression threshold');
  65. }
  66. $this->compressThreshold = $compressThreshold;
  67. if ($igbinary === null) {
  68. $this->igbinary = function_exists('igbinary_serialize');
  69. } else if ($igbinary) {
  70. if (!function_exists('igbinary_serialize'))
  71. throw new Exception('Igbinary is not present');
  72. $this->igbinary = $igbinary;
  73. }
  74. }
  75. /**
  76. * Gets an item from the cache
  77. *
  78. * @param string $key cache key
  79. * @return mixed cached object or false if not found
  80. */
  81. public function Get($key)
  82. {
  83. if (empty($key))
  84. return false;
  85. $return = $this->Load($key);
  86. if ($return === false)
  87. return false;
  88. list($cachetype, $data) = $return;
  89. if ($cachetype == GitPHP_Cache_File::CacheTypeIgbinary) {
  90. $data = igbinary_unserialize($data);
  91. } else if ($cachetype == GitPHP_Cache_File::CacheTypeGzip) {
  92. $data = unserialize(gzuncompress($data));
  93. } else {
  94. $data = unserialize($data);
  95. }
  96. return $data;
  97. }
  98. /**
  99. * Sets an item into the cache
  100. *
  101. * @param string $key cache key
  102. * @param mixed $value object to cache
  103. * @param int $lifetime cached object lifetime
  104. */
  105. public function Set($key, $value, $lifetime)
  106. {
  107. if (empty($key) || empty($value))
  108. return;
  109. $expire = '';
  110. if ($lifetime >= 0)
  111. $expire = time() + $lifetime;
  112. $this->Save($key, $value, $expire);
  113. }
  114. /**
  115. * Check if an item exists
  116. *
  117. * @param string $key cache key
  118. * @return boolean true if exists
  119. */
  120. public function Exists($key)
  121. {
  122. return ($this->Load($key) !== false);
  123. }
  124. /**
  125. * Delete an item from the cache
  126. *
  127. * @param string $key cache key
  128. */
  129. public function Delete($key)
  130. {
  131. if (empty($key))
  132. return;
  133. $file = $this->cacheDir . $this->KeyToFile($key);
  134. if (file_exists($file))
  135. unlink($file);
  136. }
  137. /**
  138. * Clear the cache
  139. */
  140. public function Clear()
  141. {
  142. if ($dh = opendir($this->cacheDir)) {
  143. while (($file = readdir($dh)) !== false) {
  144. if (($file == '.') || ($file == '..'))
  145. continue;
  146. if (file_exists($this->cacheDir . $file))
  147. unlink($this->cacheDir . $file);
  148. }
  149. closedir($dh);
  150. }
  151. }
  152. /**
  153. * Load a key's serialized data
  154. *
  155. * @param string $key cache key
  156. */
  157. private function Load($key)
  158. {
  159. if (empty($key))
  160. return false;
  161. $file = $this->cacheDir . $this->KeyToFile($key);
  162. if (!is_readable($file))
  163. return false;
  164. $contents = file_get_contents($file);
  165. if (empty($contents)) {
  166. unlink($file);
  167. return false;
  168. }
  169. $flags = strtok($contents, "\n");
  170. $expire = strtok($flags, "|");
  171. $cachetype = strtok("|");
  172. if (!empty($expire) && ($expire < time())) {
  173. unlink($file);
  174. return false;
  175. }
  176. $data = substr($contents, strlen($flags) + 1);
  177. if (empty($data)) {
  178. unlink($file);
  179. return false;
  180. }
  181. if (($cachetype == GitPHP_Cache_File::CacheTypeIgbinary) && (!$this->igbinary)) {
  182. unlink($file);
  183. return false;
  184. }
  185. return array($cachetype, $data);
  186. }
  187. /**
  188. * Save a key's data
  189. *
  190. * @param string $key cache key
  191. * @param mixed $data data
  192. * @param int $expire expiration instant
  193. */
  194. private function Save($key, $data, $expire = '')
  195. {
  196. $flags = $expire;
  197. if ($this->igbinary) {
  198. $data = igbinary_serialize($data);
  199. $flags .= '|' . GitPHP_Cache_File::CacheTypeIgbinary;
  200. } else {
  201. $data = serialize($data);
  202. if (($this->compressThreshold > 0) && (strlen($data) > $this->compressThreshold)) {
  203. $data = gzcompress($data);
  204. $flags .= '|' . GitPHP_Cache_File::CacheTypeGzip;
  205. }
  206. }
  207. file_put_contents($this->cacheDir . $this->KeyToFile($key), $flags . "\n" . $data);
  208. }
  209. /**
  210. * Converts a key to a filename
  211. *
  212. * @param string $key key
  213. * @return string filename
  214. */
  215. private function KeyToFile($key)
  216. {
  217. if (empty($key))
  218. return '';
  219. $key = preg_replace('/[^\w\|]+/', '_', $key);
  220. $key = preg_replace('/\|/', '^', $key);
  221. return $key . '.dat';
  222. }
  223. }