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: parent::init(array_merge(
72: array(
73: 'engine' => 'File', 'path' => CACHE, 'prefix' => 'cake_', 'lock' => true,
74: 'serialize' => true, 'isWindows' => false, 'mask' => 0664
75: ),
76: $settings
77: ));
78:
79: if (DS === '\\') {
80: $this->settings['isWindows'] = true;
81: }
82: if (substr($this->settings['path'], -1) !== DS) {
83: $this->settings['path'] .= DS;
84: }
85: return $this->_active();
86: }
87:
88: 89: 90: 91: 92:
93: public function gc() {
94: return $this->clear(true);
95: }
96:
97: 98: 99: 100: 101: 102: 103: 104:
105: public function write($key, $data, $duration) {
106: if ($data === '' || !$this->_init) {
107: return false;
108: }
109:
110: if ($this->_setKey($key, true) === false) {
111: return false;
112: }
113:
114: $lineBreak = "\n";
115:
116: if ($this->settings['isWindows']) {
117: $lineBreak = "\r\n";
118: }
119:
120: if (!empty($this->settings['serialize'])) {
121: if ($this->settings['isWindows']) {
122: $data = str_replace('\\', '\\\\\\\\', serialize($data));
123: } else {
124: $data = serialize($data);
125: }
126: }
127:
128: $expires = time() + $duration;
129: $contents = $expires . $lineBreak . $data . $lineBreak;
130:
131: if ($this->settings['lock']) {
132: $this->_File->flock(LOCK_EX);
133: }
134:
135: $this->_File->rewind();
136: $success = $this->_File->ftruncate(0) && $this->_File->fwrite($contents) && $this->_File->fflush();
137:
138: if ($this->settings['lock']) {
139: $this->_File->flock(LOCK_UN);
140: }
141:
142: return $success;
143: }
144:
145: 146: 147: 148: 149: 150:
151: public function read($key) {
152: if (!$this->_init || $this->_setKey($key) === false) {
153: return false;
154: }
155:
156: if ($this->settings['lock']) {
157: $this->_File->flock(LOCK_SH);
158: }
159:
160: $this->_File->rewind();
161: $time = time();
162: $cachetime = intval($this->_File->current());
163:
164: if ($cachetime !== false && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) {
165: if ($this->settings['lock']) {
166: $this->_File->flock(LOCK_UN);
167: }
168: return false;
169: }
170:
171: $data = '';
172: $this->_File->next();
173: while ($this->_File->valid()) {
174: $data .= $this->_File->current();
175: $this->_File->next();
176: }
177:
178: if ($this->settings['lock']) {
179: $this->_File->flock(LOCK_UN);
180: }
181:
182: $data = trim($data);
183:
184: if ($data !== '' && !empty($this->settings['serialize'])) {
185: if ($this->settings['isWindows']) {
186: $data = str_replace('\\\\\\\\', '\\', $data);
187: }
188: $data = unserialize((string)$data);
189: }
190: return $data;
191: }
192:
193: 194: 195: 196: 197: 198:
199: public function delete($key) {
200: if ($this->_setKey($key) === false || !$this->_init) {
201: return false;
202: }
203: $path = $this->_File->getRealPath();
204: $this->_File = null;
205: return unlink($path);
206: }
207:
208: 209: 210: 211: 212: 213:
214: public function clear($check) {
215: if (!$this->_init) {
216: return false;
217: }
218: $dir = dir($this->settings['path']);
219: if ($check) {
220: $now = time();
221: $threshold = $now - $this->settings['duration'];
222: }
223: $prefixLength = strlen($this->settings['prefix']);
224: while (($entry = $dir->read()) !== false) {
225: if (substr($entry, 0, $prefixLength) !== $this->settings['prefix']) {
226: continue;
227: }
228: if ($this->_setKey($entry) === false) {
229: continue;
230: }
231: if ($check) {
232: $mtime = $this->_File->getMTime();
233:
234: if ($mtime > $threshold) {
235: continue;
236: }
237:
238: $expires = (int)$this->_File->current();
239:
240: if ($expires > $now) {
241: continue;
242: }
243: }
244: $path = $this->_File->getRealPath();
245: $this->_File = null;
246: if (file_exists($path)) {
247: unlink($path);
248: }
249: }
250: $dir->close();
251: return true;
252: }
253:
254: 255: 256: 257: 258: 259: 260: 261:
262: public function decrement($key, $offset = 1) {
263: throw new CacheException(__d('cake_dev', 'Files cannot be atomically decremented.'));
264: }
265:
266: 267: 268: 269: 270: 271: 272: 273:
274: public function increment($key, $offset = 1) {
275: throw new CacheException(__d('cake_dev', 'Files cannot be atomically incremented.'));
276: }
277:
278: 279: 280: 281: 282: 283: 284: 285:
286: protected function _setKey($key, $createKey = false) {
287: $path = new SplFileInfo($this->settings['path'] . $key);
288:
289: if (!$createKey && !$path->isFile()) {
290: return false;
291: }
292: if (empty($this->_File) || $this->_File->getBaseName() !== $key) {
293: $exists = file_exists($path->getPathname());
294: try {
295: $this->_File = $path->openFile('c+');
296: } catch (Exception $e) {
297: trigger_error($e->getMessage(), E_USER_WARNING);
298: return false;
299: }
300: unset($path);
301:
302: if (!$exists && !chmod($this->_File->getPathname(), (int) $this->settings['mask'])) {
303: trigger_error(__d(
304: 'cake_dev', 'Could not apply permission mask "%s" on cache file "%s"',
305: array($this->_File->getPathname(), $this->settings['mask'])), E_USER_WARNING);
306: }
307: }
308: return true;
309: }
310:
311: 312: 313: 314: 315:
316: protected function _active() {
317: $dir = new SplFileInfo($this->settings['path']);
318: if ($this->_init && !($dir->isDir() && $dir->isWritable())) {
319: $this->_init = false;
320: trigger_error(__d('cake_dev', '%s is not writable', $this->settings['path']), E_USER_WARNING);
321: return false;
322: }
323: return true;
324: }
325: }
326: