1. sfFileCache.class.php
  2. /** * Cache class that stores content in files. * * @package symfony * @subpackage cache * @author Fabien Potencier * @version SVN: $Id: sfFileCache.class.php 23810 2009-11-12 11:07:44Z Kris.Wallsmith $ */
  3. class sfFileCache extends sfCache
  4. {
  5. const READ_DATA = 1;
  6. const READ_TIMEOUT = 2;
  7. const READ_LAST_MODIFIED = 4;
  8. const EXTENSION = '.cache';
  9. /**
  10. * Initializes this sfCache instance.
  11. *
  12. * Available options:
  13. *
  14. * * cache_dir: The directory where to put cache files
  15. *
  16. * * see sfCache for options available for all drivers
  17. *
  18. * @see sfCache
  19. */
  20. public function initialize($options = array())
  21. {
  22. parent::initialize($options);
  23. if (!$this->getOption('cache_dir'))
  24. {
  25. throw new sfInitializationException('You must pass a "cache_dir" option to initialize a sfFileCache object.');
  26. }
  27. $this->setcache_dir($this->getOption('cache_dir'));
  28. }
  29. /**
  30. * @see sfCache
  31. */
  32. public function get($key, $default = null)
  33. {
  34. $file_path = $this->getFilePath($key);
  35. if (!file_exists($file_path))
  36. {
  37. return $default;
  38. }
  39. $data = $this->read($file_path, self::READ_DATA);
  40. if ($data[self::READ_DATA] === null)
  41. {
  42. return $default;
  43. }
  44. return $data[self::READ_DATA];
  45. }
  46. /**
  47. * @see sfCache
  48. */
  49. public function has($key)
  50. {
  51. $path = $this->getFilePath($key);
  52. return file_exists($path) && $this->isValid($path);
  53. }
  54. /**
  55. * @see sfCache
  56. */
  57. public function set($key, $data, $lifetime = null)
  58. {
  59. if ($this->getOption('automatic_cleaning_factor') > 0 && rand(1, $this->getOption('automatic_cleaning_factor')) == 1)
  60. {
  61. $this->clean(sfCache::OLD);
  62. }
  63. return $this->write($this->getFilePath($key), $data, time() + $this->getLifetime($lifetime));
  64. }
  65. /**
  66. * @see sfCache
  67. */
  68. public function remove($key)
  69. {
  70. return @unlink($this->getFilePath($key));
  71. }
  72. /**
  73. * @see sfCache
  74. */
  75. public function removePattern($pattern)
  76. {
  77. if (false !== strpos($pattern, '**'))
  78. {
  79. $pattern = str_replace(sfCache::SEPARATOR, DIRECTORY_SEPARATOR, $pattern).self::EXTENSION;
  80. $regexp = self::patternToRegexp($pattern);
  81. $paths = array();
  82. foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->getOption('cache_dir'))) as $path)
  83. {
  84. if (preg_match($regexp, str_replace($this->getOption('cache_dir').DIRECTORY_SEPARATOR, '', $path)))
  85. {
  86. $paths[] = $path;
  87. }
  88. }
  89. }
  90. else
  91. {
  92. $paths = glob($this->getOption('cache_dir').DIRECTORY_SEPARATOR.str_replace(sfCache::SEPARATOR, DIRECTORY_SEPARATOR, $pattern).self::EXTENSION);
  93. }
  94. foreach ($paths as $path)
  95. {
  96. if (is_dir($path))
  97. {
  98. sfToolkit::clearDirectory($path);
  99. }
  100. else
  101. {
  102. @unlink($path);
  103. }
  104. }
  105. }
  106. /**
  107. * @see sfCache
  108. */
  109. public function clean($mode = sfCache::ALL)
  110. {
  111. if (!is_dir($this->getOption('cache_dir')))
  112. {
  113. return true;
  114. }
  115. $result = true;
  116. foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->getOption('cache_dir'))) as $file)
  117. {
  118. if (sfCache::ALL == $mode || !$this->isValid($file))
  119. {
  120. $result = @unlink($file) && $result;
  121. }
  122. }
  123. return $result;
  124. }
  125. /**
  126. * @see sfCache
  127. */
  128. public function getTimeout($key)
  129. {
  130. $path = $this->getFilePath($key);
  131. if (!file_exists($path))
  132. {
  133. return 0;
  134. }
  135. $data = $this->read($path, self::READ_TIMEOUT);
  136. return $data[self::READ_TIMEOUT] < time() ? 0 : $data[self::READ_TIMEOUT];
  137. }
  138. /**
  139. * @see sfCache
  140. */
  141. public function getLastModified($key)
  142. {
  143. $path = $this->getFilePath($key);
  144. if (!file_exists($path))
  145. {
  146. return 0;
  147. }
  148. $data = $this->read($path, self::READ_TIMEOUT | self::READ_LAST_MODIFIED);
  149. if ($data[self::READ_TIMEOUT] < time())
  150. {
  151. return 0;
  152. }
  153. return $data[self::READ_LAST_MODIFIED];
  154. }
  155. protected function isValid($path)
  156. {
  157. $data = $this->read($path, self::READ_TIMEOUT);
  158. return time() < $data[self::READ_TIMEOUT];
  159. }
  160. /**
  161. * Converts a cache key to a full path.
  162. *
  163. * @param string $key The cache key
  164. *
  165. * @return string The full path to the cache file
  166. */
  167. protected function getFilePath($key)
  168. {
  169. return $this->getOption('cache_dir').DIRECTORY_SEPARATOR.str_replace(sfCache::SEPARATOR, DIRECTORY_SEPARATOR, $key).self::EXTENSION;
  170. }
  171. /**
  172. * Reads the cache file and returns the content.
  173. *
  174. * @param string $path The file path
  175. * @param mixed $type The type of data you want to be returned
  176. * sfFileCache::READ_DATA: The cache content
  177. * sfFileCache::READ_TIMEOUT: The timeout
  178. * sfFileCache::READ_LAST_MODIFIED: The last modification timestamp
  179. *
  180. * @return array the (meta)data of the cache file. E.g. $data[sfFileCache::READ_DATA]
  181. *
  182. * @throws sfCacheException
  183. */
  184. protected function read($path, $type = self::READ_DATA)
  185. {
  186. if (!$fp = @fopen($path, 'rb'))
  187. {
  188. throw new sfCacheException(sprintf('Unable to read cache file "%s".', $path));
  189. }
  190. @flock($fp, LOCK_SH);
  191. $data[self::READ_TIMEOUT] = intval(@stream_get_contents($fp, 12, 0));
  192. if ($type != self::READ_TIMEOUT && time() < $data[self::READ_TIMEOUT])
  193. {
  194. if ($type & self::READ_LAST_MODIFIED)
  195. {
  196. $data[self::READ_LAST_MODIFIED] = intval(@stream_get_contents($fp, 12, 12));
  197. }
  198. if ($type & self::READ_DATA)
  199. {
  200. fseek($fp, 0, SEEK_END);
  201. $length = ftell($fp) - 24;
  202. fseek($fp, 24);
  203. $data[self::READ_DATA] = @fread($fp, $length);
  204. }
  205. }
  206. else
  207. {
  208. $data[self::READ_LAST_MODIFIED] = null;
  209. $data[self::READ_DATA] = null;
  210. }
  211. @flock($fp, LOCK_UN);
  212. @fclose($fp);
  213. return $data;
  214. }
  215. /**
  216. * Writes the given data in the cache file.
  217. *
  218. * @param string $path The file path
  219. * @param string $data The data to put in cache
  220. * @param integer $timeout The timeout timestamp
  221. *
  222. * @return boolean true if ok, otherwise false
  223. *
  224. * @throws sfCacheException
  225. */
  226. protected function write($path, $data, $timeout)
  227. {
  228. $current_umask = umask();
  229. umask(0000);
  230. if (!is_dir(dirname($path)))
  231. {
  232. // create directory structure if needed
  233. mkdir(dirname($path), 0777, true);
  234. }
  235. $tmpFile = tempnam(dirname($path), basename($path));
  236. if (!$fp = @fopen($tmpFile, 'wb'))
  237. {
  238. throw new sfCacheException(sprintf('Unable to write cache file "%s".', $tmpFile));
  239. }
  240. @fwrite($fp, str_pad($timeout, 12, 0, STR_PAD_LEFT));
  241. @fwrite($fp, str_pad(time(), 12, 0, STR_PAD_LEFT));
  242. @fwrite($fp, $data);
  243. @fclose($fp);
  244. // Hack from Agavi (http://trac.agavi.org/changeset/3979)
  245. // With php < 5.2.6 on win32, renaming to an already existing file doesn't work, but copy does,
  246. // so we simply assume that when rename() fails that we are on win32 and try to use copy()
  247. if (!@rename($tmpFile, $path))
  248. {
  249. if (copy($tmpFile, $path))
  250. {
  251. unlink($tmpFile);
  252. }
  253. }
  254. chmod($path, 0666);
  255. umask($current_umask);
  256. return true;
  257. }
  258. /**
  259. * Sets the cache root directory.
  260. *
  261. * @param string $cache_dir The directory where to put the cache files
  262. */
  263. protected function setcache_dir($cache_dir)
  264. {
  265. // remove last DIRECTORY_SEPARATOR
  266. if (DIRECTORY_SEPARATOR == substr($cache_dir, -1))
  267. {
  268. $cache_dir = substr($cache_dir, 0, -1);
  269. }
  270. // create cache dir if needed
  271. if (!is_dir($cache_dir))
  272. {
  273. $current_umask = umask(0000);
  274. @mkdir($cache_dir, 0777, true);
  275. umask($current_umask);
  276. }
  277. }
  278. }

Debug toolbar