Overview

Packages

  • Cache

Classes

  • Horde_Cache
  • Horde_Cache_Exception
  • Horde_Cache_Storage_Apc
  • Horde_Cache_Storage_Base
  • Horde_Cache_Storage_Eaccelerator
  • Horde_Cache_Storage_File
  • Horde_Cache_Storage_Memcache
  • Horde_Cache_Storage_Mock
  • Horde_Cache_Storage_Null
  • Horde_Cache_Storage_Session
  • Horde_Cache_Storage_Sql
  • Horde_Cache_Storage_Stack
  • Horde_Cache_Storage_Xcache
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * This class provides cache storage in the filesystem.
  4:  *
  5:  * Copyright 1999-2012 Horde LLC (http://www.horde.org/)
  6:  *
  7:  * See the enclosed file COPYING for license information (LGPL). If you
  8:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  9:  *
 10:  * @author   Anil Madhavapeddy <anil@recoil.org>
 11:  * @author   Chuck Hagenbuch <chuck@horde.org>
 12:  * @author   Michael Slusarz <slusarz@horde.org>
 13:  * @category Horde
 14:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 15:  * @package  Cache
 16:  */
 17: class Horde_Cache_Storage_File extends Horde_Cache_Storage_Base
 18: {
 19:     /* Location of the garbage collection data file. */
 20:     const GC_FILE = 'horde_cache_gc';
 21: 
 22:     /**
 23:      * The location of the temp directory.
 24:      *
 25:      * @var string
 26:      */
 27:     protected $_dir;
 28: 
 29:     /**
 30:      * List of key to filename mappings.
 31:      *
 32:      * @var array
 33:      */
 34:     protected $_file = array();
 35: 
 36:     /**
 37:      * Constructor.
 38:      *
 39:      * @param array $params  Optional parameters:
 40:      * <pre>
 41:      * 'dir' - (string) The base directory to store the cache files in.
 42:      *         DEFAULT: System default
 43:      * 'prefix' - (string) The filename prefix to use for the cache files.
 44:      *            DEFAULT: 'cache_'
 45:      * 'sub' - (integer) If non-zero, the number of subdirectories to create
 46:      *         to store the file (i.e. PHP's session.save_path).
 47:      *         DEFAULT: 0
 48:      * </pre>
 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:      * Destructor.
 66:      */
 67:     public function __destruct()
 68:     {
 69:         /* Only do garbage collection 0.1% of the time we create an object. */
 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:             /* Nothing cached, return failure. */
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:             // This may result in duplicate entries in GC_FILE, but we
145:             // will take care of these whenever we do GC and this is quicker
146:             // than having to check every time we access the file.
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:         /* Key exists in the cache */
159:         if (file_exists($filename)) {
160:             /* 0 means no expire.
161:              * Also, If the file was been created after the supplied value,
162:              * the data is valid (fresh). */
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:      * Return a list of cache files.
194:      *
195:      * @return array  Pathnames to cache files.
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:      * Map a cache key to a unique filename.
222:      *
223:      * @param string $key     Cache key.
224:      * @param string $create  Create path if it doesn't exist?
225:      *
226:      * @return string  Fully qualified filename.
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: 
API documentation generated by ApiGen