PageRenderTime 47ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Cake/Cache/Engine/FileEngine.php

https://bitbucket.org/udeshika/fake_twitter
PHP | 325 lines | 201 code | 24 blank | 100 comment | 34 complexity | 338a2bb479c01cf0165587d5f8d9e7e5 MD5 | raw file
  1. <?php
  2. /**
  3. * File Storage engine for cache. Filestorage is the slowest cache storage
  4. * to read and write. However, it is good for servers that don't have other storage
  5. * engine available, or have content which is not performance sensitive.
  6. *
  7. * You can configure a FileEngine cache, using Cache::config()
  8. *
  9. * PHP 5
  10. *
  11. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  12. * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  13. *
  14. * Licensed under The MIT License
  15. * Redistributions of files must retain the above copyright notice.
  16. *
  17. * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  18. * @link http://cakephp.org CakePHP(tm) Project
  19. * @since CakePHP(tm) v 1.2.0.4933
  20. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  21. */
  22. /**
  23. * File Storage engine for cache. Filestorage is the slowest cache storage
  24. * to read and write. However, it is good for servers that don't have other storage
  25. * engine available, or have content which is not performance sensitive.
  26. *
  27. * You can configure a FileEngine cache, using Cache::config()
  28. *
  29. * @package Cake.Cache.Engine
  30. */
  31. class FileEngine extends CacheEngine {
  32. /**
  33. * Instance of SplFileObject class
  34. *
  35. * @var File
  36. */
  37. protected $_File = null;
  38. /**
  39. * Settings
  40. *
  41. * - path = absolute path to cache directory, default => CACHE
  42. * - prefix = string prefix for filename, default => cake_
  43. * - lock = enable file locking on write, default => false
  44. * - serialize = serialize the data, default => true
  45. *
  46. * @var array
  47. * @see CacheEngine::__defaults
  48. */
  49. public $settings = array();
  50. /**
  51. * True unless FileEngine::__active(); fails
  52. *
  53. * @var boolean
  54. */
  55. protected $_init = true;
  56. /**
  57. * Initialize the Cache Engine
  58. *
  59. * Called automatically by the cache frontend
  60. * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
  61. *
  62. * @param array $settings array of setting for the engine
  63. * @return boolean True if the engine has been successfully initialized, false if not
  64. */
  65. public function init($settings = array()) {
  66. parent::init(array_merge(
  67. array(
  68. 'engine' => 'File', 'path' => CACHE, 'prefix' => 'cake_', 'lock' => true,
  69. 'serialize' => true, 'isWindows' => false, 'mask' => 0664
  70. ),
  71. $settings
  72. ));
  73. if (DS === '\\') {
  74. $this->settings['isWindows'] = true;
  75. }
  76. if (substr($this->settings['path'], -1) !== DS) {
  77. $this->settings['path'] .= DS;
  78. }
  79. return $this->_active();
  80. }
  81. /**
  82. * Garbage collection. Permanently remove all expired and deleted data
  83. *
  84. * @return boolean True if garbage collection was successful, false on failure
  85. */
  86. public function gc() {
  87. return $this->clear(true);
  88. }
  89. /**
  90. * Write data for key into cache
  91. *
  92. * @param string $key Identifier for the data
  93. * @param mixed $data Data to be cached
  94. * @param mixed $duration How long to cache the data, in seconds
  95. * @return boolean True if the data was successfully cached, false on failure
  96. */
  97. public function write($key, $data, $duration) {
  98. if ($data === '' || !$this->_init) {
  99. return false;
  100. }
  101. if ($this->_setKey($key, true) === false) {
  102. return false;
  103. }
  104. $lineBreak = "\n";
  105. if ($this->settings['isWindows']) {
  106. $lineBreak = "\r\n";
  107. }
  108. if (!empty($this->settings['serialize'])) {
  109. if ($this->settings['isWindows']) {
  110. $data = str_replace('\\', '\\\\\\\\', serialize($data));
  111. } else {
  112. $data = serialize($data);
  113. }
  114. }
  115. $expires = time() + $duration;
  116. $contents = $expires . $lineBreak . $data . $lineBreak;
  117. if ($this->settings['lock']) {
  118. $this->_File->flock(LOCK_EX);
  119. }
  120. $this->_File->rewind();
  121. $success = $this->_File->ftruncate(0) && $this->_File->fwrite($contents) && $this->_File->fflush();
  122. if ($this->settings['lock']) {
  123. $this->_File->flock(LOCK_UN);
  124. }
  125. return $success;
  126. }
  127. /**
  128. * Read a key from the cache
  129. *
  130. * @param string $key Identifier for the data
  131. * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
  132. */
  133. public function read($key) {
  134. if (!$this->_init || $this->_setKey($key) === false) {
  135. return false;
  136. }
  137. if ($this->settings['lock']) {
  138. $this->_File->flock(LOCK_SH);
  139. }
  140. $this->_File->rewind();
  141. $time = time();
  142. $cachetime = intval($this->_File->current());
  143. if ($cachetime !== false && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) {
  144. if ($this->settings['lock']) {
  145. $this->_File->flock(LOCK_UN);
  146. }
  147. return false;
  148. }
  149. $data = '';
  150. $this->_File->next();
  151. while ($this->_File->valid()) {
  152. $data .= $this->_File->current();
  153. $this->_File->next();
  154. }
  155. if ($this->settings['lock']) {
  156. $this->_File->flock(LOCK_UN);
  157. }
  158. $data = trim($data);
  159. if ($data !== '' && !empty($this->settings['serialize'])) {
  160. if ($this->settings['isWindows']) {
  161. $data = str_replace('\\\\\\\\', '\\', $data);
  162. }
  163. $data = unserialize((string)$data);
  164. }
  165. return $data;
  166. }
  167. /**
  168. * Delete a key from the cache
  169. *
  170. * @param string $key Identifier for the data
  171. * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
  172. */
  173. public function delete($key) {
  174. if ($this->_setKey($key) === false || !$this->_init) {
  175. return false;
  176. }
  177. $path = $this->_File->getRealPath();
  178. $this->_File = null;
  179. return unlink($path);
  180. }
  181. /**
  182. * Delete all values from the cache
  183. *
  184. * @param boolean $check Optional - only delete expired cache items
  185. * @return boolean True if the cache was successfully cleared, false otherwise
  186. */
  187. public function clear($check) {
  188. if (!$this->_init) {
  189. return false;
  190. }
  191. $dir = dir($this->settings['path']);
  192. if ($check) {
  193. $now = time();
  194. $threshold = $now - $this->settings['duration'];
  195. }
  196. $prefixLength = strlen($this->settings['prefix']);
  197. while (($entry = $dir->read()) !== false) {
  198. if (substr($entry, 0, $prefixLength) !== $this->settings['prefix']) {
  199. continue;
  200. }
  201. if ($this->_setKey($entry) === false) {
  202. continue;
  203. }
  204. if ($check) {
  205. $mtime = $this->_File->getMTime();
  206. if ($mtime > $threshold) {
  207. continue;
  208. }
  209. $expires = (int)$this->_File->current();
  210. if ($expires > $now) {
  211. continue;
  212. }
  213. }
  214. $path = $this->_File->getRealPath();
  215. $this->_File = null;
  216. if (file_exists($path)) {
  217. unlink($path);
  218. }
  219. }
  220. $dir->close();
  221. return true;
  222. }
  223. /**
  224. * Not implemented
  225. *
  226. * @param string $key
  227. * @param integer $offset
  228. * @return void
  229. * @throws CacheException
  230. */
  231. public function decrement($key, $offset = 1) {
  232. throw new CacheException(__d('cake_dev', 'Files cannot be atomically decremented.'));
  233. }
  234. /**
  235. * Not implemented
  236. *
  237. * @param string $key
  238. * @param integer $offset
  239. * @return void
  240. * @throws CacheException
  241. */
  242. public function increment($key, $offset = 1) {
  243. throw new CacheException(__d('cake_dev', 'Files cannot be atomically incremented.'));
  244. }
  245. /**
  246. * Sets the current cache key this class is managing, and creates a writable SplFileObject
  247. * for the cache file the key is referring to.
  248. *
  249. * @param string $key The key
  250. * @param boolean $createKey Whether the key should be created if it doesn't exists, or not
  251. * @return boolean true if the cache key could be set, false otherwise
  252. */
  253. protected function _setKey($key, $createKey = false) {
  254. $path = new SplFileInfo($this->settings['path'] . $key);
  255. if (!$createKey && !$path->isFile()) {
  256. return false;
  257. }
  258. if (empty($this->_File) || $this->_File->getBaseName() !== $key) {
  259. $exists = file_exists($path->getPathname());
  260. try {
  261. $this->_File = $path->openFile('c+');
  262. } catch (Exception $e) {
  263. trigger_error($e->getMessage(), E_USER_WARNING);
  264. return false;
  265. }
  266. unset($path);
  267. if (!$exists && !chmod($this->_File->getPathname(), (int) $this->settings['mask'])) {
  268. trigger_error(__d(
  269. 'cake_dev', 'Could not apply permission mask "%s" on cache file "%s"',
  270. array($this->_File->getPathname(), $this->settings['mask'])), E_USER_WARNING);
  271. }
  272. }
  273. return true;
  274. }
  275. /**
  276. * Determine is cache directory is writable
  277. *
  278. * @return boolean
  279. */
  280. protected function _active() {
  281. $dir = new SplFileInfo($this->settings['path']);
  282. if ($this->_init && !($dir->isDir() && $dir->isWritable())) {
  283. $this->_init = false;
  284. trigger_error(__d('cake_dev', '%s is not writable', $this->settings['path']), E_USER_WARNING);
  285. return false;
  286. }
  287. return true;
  288. }
  289. }