1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
17: class Horde_Cache_Storage_File extends Horde_Cache_Storage_Base
18: {
19:
20: const GC_FILE = 'horde_cache_gc';
21:
22: 23: 24: 25: 26:
27: protected $_dir;
28:
29: 30: 31: 32: 33:
34: protected $_file = array();
35:
36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49:
50: public function __construct(array $params = array())
51: {
52: $params = array_merge(array(
53: 'prefix' => 'cache_',
54: 'sub' => 0
55: ), $params);
56:
57: $this->_dir = (isset($params['dir']) && @is_dir($params['dir']))
58: ? $params['dir']
59: : Horde_Util::getTempDir();
60:
61: parent::__construct($params);
62: }
63:
64: 65: 66:
67: public function __destruct()
68: {
69:
70: if (rand(0, 999) != 0) {
71: return;
72: }
73:
74: $filename = $this->_dir . '/' . self::GC_FILE;
75: $excepts = array();
76:
77: if (is_readable($filename)) {
78: $gc_file = file($filename, FILE_IGNORE_NEW_LINES);
79: reset($gc_file);
80: next($gc_file);
81: while (list(,$data) = each($gc_file)) {
82: $parts = explode("\t", $data, 2);
83: $excepts[$parts[0]] = $parts[1];
84: }
85: }
86:
87: $c_time = time();
88:
89: foreach ($this->_getCacheFiles() as $fname => $pname) {
90: $d_time = isset($excepts[$fname])
91: ? $excepts[$fname]
92: : $this->_params['lifetime'];
93:
94: if (!empty($d_time) &&
95: (($c_time - $d_time) > filemtime($pname))) {
96: @unlink($pname);
97: unset($excepts[$fname]);
98: }
99: }
100:
101: if ($fp = @fopen($filename, 'w')) {
102: foreach ($excepts as $key => $val) {
103: fwrite($fp, $key . "\t" . $val . "\n");
104: }
105: fclose($fp);
106: }
107: }
108:
109: 110:
111: public function get($key, $lifetime = 0)
112: {
113: if (!$this->exists($key, $lifetime)) {
114:
115: return false;
116: }
117:
118: $filename = $this->_keyToFile($key);
119: $size = filesize($filename);
120:
121: return $size
122: ? @file_get_contents($filename)
123: : '';
124: }
125:
126: 127:
128: public function set($key, $data, $lifetime = 0)
129: {
130: $filename = $this->_keyToFile($key, true);
131: $tmp_file = Horde_Util::getTempFile('HordeCache', true, $this->_dir);
132: if (isset($this->_params['umask'])) {
133: chmod($tmp_file, 0666 & ~$this->_params['umask']);
134: }
135:
136: if (file_put_contents($tmp_file, $data) === false) {
137: throw new Horde_Cache_Exception('Cannot write to cache directory ' . $this->_dir);
138: }
139:
140: @rename($tmp_file, $filename);
141:
142: if (($lifetime != $this->_params['lifetime']) &&
143: ($fp = @fopen($this->_dir . '/' . self::GC_FILE, 'a'))) {
144:
145:
146:
147: fwrite($fp, $filename . "\t" . (empty($lifetime) ? 0 : time() + $lifetime) . "\n");
148: fclose($fp);
149: }
150: }
151:
152: 153:
154: public function exists($key, $lifetime = 0)
155: {
156: $filename = $this->_keyToFile($key);
157:
158:
159: if (file_exists($filename)) {
160: 161: 162:
163: if (($lifetime == 0) ||
164: (time() - $lifetime <= filemtime($filename))) {
165: return true;
166: }
167:
168: @unlink($filename);
169: }
170:
171: return false;
172: }
173:
174: 175:
176: public function expire($key)
177: {
178: $filename = $this->_keyToFile($key);
179: return @unlink($filename);
180: }
181:
182: 183:
184: public function clear()
185: {
186: foreach ($this->_getCacheFiles() as $val) {
187: @unlink($val);
188: }
189: @unlink($this->_dir . '/' . self::GC_FILE);
190: }
191:
192: 193: 194: 195: 196:
197: protected function _getCacheFiles()
198: {
199: $paths = array();
200:
201: try {
202: $it = empty($this->_params['sub'])
203: ? new DirectoryIterator($this->_dir)
204: : new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->_dir), RecursiveIteratorIterator::CHILD_FIRST);
205: } catch (UnexpectedValueException $e) {
206: return $paths;
207: }
208:
209: foreach ($it as $val) {
210: if (!$val->isDir() &&
211: ($fname = $val->getFilename()) &&
212: (strpos($fname, $this->_params['prefix']) === 0)) {
213: $paths[$fname] = $val->getPathname();
214: }
215: }
216:
217: return $paths;
218: }
219:
220: 221: 222: 223: 224: 225: 226: 227:
228: protected function _keyToFile($key, $create = false)
229: {
230: if ($create || !isset($this->_file[$key])) {
231: $dir = $this->_dir . '/';
232: $md5 = hash('md5', $key);
233: $sub = '';
234:
235: if (!empty($this->_params['sub'])) {
236: $max = min($this->_params['sub'], strlen($md5));
237: for ($i = 0; $i < $max; $i++) {
238: $sub .= $md5[$i];
239: if ($create && !is_dir($dir . $sub)) {
240: if (!mkdir($dir . $sub)) {
241: $sub = '';
242: break;
243: }
244: }
245: $sub .= '/';
246: }
247: }
248:
249: $this->_file[$key] = $dir . $sub . $this->_params['prefix'] . $md5;
250: }
251:
252: return $this->_file[$key];
253: }
254:
255: }
256: