/core/Cache/RubberFileSystem.class.php

https://github.com/crazedr0m/onphp-framework · PHP · 282 lines · 199 code · 56 blank · 27 comment · 30 complexity · cc96eb66ed02962d23997b10eb3b83dc MD5 · raw file

  1. <?php
  2. /***************************************************************************
  3. * Copyright (C) 2005-2008 by Konstantin V. Arkhipov *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU Lesser General Public License as *
  7. * published by the Free Software Foundation; either version 3 of the *
  8. * License, or (at your option) any later version. *
  9. * *
  10. ***************************************************************************/
  11. /**
  12. * Simple filesystem cache.
  13. *
  14. * @ingroup Cache
  15. **/
  16. final class RubberFileSystem extends CachePeer
  17. {
  18. private $directory = null;
  19. /**
  20. * @return RubberFileSystem
  21. **/
  22. public static function create($directory = 'cache/')
  23. {
  24. return new self($directory);
  25. }
  26. public function __construct($directory = 'cache/')
  27. {
  28. $directory = ONPHP_TEMP_PATH.$directory;
  29. if (!is_writable($directory)) {
  30. if (!mkdir($directory, 0700, true)) {
  31. throw new WrongArgumentException(
  32. "can not write to '{$directory}'"
  33. );
  34. }
  35. }
  36. if ($directory[strlen($directory) - 1] != DIRECTORY_SEPARATOR)
  37. $directory .= DIRECTORY_SEPARATOR;
  38. $this->directory = $directory;
  39. }
  40. public function isAlive()
  41. {
  42. if (!is_writable($this->directory))
  43. return mkdir($this->directory, 0700, true);
  44. else
  45. return true;
  46. }
  47. /**
  48. * @return RubberFileSystem
  49. **/
  50. public function clean()
  51. {
  52. // just to return 'true'
  53. FileUtils::removeDirectory($this->directory, true);
  54. return parent::clean();
  55. }
  56. public function increment($key, $value)
  57. {
  58. $path = $this->makePath($key);
  59. if (null !== ($current = $this->operate($path))) {
  60. $this->operate($path, $current += $value);
  61. return $current;
  62. }
  63. return null;
  64. }
  65. public function decrement($key, $value)
  66. {
  67. $path = $this->makePath($key);
  68. if (null !== ($current = $this->operate($path))) {
  69. $this->operate($path, $current -= $value);
  70. return $current;
  71. }
  72. return null;
  73. }
  74. public function get($key)
  75. {
  76. $path = $this->makePath($key);
  77. if (!is_readable($path)) {
  78. return null;
  79. }
  80. try {
  81. $fileTime = filemtime($path);
  82. } catch (BaseException $e) {
  83. $fileTime = null;
  84. }
  85. if ($fileTime === null) {
  86. return null;
  87. }
  88. if ($fileTime <= time()) {
  89. try {
  90. unlink($path);
  91. } catch (BaseException $e) {
  92. // we're in race with unexpected clean()
  93. }
  94. return null;
  95. }
  96. return $this->operate($path);
  97. }
  98. public function delete($key)
  99. {
  100. try {
  101. unlink($this->makePath($key));
  102. } catch (BaseException $e) {
  103. return false;
  104. }
  105. return true;
  106. }
  107. public function append($key, $data)
  108. {
  109. $path = $this->makePath($key);
  110. $directory = dirname($path);
  111. if (!file_exists($directory)) {
  112. try {
  113. mkdir($directory);
  114. } catch (BaseException $e) {
  115. // we're in race
  116. }
  117. }
  118. if (!is_writable($path))
  119. return false;
  120. try {
  121. $fp = fopen($path, 'ab');
  122. } catch (BaseException $e) {
  123. return false;
  124. }
  125. fwrite($fp, $data);
  126. fclose($fp);
  127. return true;
  128. }
  129. protected function store($action, $key, $value, $expires = 0)
  130. {
  131. $path = $this->makePath($key);
  132. $time = time();
  133. $directory = dirname($path);
  134. if (!file_exists($directory)) {
  135. try {
  136. mkdir($directory);
  137. } catch (BaseException $e) {
  138. // we're in race
  139. }
  140. }
  141. // do not add, if file exist and not expired
  142. if (
  143. $action == 'add'
  144. && is_readable($path)
  145. && filemtime($path) > $time
  146. )
  147. return true;
  148. // do not replace, when file not exist or expired
  149. if ($action == 'replace') {
  150. if (!is_readable($path)) {
  151. return false;
  152. } elseif (filemtime($path) <= $time) {
  153. $this->delete($key);
  154. return false;
  155. }
  156. }
  157. $this->operate($path, $value, $expires);
  158. return true;
  159. }
  160. private function operate($path, $value = null, $expires = null)
  161. {
  162. $key = hexdec(substr(md5($path), 3, 2)) + 1;
  163. $pool = SemaphorePool::me();
  164. if (!$pool->get($key)) {
  165. return null;
  166. }
  167. $result = $this->doOperate($path, $value, $expires);
  168. $free = $pool->free($key);
  169. if ($value !== null) {
  170. $result = $free;
  171. }
  172. return $result;
  173. }
  174. private function doOperate($path, $value = null, $expires = null)
  175. {
  176. $tmp = null;
  177. try {
  178. if ($value === null) {
  179. $fp = fopen($path, 'rb');
  180. } else {
  181. $old = umask(0077);
  182. $tmp = FileUtils::makeTempFile();
  183. $fp = fopen($tmp, 'wb');
  184. umask($old);
  185. }
  186. } catch (BaseException $e) {
  187. if (isset($fp) && $fp) {
  188. fclose($fp);
  189. }
  190. if ($tmp) {
  191. unlink($tmp);
  192. }
  193. return null;
  194. }
  195. if ($value === null) {
  196. $size = filesize($path);
  197. if ($size > 0)
  198. $data = fread($fp, $size);
  199. else
  200. $data = null;
  201. fclose($fp);
  202. return $data ? $this->restoreData($data) : null;
  203. }
  204. fwrite($fp, $this->prepareData($value));
  205. fflush($fp);
  206. fclose($fp);
  207. rename($tmp, $path);
  208. if ($expires < parent::TIME_SWITCH)
  209. $expires += time();
  210. try {
  211. touch($path, $expires);
  212. } catch (BaseException $e) {
  213. // race-removed
  214. }
  215. return;
  216. }
  217. private function makePath($key)
  218. {
  219. return
  220. $this->directory
  221. .$key[0].$key[1]
  222. .DIRECTORY_SEPARATOR
  223. .substr($key, 2);
  224. }
  225. }
  226. ?>