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

/Core/Libs/Lib/Cache/Engine/NamespaceFileCacheEngine.php

http://github.com/infinitas/infinitas
PHP | 362 lines | 215 code | 33 blank | 114 comment | 29 complexity | c69ff4c2bb7fa1ef28696dc34cd123d4 MD5 | raw file
  1. <?php
  2. /**
  3. * Partial reimplementation of Cake's original FileEngine. This
  4. * engine supports namespaces similar to:
  5. *
  6. * Cache::write('app.pictures.recent', $recentPictures, 'app');
  7. * Cache::write('app.pictures.top', $topPictures, 'app');
  8. *
  9. * This namespacing allows deletion of parent namespaces like:
  10. *
  11. * Cache::delete('app.pictures', 'app');
  12. *
  13. * The settings for this cache should be
  14. * defined at the bottom of /config/bootstrap.php like the following:
  15. *
  16. * App::import('Vendor', 'NamespaceFile');
  17. * Cache::config('app', array('engine' => 'NamespaceFile', 'duration'=> '+1 hour', 'prefix' => 'cake.'));
  18. *
  19. * Place this file in APP/vendors/.
  20. *
  21. * WARNING: DO NOT USE AS YOUR APP'S DEFAULT CACHE!
  22. *
  23. * @author Frank de Graaf (Phally)
  24. * @link http://www.frankdegraaf.net
  25. * @license MIT license
  26. */
  27. class NamespaceFileEngine extends CacheEngine {
  28. /**
  29. * Instance of File class
  30. *
  31. * @var File
  32. */
  33. public $File = null;
  34. /**
  35. * settings
  36. * path = absolute path to cache directory, default => CACHE
  37. * prefix = string prefix for filename, default => cake.
  38. * lock = enable file locking on write, default => false
  39. * serialize = serialize the data, default => true
  40. *
  41. * @var array
  42. * @see CacheEngine::__defaults
  43. */
  44. public $settings = array();
  45. /**
  46. * Set to true if NamespaceFileEngine::init(); and NamespaceFileEngine::__active(); do not fail.
  47. *
  48. * @var boolean
  49. */
  50. private $__active = false;
  51. /**
  52. * True unless NamespaceFileEngine::__active(); fails
  53. *
  54. * @var boolean
  55. */
  56. private $__init = true;
  57. /**
  58. * Initialize the Cache Engine
  59. *
  60. * Called automatically by the cache frontend
  61. * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
  62. *
  63. * @param array $setting array of setting for the engine
  64. *
  65. * @return boolean
  66. */
  67. function init($settings = array()) {
  68. parent::init(
  69. array_merge(
  70. array(
  71. 'engine' => 'NamespaceFile', 'path' => CACHE, 'prefix' => 'cake.', 'lock' => false,
  72. 'serialize' => true, 'isWindows' => false
  73. ),
  74. $settings
  75. )
  76. );
  77. if (!isset($this->File)) {
  78. if (!class_exists('File')) {
  79. App::import('File');
  80. }
  81. $this->File = new File($this->settings['path']);
  82. }
  83. if (DS === '\\') {
  84. $this->settings['isWindows'] = true;
  85. }
  86. return $this->__active();
  87. }
  88. /**
  89. * Garbage collection. Permanently remove all expired and deleted data
  90. *
  91. * @return boolean
  92. */
  93. function gc() {
  94. return $this->clear(true);
  95. }
  96. /**
  97. * Write data for key into cache
  98. *
  99. * @param string $key Identifier for the data
  100. * @param mixed $data Data to be cached
  101. * @param mixed $duration How long to cache the data, in seconds
  102. *
  103. * @return boolean
  104. */
  105. public function write($key, $value, $duration) {
  106. $writable = $this->__setKey($key);
  107. if (!is_a($writable, 'File') || !$this->__init || $data === '') {
  108. return false;
  109. }
  110. if (!$writable->exists()) {
  111. $writable->Folder->create($this->__getFolderPath($key));
  112. $writable->create();
  113. }
  114. $lineBreak = "\n";
  115. if ($this->settings['isWindows']) {
  116. $lineBreak = "\r\n";
  117. }
  118. if (!empty($this->settings['serialize'])) {
  119. if ($this->settings['isWindows']) {
  120. $data = str_replace('\\', '\\\\\\\\', serialize($data));
  121. }
  122. else {
  123. $data = serialize($data);
  124. }
  125. }
  126. if ($this->settings['lock']) {
  127. $writable->lock = true;
  128. }
  129. $expires = time() + $duration;
  130. $contents = $expires . $lineBreak . $data . $lineBreak;
  131. $success = $writable->write($contents);
  132. $writable->close();
  133. return $success;
  134. }
  135. /**
  136. * Increment a number under the key and return incremented value
  137. *
  138. * @param string $key Identifier for the data
  139. * @param integer $offset How much to add
  140. * @return New incremented value, false otherwise
  141. */
  142. public function increment($key, $offset = 1) {
  143. throw new CacheException(__d('cake_dev', 'Files cannot be atomically incremented.'));
  144. }
  145. /**
  146. * Decrement a number under the key and return decremented value
  147. *
  148. * @param string $key Identifier for the data
  149. * @param integer $offset How much to subtract
  150. * @return New incremented value, false otherwise
  151. */
  152. public function decrement($key, $offset = 1) {
  153. throw new CacheException(__d('cake_dev', 'Files cannot be atomically decremented.'));
  154. }
  155. /**
  156. * Read a key from the cache
  157. *
  158. * @param string $key Identifier for the data
  159. *
  160. * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
  161. */
  162. function read($key) {
  163. $readable = $this->__setKey($key);
  164. if (!is_a($readable, 'File') || !$this->__init || !$readable->exists()) {
  165. return false;
  166. }
  167. if ($this->settings['lock']) {
  168. $readable->lock = true;
  169. }
  170. $time = time();
  171. $cachetime = intval($readable->read(11));
  172. if ($cachetime !== false && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) {
  173. $readable->close();
  174. $readable->delete();
  175. return false;
  176. }
  177. $data = $readable->read(true);
  178. if ($data !== '' && !empty($this->settings['serialize'])) {
  179. if ($this->settings['isWindows']) {
  180. $data = str_replace('\\\\\\\\', '\\', $data);
  181. }
  182. $data = unserialize((string) $data);
  183. }
  184. $readable->close();
  185. return $data;
  186. }
  187. /**
  188. * Delete a key from the cache
  189. *
  190. * @param string $key Identifier for the data
  191. *
  192. * @return boolean
  193. */
  194. function delete($key) {
  195. $deletable = $this->__setKey($key);
  196. if ($deletable === false) {
  197. return false;
  198. }
  199. $deletable->delete();
  200. }
  201. /**
  202. * Delete all values from the cache
  203. *
  204. * @param boolean $check Optional - only delete expired cache items
  205. *
  206. * @return boolean
  207. */
  208. function clear($check = false) {
  209. if (!$this->__init) {
  210. return false;
  211. }
  212. $tree = $this->File->Folder->tree($this->settings['path'] . substr($this->settings['prefix'], 0, -1) . DS);
  213. foreach ($tree[1] as $file) {
  214. $deletable = $this->__setPath($file);
  215. if ($check) {
  216. $now = time();
  217. $threshold = $now - $this->settings['duration'];
  218. $mtime = $deletable->lastChange();
  219. if ($mtime === false || $mtime > $threshold) {
  220. continue;
  221. }
  222. $expires = $deletable->read(11);
  223. $deletable->close();
  224. if ($expires > $now) {
  225. continue;
  226. }
  227. }
  228. $deletable->delete();
  229. }
  230. $tree[0] = array_reverse($tree[0]);
  231. foreach ($tree[0] as $folder) {
  232. $deletable = $this->__setPath($folder);
  233. if (!is_a($deletable, 'Folder')) {
  234. continue;
  235. }
  236. $contents = $deletable->read();
  237. if (empty($contents[0]) && empty($contents[1])) {
  238. $deletable->delete();
  239. }
  240. }
  241. }
  242. /**
  243. * Get absolute file for a given key
  244. *
  245. * @param string $key The key
  246. *
  247. * @return mixed Absolute cache file for the given key or false if erroneous
  248. */
  249. function __getPath($key) {
  250. return $this->settings['path'] . str_replace('.', DS, $key);
  251. }
  252. /**
  253. * Get absolute folder for a given key
  254. *
  255. * @param string $key The key
  256. *
  257. * @return mixed Absolute cache file for the given key or false if erroneous
  258. */
  259. function __getFolderPath($key) {
  260. return $this->__getPath(substr($key, 0, strrpos($key, '.') + 1));
  261. }
  262. /**
  263. * Return the class (File/Folder) for a key.
  264. *
  265. * @param string $key The key
  266. *
  267. * @return mixed File or Folder class for a key.
  268. */
  269. function __setKey($key) {
  270. return $this->__setPath($this->__getPath($key));
  271. }
  272. /**
  273. * Return the class (File/Folder) for a path.
  274. *
  275. * @param string $path The path
  276. *
  277. * @return mixed File or Folder class for a path.
  278. */
  279. function __setPath($path) {
  280. $this->File->Folder->cd($path);
  281. if (is_dir($path)) {
  282. $object = $this->File->Folder;
  283. return $object;
  284. }
  285. $this->File->path = $path;
  286. $object = $this->File;
  287. return $object;
  288. }
  289. /**
  290. * Determine is cache directory is writable
  291. *
  292. * @return boolean
  293. */
  294. function __active() {
  295. if (!$this->__active && $this->__init && !is_writable($this->settings['path'])) {
  296. $this->__init = false;
  297. trigger_error(sprintf(__d('libs', '%s is not writable'), $this->settings['path']), E_USER_WARNING);
  298. }
  299. else {
  300. $this->__active = true;
  301. }
  302. return true;
  303. }
  304. /**
  305. * Override the parents key method, so the keys don't get transformed
  306. *
  307. * @param string $key The key
  308. *
  309. * @return mixed The key if one is passed else return false
  310. */
  311. function key($key = null) {
  312. if (empty($key)) {
  313. return false;
  314. }
  315. return $key;
  316. }
  317. }