1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
22:
23: 24: 25: 26: 27: 28: 29: 30: 31:
32: class FileEngine extends CacheEngine {
33:
34: 35: 36: 37: 38:
39: protected $_File = null;
40:
41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51:
52: public $settings = array();
53:
54: 55: 56: 57: 58:
59: protected $_init = true;
60:
61: 62: 63: 64: 65: 66: 67: 68: 69:
70: public function init($settings = array()) {
71: $settings += array(
72: 'engine' => 'File',
73: 'path' => CACHE,
74: 'prefix' => 'cake_',
75: 'lock' => true,
76: 'serialize' => true,
77: 'isWindows' => false,
78: 'mask' => 0664
79: );
80: parent::init($settings);
81:
82: if (DS === '\\') {
83: $this->settings['isWindows'] = true;
84: }
85: if (substr($this->settings['path'], -1) !== DS) {
86: $this->settings['path'] .= DS;
87: }
88: if (!empty($this->_groupPrefix)) {
89: $this->_groupPrefix = str_replace('_', DS, $this->_groupPrefix);
90: }
91: return $this->_active();
92: }
93:
94: 95: 96: 97: 98: 99:
100: public function gc($expires = null) {
101: return $this->clear(true);
102: }
103:
104: 105: 106: 107: 108: 109: 110: 111:
112: public function write($key, $data, $duration) {
113: if ($data === '' || !$this->_init) {
114: return false;
115: }
116:
117: if ($this->_setKey($key, true) === false) {
118: return false;
119: }
120:
121: $lineBreak = "\n";
122:
123: if ($this->settings['isWindows']) {
124: $lineBreak = "\r\n";
125: }
126:
127: if (!empty($this->settings['serialize'])) {
128: if ($this->settings['isWindows']) {
129: $data = str_replace('\\', '\\\\\\\\', serialize($data));
130: } else {
131: $data = serialize($data);
132: }
133: }
134:
135: $expires = time() + $duration;
136: $contents = $expires . $lineBreak . $data . $lineBreak;
137:
138: if ($this->settings['lock']) {
139: $this->_File->flock(LOCK_EX);
140: }
141:
142: $this->_File->rewind();
143: $success = $this->_File->ftruncate(0) && $this->_File->fwrite($contents) && $this->_File->fflush();
144:
145: if ($this->settings['lock']) {
146: $this->_File->flock(LOCK_UN);
147: }
148:
149: return $success;
150: }
151:
152: 153: 154: 155: 156: 157:
158: public function read($key) {
159: if (!$this->_init || $this->_setKey($key) === false) {
160: return false;
161: }
162:
163: if ($this->settings['lock']) {
164: $this->_File->flock(LOCK_SH);
165: }
166:
167: $this->_File->rewind();
168: $time = time();
169: $cachetime = intval($this->_File->current());
170:
171: if ($cachetime !== false && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) {
172: if ($this->settings['lock']) {
173: $this->_File->flock(LOCK_UN);
174: }
175: return false;
176: }
177:
178: $data = '';
179: $this->_File->next();
180: while ($this->_File->valid()) {
181: $data .= $this->_File->current();
182: $this->_File->next();
183: }
184:
185: if ($this->settings['lock']) {
186: $this->_File->flock(LOCK_UN);
187: }
188:
189: $data = trim($data);
190:
191: if ($data !== '' && !empty($this->settings['serialize'])) {
192: if ($this->settings['isWindows']) {
193: $data = str_replace('\\\\\\\\', '\\', $data);
194: }
195: $data = unserialize((string)$data);
196: }
197: return $data;
198: }
199:
200: 201: 202: 203: 204: 205:
206: public function delete($key) {
207: if ($this->_setKey($key) === false || !$this->_init) {
208: return false;
209: }
210: $path = $this->_File->getRealPath();
211: $this->_File = null;
212: return unlink($path);
213: }
214:
215: 216: 217: 218: 219: 220:
221: public function clear($check) {
222: if (!$this->_init) {
223: return false;
224: }
225: $dir = dir($this->settings['path']);
226: if ($check) {
227: $now = time();
228: $threshold = $now - $this->settings['duration'];
229: }
230: $prefixLength = strlen($this->settings['prefix']);
231: while (($entry = $dir->read()) !== false) {
232: if (substr($entry, 0, $prefixLength) !== $this->settings['prefix']) {
233: continue;
234: }
235: if ($this->_setKey($entry) === false) {
236: continue;
237: }
238: if ($check) {
239: $mtime = $this->_File->getMTime();
240:
241: if ($mtime > $threshold) {
242: continue;
243: }
244:
245: $expires = (int)$this->_File->current();
246:
247: if ($expires > $now) {
248: continue;
249: }
250: }
251: $path = $this->_File->getRealPath();
252: $this->_File = null;
253: if (file_exists($path)) {
254: unlink($path);
255: }
256: }
257: $dir->close();
258: return true;
259: }
260:
261: 262: 263: 264: 265: 266: 267: 268:
269: public function decrement($key, $offset = 1) {
270: throw new CacheException(__d('cake_dev', 'Files cannot be atomically decremented.'));
271: }
272:
273: 274: 275: 276: 277: 278: 279: 280:
281: public function increment($key, $offset = 1) {
282: throw new CacheException(__d('cake_dev', 'Files cannot be atomically incremented.'));
283: }
284:
285: 286: 287: 288: 289: 290: 291: 292:
293: protected function _setKey($key, $createKey = false) {
294: $groups = null;
295: if (!empty($this->_groupPrefix)) {
296: $groups = vsprintf($this->_groupPrefix, $this->groups());
297: }
298: $dir = $this->settings['path'] . $groups;
299:
300: if (!is_dir($dir)) {
301: mkdir($dir, 0777, true);
302: }
303: $path = new SplFileInfo($dir . $key);
304:
305: if (!$createKey && !$path->isFile()) {
306: return false;
307: }
308: if (empty($this->_File) || $this->_File->getBaseName() !== $key) {
309: $exists = file_exists($path->getPathname());
310: try {
311: $this->_File = $path->openFile('c+');
312: } catch (Exception $e) {
313: trigger_error($e->getMessage(), E_USER_WARNING);
314: return false;
315: }
316: unset($path);
317:
318: if (!$exists && !chmod($this->_File->getPathname(), (int)$this->settings['mask'])) {
319: trigger_error(__d(
320: 'cake_dev', 'Could not apply permission mask "%s" on cache file "%s"',
321: array($this->_File->getPathname(), $this->settings['mask'])), E_USER_WARNING);
322: }
323: }
324: return true;
325: }
326:
327: 328: 329: 330: 331:
332: protected function _active() {
333: $dir = new SplFileInfo($this->settings['path']);
334: if ($this->_init && !($dir->isDir() && $dir->isWritable())) {
335: $this->_init = false;
336: trigger_error(__d('cake_dev', '%s is not writable', $this->settings['path']), E_USER_WARNING);
337: return false;
338: }
339: return true;
340: }
341:
342: 343: 344: 345: 346: 347:
348: public function key($key) {
349: if (empty($key)) {
350: return false;
351: }
352:
353: $key = Inflector::underscore(str_replace(array(DS, '/', '.'), '_', strval($key)));
354: return $key;
355: }
356:
357: 358: 359: 360: 361:
362: public function clearGroup($group) {
363: $directoryIterator = new RecursiveDirectoryIterator($this->settings['path']);
364: $contents = new RecursiveIteratorIterator($directoryIterator, RecursiveIteratorIterator::CHILD_FIRST);
365: foreach ($contents as $object) {
366: $containsGroup = strpos($object->getPathName(), DS . $group . DS) !== false;
367: if ($object->isFile() && $containsGroup) {
368: unlink($object->getPathName());
369: }
370: }
371: return true;
372: }
373: }
374: