Initial Kohana install
[speedfreak] / Server / system / libraries / drivers / Cache / File.php
1 <?php defined('SYSPATH') OR die('No direct access allowed.');
2 /**
3  * File-based Cache driver.
4  *
5  * $Id: File.php 4046 2009-03-05 19:23:29Z Shadowhand $
6  *
7  * @package    Cache
8  * @author     Kohana Team
9  * @copyright  (c) 2007-2008 Kohana Team
10  * @license    http://kohanaphp.com/license.html
11  */
12 class Cache_File_Driver implements Cache_Driver {
13
14         protected $directory = '';
15
16         /**
17          * Tests that the storage location is a directory and is writable.
18          */
19         public function __construct($directory)
20         {
21                 // Find the real path to the directory
22                 $directory = str_replace('\\', '/', realpath($directory)).'/';
23
24                 // Make sure the cache directory is writable
25                 if ( ! is_dir($directory) OR ! is_writable($directory))
26                         throw new Kohana_Exception('cache.unwritable', $directory);
27
28                 // Directory is valid
29                 $this->directory = $directory;
30         }
31
32         /**
33          * Finds an array of files matching the given id or tag.
34          *
35          * @param  string  cache id or tag
36          * @param  bool    search for tags
37          * @return array   of filenames matching the id or tag
38          */
39         public function exists($id, $tag = FALSE)
40         {
41                 if ($id === TRUE)
42                 {
43                         // Find all the files
44                         return glob($this->directory.'*~*~*');
45                 }
46                 elseif ($tag === TRUE)
47                 {
48                         // Find all the files that have the tag name
49                         $paths = glob($this->directory.'*~*'.$id.'*~*');
50
51                         // Find all tags matching the given tag
52                         $files = array();
53                         foreach ($paths as $path)
54                         {
55                                 // Split the files
56                                 $tags = explode('~', basename($path));
57
58                                 // Find valid tags
59                                 if (count($tags) !== 3 OR empty($tags[1]))
60                                         continue;
61
62                                 // Split the tags by plus signs, used to separate tags
63                                 $tags = explode('+', $tags[1]);
64
65                                 if (in_array($tag, $tags))
66                                 {
67                                         // Add the file to the array, it has the requested tag
68                                         $files[] = $path;
69                                 }
70                         }
71
72                         return $files;
73                 }
74                 else
75                 {
76                         // Find the file matching the given id
77                         return glob($this->directory.$id.'~*');
78                 }
79         }
80
81         /**
82          * Sets a cache item to the given data, tags, and lifetime.
83          *
84          * @param   string   cache id to set
85          * @param   string   data in the cache
86          * @param   array    cache tags
87          * @param   integer  lifetime
88          * @return  bool
89          */
90         public function set($id, $data, array $tags = NULL, $lifetime)
91         {
92                 // Remove old cache files
93                 $this->delete($id);
94
95                 // Cache File driver expects unix timestamp
96                 if ($lifetime !== 0)
97                 {
98                         $lifetime += time();
99                 }
100
101                 if ( ! empty($tags))
102                 {
103                         // Convert the tags into a string list
104                         $tags = implode('+', $tags);
105                 }
106
107                 // Write out a serialized cache
108                 return (bool) file_put_contents($this->directory.$id.'~'.$tags.'~'.$lifetime, serialize($data));
109         }
110
111         /**
112          * Finds an array of ids for a given tag.
113          *
114          * @param  string  tag name
115          * @return array   of ids that match the tag
116          */
117         public function find($tag)
118         {
119                 // An array will always be returned
120                 $result = array();
121
122                 if ($paths = $this->exists($tag, TRUE))
123                 {
124                         // Length of directory name
125                         $offset = strlen($this->directory);
126
127                         // Find all the files with the given tag
128                         foreach ($paths as $path)
129                         {
130                                 // Get the id from the filename
131                                 list($id, $junk) = explode('~', basename($path), 2);
132
133                                 if (($data = $this->get($id)) !== FALSE)
134                                 {
135                                         // Add the result to the array
136                                         $result[$id] = $data;
137                                 }
138                         }
139                 }
140
141                 return $result;
142         }
143
144         /**
145          * Fetches a cache item. This will delete the item if it is expired or if
146          * the hash does not match the stored hash.
147          *
148          * @param   string  cache id
149          * @return  mixed|NULL
150          */
151         public function get($id)
152         {
153                 if ($file = $this->exists($id))
154                 {
155                         // Use the first file
156                         $file = current($file);
157
158                         // Validate that the cache has not expired
159                         if ($this->expired($file))
160                         {
161                                 // Remove this cache, it has expired
162                                 $this->delete($id);
163                         }
164                         else
165                         {
166                                 // Turn off errors while reading the file
167                                 $ER = error_reporting(0);
168
169                                 if (($data = file_get_contents($file)) !== FALSE)
170                                 {
171                                         // Unserialize the data
172                                         $data = unserialize($data);
173                                 }
174                                 else
175                                 {
176                                         // Delete the data
177                                         unset($data);
178                                 }
179
180                                 // Turn errors back on
181                                 error_reporting($ER);
182                         }
183                 }
184
185                 // Return NULL if there is no data
186                 return isset($data) ? $data : NULL;
187         }
188
189         /**
190          * Deletes a cache item by id or tag
191          *
192          * @param   string   cache id or tag, or TRUE for "all items"
193          * @param   boolean  use tags
194          * @return  boolean
195          */
196         public function delete($id, $tag = FALSE)
197         {
198                 $files = $this->exists($id, $tag);
199
200                 if (empty($files))
201                         return FALSE;
202
203                 // Disable all error reporting while deleting
204                 $ER = error_reporting(0);
205
206                 foreach ($files as $file)
207                 {
208                         // Remove the cache file
209                         if ( ! unlink($file))
210                                 Kohana::log('error', 'Cache: Unable to delete cache file: '.$file);
211                 }
212
213                 // Turn on error reporting again
214                 error_reporting($ER);
215
216                 return TRUE;
217         }
218
219         /**
220          * Deletes all cache files that are older than the current time.
221          *
222          * @return void
223          */
224         public function delete_expired()
225         {
226                 if ($files = $this->exists(TRUE))
227                 {
228                         // Disable all error reporting while deleting
229                         $ER = error_reporting(0);
230
231                         foreach ($files as $file)
232                         {
233                                 if ($this->expired($file))
234                                 {
235                                         // The cache file has already expired, delete it
236                                         if ( ! unlink($file))
237                                                 Kohana::log('error', 'Cache: Unable to delete cache file: '.$file);
238                                 }
239                         }
240
241                         // Turn on error reporting again
242                         error_reporting($ER);
243                 }
244         }
245
246         /**
247          * Check if a cache file has expired by filename.
248          *
249          * @param  string  filename
250          * @return bool
251          */
252         protected function expired($file)
253         {
254                 // Get the expiration time
255                 $expires = (int) substr($file, strrpos($file, '~') + 1);
256
257                 // Expirations of 0 are "never expire"
258                 return ($expires !== 0 AND $expires <= time());
259         }
260
261 } // End Cache File Driver