Development

/branches/1.1/lib/cache/sfFileCache.class.php

You must first sign up to be able to contribute.

root/branches/1.1/lib/cache/sfFileCache.class.php

Revision 16899, 7.5 kB (checked in by fabien, 6 years ago)

[1.0, 1.1, 1.2, 1.3] optimized windows rename() problem hack (based on a patch from Agavi)

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Rev Date
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 /**
12  * Cache class that stores content in files.
13  *
14  * @package    symfony
15  * @subpackage cache
16  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
17  * @version    SVN: $Id$
18  */
19 class sfFileCache extends sfCache
20 {
21   const READ_DATA = 1;
22   const READ_TIMEOUT = 2;
23   const READ_LAST_MODIFIED = 4;
24
25   const EXTENSION = '.cache';
26
27  /**
28   * Initializes this sfCache instance.
29   *
30   * Available options:
31   *
32   * * cache_dir: The directory where to put cache files
33   *
34   * * see sfCache for options available for all drivers
35   *
36   * @see sfCache
37   */
38   public function initialize($options = array())
39   {
40     parent::initialize($options);
41
42     if (!$this->getOption('cache_dir'))
43     {
44       throw new sfInitializationException('You must pass a "cache_dir" option to initialize a sfFileCache object.');
45     }
46
47     $this->setcache_dir($this->getOption('cache_dir'));
48   }
49
50   /**
51    * @see sfCache
52    */
53   public function get($key, $default = null)
54   {
55     if (!$this->has($key))
56     {
57       return $default;
58     }
59
60     return $this->read($this->getFilePath($key));
61   }
62
63   /**
64    * @see sfCache
65    */
66   public function has($key)
67   {
68     return file_exists($this->getFilePath($key)) && time() < $this->read($this->getFilePath($key), self::READ_TIMEOUT);
69   }
70
71   /**
72    * @see sfCache
73    */
74   public function set($key, $data, $lifetime = null)
75   {
76     if ($this->getOption('automatic_cleaning_factor') > 0 && rand(1, $this->getOption('automatic_cleaning_factor')) == 1)
77     {
78       $this->clean(sfCache::OLD);
79     }
80
81     return $this->write($this->getFilePath($key), $data, time() + $this->getLifetime($lifetime));
82   }
83
84   /**
85    * @see sfCache
86    */
87   public function remove($key)
88   {
89     return @unlink($this->getFilePath($key));
90   }
91
92   /**
93    * @see sfCache
94    */
95   public function removePattern($pattern)
96   {
97     if (false !== strpos($pattern, '**'))
98     {
99       $pattern = str_replace(sfCache::SEPARATOR, DIRECTORY_SEPARATOR, $pattern).self::EXTENSION;
100
101       $regexp = self::patternToRegexp($pattern);
102       $paths = array();
103       foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->getOption('cache_dir'))) as $path)
104       {
105         if (preg_match($regexp, str_replace($this->getOption('cache_dir').DIRECTORY_SEPARATOR, '', $path)))
106         {
107           $paths[] = $path;
108         }
109       }
110     }
111     else
112     {
113       $paths = glob($this->getOption('cache_dir').DIRECTORY_SEPARATOR.str_replace(sfCache::SEPARATOR, DIRECTORY_SEPARATOR, $pattern).self::EXTENSION);
114     }
115
116     foreach ($paths as $path)
117     {
118       if (is_dir($path))
119       {
120         sfToolkit::clearDirectory($path);
121       }
122       else
123       {
124         @unlink($path);
125       }
126     }
127   }
128
129   /**
130    * @see sfCache
131    */
132   public function clean($mode = sfCache::ALL)
133   {
134     if (!is_dir($this->getOption('cache_dir')))
135     {
136       return true;
137     }
138
139     $result = true;
140     foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->getOption('cache_dir'))) as $file)
141     {
142       if (sfCache::ALL == $mode || time() > $this->read($file, self::READ_TIMEOUT))
143       {
144         $result = @unlink($file) && $result;
145       }
146     }
147
148     return $result;
149   }
150
151   /**
152    * @see sfCache
153    */
154   public function getTimeout($key)
155   {
156     $path = $this->getFilePath($key);
157
158     if (!file_exists($path))
159     {
160       return 0;
161     }
162
163     $timeout = $this->read($path, self::READ_TIMEOUT);
164
165     return $timeout < time() ? 0 : $timeout;
166   }
167
168   /**
169    * @see sfCache
170    */
171   public function getLastModified($key)
172   {
173     $path = $this->getFilePath($key);
174
175     if (!file_exists($path) || $this->read($path, self::READ_TIMEOUT) < time())
176     {
177       return 0;
178     }
179
180     return $this->read($path, self::READ_LAST_MODIFIED);
181   }
182
183  /**
184   * Converts a cache key to a full path.
185   *
186   * @param string  $key  The cache key
187   *
188   * @return string The full path to the cache file
189   */
190   protected function getFilePath($key)
191   {
192     return $this->getOption('cache_dir').DIRECTORY_SEPARATOR.str_replace(sfCache::SEPARATOR, DIRECTORY_SEPARATOR, $key).self::EXTENSION;
193   }
194
195  /**
196   * Reads the cache file and returns the content.
197   *
198   * @param string $path The file path
199   * @param mixed  $type The type of data you want to be returned
200   *                     sfFileCache::READ_DATA: The cache content
201   *                     sfFileCache::READ_TIMEOUT: The timeout
202   *                     sfFileCache::READ_LAST_MODIFIED: The last modification timestamp
203   *
204   * @return string The content of the cache file.
205   *
206   * @throws sfCacheException
207   */
208   protected function read($path, $type = self::READ_DATA)
209   {
210     if (!$fp = @fopen($path, 'rb'))
211     {
212       throw new sfCacheException(sprintf('Unable to read cache file "%s".', $path));
213     }
214
215     @flock($fp, LOCK_SH);
216     clearstatcache(); // because the filesize can be cached by PHP itself...
217     $length = @filesize($path);
218     $mqr = get_magic_quotes_runtime();
219     set_magic_quotes_runtime(0);
220     switch ($type)
221     {
222       case self::READ_TIMEOUT:
223         $data = $length ? intval(@fread($fp, 12)) : 0;
224         break;
225       case self::READ_LAST_MODIFIED:
226         @fseek($fp, 12);
227         $data = $length ? intval(@fread($fp, 12)) : 0;
228         break;
229       case self::READ_DATA:
230         if ($length)
231         {
232           @fseek($fp, 24);
233           $data = @fread($fp, $length - 24);
234         }
235         else
236         {
237           $data = '';
238         }
239         break;
240       default:
241         throw new sfConfigurationException(sprintf('Unknown type "%s".', $type));
242     }
243     set_magic_quotes_runtime($mqr);
244     @flock($fp, LOCK_UN);
245     @fclose($fp);
246
247     return $data;
248   }
249
250  /**
251   * Writes the given data in the cache file.
252   *
253   * @param  string  $path     The file path
254   * @param  string  $data     The data to put in cache
255   * @param  integer $timeout  The timeout timestamp
256   *
257   * @return boolean true if ok, otherwise false
258   *
259   * @throws sfCacheException
260   */
261   protected function write($path, $data, $timeout)
262   {
263     $current_umask = umask();
264     umask(0000);
265     if (!is_dir(dirname($path)))
266     {
267       // create directory structure if needed
268       mkdir(dirname($path), 0777, true);
269     }
270
271     $tmpFile = tempnam(dirname($path), basename($path));
272
273     if (!$fp = @fopen($tmpFile, 'wb'))
274     {
275        throw new sfCacheException(sprintf('Unable to write cache file "%s".', $tmpFile));
276     }
277
278     @fwrite($fp, str_pad($timeout, 12, 0, STR_PAD_LEFT));
279     @fwrite($fp, str_pad(time(), 12, 0, STR_PAD_LEFT));
280     @fwrite($fp, $data);
281     @fclose($fp);
282
283     // Hack from Agavi (http://trac.agavi.org/changeset/3979)
284     // With php < 5.2.6 on win32, renaming to an already existing file doesn't work, but copy does,
285     // so we simply assume that when rename() fails that we are on win32 and try to use copy()
286     if (!@rename($tmpFile, $path))
287     {
288       if (copy($tmpFile, $path))
289       {
290         unlink($tmpFile);
291       }
292     }
293
294     chmod($path, 0666);
295     umask($current_umask);
296
297     return true;
298   }
299
300   /**
301    * Sets the cache root directory.
302    *
303    * @param string $cache_dir The directory where to put the cache files
304    */
305   protected function setcache_dir($cache_dir)
306   {
307     // remove last DIRECTORY_SEPARATOR
308     if (DIRECTORY_SEPARATOR == substr($cache_dir, -1))
309     {
310       $cache_dir = substr($cache_dir, 0, -1);
311     }
312
313     // create cache dir if needed
314     if (!is_dir($cache_dir))
315     {
316       $current_umask = umask(0000);
317       @mkdir($cache_dir, 0777, true);
318       umask($current_umask);
319     }
320   }
321 }
322
Note: See TracBrowser for help on using the browser.