root/tags/1_0-rc1/wp-includes/cache.php

Revision 550, 11.0 kB (checked in by donncha, 3 years ago)

WP Merge and new features

Line 
1 <?php
2 function wp_cache_add($key, $data, $flag = '', $expire = 0) {
3     global $wp_object_cache;
4
5     return $wp_object_cache->add($key, $data, $flag, $expire);
6 }
7
8 function wp_cache_close() {
9     global $wp_object_cache;
10
11     return $wp_object_cache->save();
12 }
13
14 function wp_cache_delete($id, $flag = '') {
15     global $wp_object_cache;
16
17     return $wp_object_cache->delete($id, $flag);
18 }
19
20 function wp_cache_flush() {
21     global $wp_object_cache;
22
23     return $wp_object_cache->flush();
24 }
25
26 function wp_cache_get($id, $flag = '') {
27     global $wp_object_cache;
28
29     return $wp_object_cache->get($id, $flag);
30 }
31
32 function wp_cache_init() {
33     global $wp_object_cache;
34
35     $wp_object_cache = new WP_Object_Cache();
36 }
37
38 function wp_cache_replace($key, $data, $flag = '', $expire = 0) {
39     global $wp_object_cache;
40
41     return $wp_object_cache->replace($key, $data, $flag, $expire);
42 }
43
44 function wp_cache_set($key, $data, $flag = '', $expire = 0) {
45     global $wp_object_cache;
46
47     return $wp_object_cache->set($key, $data, $flag, $expire);
48 }
49
50 define('CACHE_SERIAL_HEADER', "<?php\n/*");
51 define('CACHE_SERIAL_FOOTER', "*/\n?".">");
52
53 class WP_Object_Cache {
54     var $cache_dir;
55     var $cache_enabled = false;
56     var $expiration_time = 900;
57     var $flock_filename = 'wp_object_cache.lock';
58     var $mutex;
59     var $cache = array ();
60     var $dirty_objects = array ();
61     var $non_existant_objects = array ();
62     var $global_groups = array ('users', 'userlogins', 'usermeta', 'site-options', 'site-lookup', 'blog-lookup', 'blog-details');
63     var $blog_id;
64     var $cold_cache_hits = 0;
65     var $warm_cache_hits = 0;
66     var $cache_misses = 0;
67     var $secret = '';
68
69     function acquire_lock() {
70         // Acquire a write lock.
71         $this->mutex = @fopen($this->cache_dir.$this->flock_filename, 'w');
72         if ( false == $this->mutex)
73             return false;
74         flock($this->mutex, LOCK_EX);
75         return true;
76     }
77
78     function add($id, $data, $group = 'default', $expire = '') {
79         if (empty ($group))
80             $group = 'default';
81
82         if (false !== $this->get($id, $group, false))
83             return false;
84
85         return $this->set($id, $data, $group, $expire);
86     }
87
88     function delete($id, $group = 'default', $force = false) {
89         if (empty ($group))
90             $group = 'default';
91
92         if (!$force && false === $this->get($id, $group, false))
93             return false;
94
95         unset ($this->cache[$group][$id]);
96         $this->non_existant_objects[$group][$id] = true;
97         $this->dirty_objects[$group][] = $id;
98         return true;
99     }
100
101     function flush() {
102         if ( !$this->cache_enabled )
103             return true;
104
105         if ( ! $this->acquire_lock() )
106             return false;
107
108         $this->rm_cache_dir();
109         $this->cache = array ();
110         $this->dirty_objects = array ();
111         $this->non_existant_objects = array ();
112
113         $this->release_lock();
114
115         return true;
116     }
117
118     function get($id, $group = 'default', $count_hits = true) {
119         if (empty ($group))
120             $group = 'default';
121
122         if (isset ($this->cache[$group][$id])) {
123             if ($count_hits)
124                 $this->warm_cache_hits += 1;
125             return $this->cache[$group][$id];
126         }
127
128         if (isset ($this->non_existant_objects[$group][$id]))
129             return false;
130
131         //  If caching is not enabled, we have to fall back to pulling from the DB.
132         if (!$this->cache_enabled) {
133             if (!isset ($this->cache[$group]))
134                 $this->load_group_from_db($group);
135
136             if (isset ($this->cache[$group][$id])) {
137                 $this->cold_cache_hits += 1;
138                 return $this->cache[$group][$id];
139             }
140
141             $this->non_existant_objects[$group][$id] = true;
142             $this->cache_misses += 1;
143             return false;
144         }
145
146         $cache_file = $this->cache_dir.$this->get_group_dir($group)."/".$this->hash($id).'.php';
147         if (!file_exists($cache_file)) {
148             $this->non_existant_objects[$group][$id] = true;
149             $this->cache_misses += 1;
150             return false;
151         }
152
153         // If the object has expired, remove it from the cache and return false to force
154         // a refresh.
155         $now = time();
156         if ((filemtime($cache_file) + $this->expiration_time) <= $now) {
157             $this->cache_misses += 1;
158             $this->delete($id, $group, true);
159             return false;
160         }
161
162         $this->cache[$group][$id] = unserialize(base64_decode(substr(@ file_get_contents($cache_file), strlen(CACHE_SERIAL_HEADER), -strlen(CACHE_SERIAL_FOOTER))));
163         if (false === $this->cache[$group][$id])
164             $this->cache[$group][$id] = '';
165
166         $this->cold_cache_hits += 1;
167         return $this->cache[$group][$id];
168     }
169
170     function get_group_dir($group) {
171         if (false !== array_search($group, $this->global_groups))
172             return $group;
173
174         return "{$this->blog_id}/$group";
175     }
176
177     function hash($data) {
178         if ( function_exists('hash_hmac') ) {
179             return hash_hmac('md5', $data, $this->secret);
180         } else {
181             return md5($data . $this->secret);
182         }
183     }
184
185     function load_group_from_db($group) {
186         global $wpdb;
187
188         if ('category' == $group) {
189             $this->cache['category'] = array ();
190             if ($dogs = $wpdb->get_results("SELECT * FROM $wpdb->categories")) {
191                 foreach ($dogs as $catt)
192                     $this->cache['category'][$catt->cat_ID] = $catt;
193             }
194         } else
195             if ('options' == $group) {
196                 $wpdb->hide_errors();
197                 if (!$options = $wpdb->get_results("SELECT option_name, option_value FROM $wpdb->options WHERE autoload = 'yes'")) {
198                     $options = $wpdb->get_results("SELECT option_name, option_value FROM $wpdb->options");
199                 }
200                 $wpdb->show_errors();
201
202                 if ( ! $options )
203                     return;
204
205                 foreach ($options as $option) {
206                     $this->cache['options'][$option->option_name] = $option->option_value;
207                 }
208             }
209     }
210
211     function make_group_dir($group, $perms) {
212         $group_dir = $this->get_group_dir($group);
213         $make_dir = '';
214         foreach (split('/', $group_dir) as $subdir) {
215             $make_dir .= "$subdir/";
216             if (!file_exists($this->cache_dir.$make_dir)) {
217                 if (! @ mkdir($this->cache_dir.$make_dir))
218                     break;
219                 @ chmod($this->cache_dir.$make_dir, $perms);
220             }
221
222             if (!file_exists($this->cache_dir.$make_dir."index.php")) {
223                 $file_perms = $perms & 0000666;
224                 @ touch($this->cache_dir.$make_dir."index.php");
225                 @ chmod($this->cache_dir.$make_dir."index.php", $file_perms);
226             }
227         }
228
229         return $this->cache_dir."$group_dir/";
230     }
231
232     function rm_cache_dir() {
233         $dir = $this->cache_dir;
234         $dir = rtrim($dir, DIRECTORY_SEPARATOR);
235         $top_dir = $dir;
236         $stack = array($dir);
237         $index = 0;
238
239         while ($index < count($stack)) {
240             # Get indexed directory from stack
241             $dir = $stack[$index];
242       
243             $dh = @ opendir($dir);
244             if (!$dh)
245                 return false;
246       
247             while (($file = @ readdir($dh)) !== false) {
248                 if ($file == '.' or $file == '..')
249                     continue;
250
251                 if (@ is_dir($dir . DIRECTORY_SEPARATOR . $file))
252                     $stack[] = $dir . DIRECTORY_SEPARATOR . $file;
253                 else if (@ is_file($dir . DIRECTORY_SEPARATOR . $file))
254                     @ unlink($dir . DIRECTORY_SEPARATOR . $file);
255             }
256
257             $index++;
258         }
259
260         $stack = array_reverse($stack);  // Last added dirs are deepest
261         foreach($stack as $dir) {
262             if ( $dir != $top_dir)
263                 @ rmdir($dir);
264         }
265
266     }
267
268     function release_lock() {
269         // Release write lock.
270         flock($this->mutex, LOCK_UN);
271         fclose($this->mutex);
272     }
273
274     function replace($id, $data, $group = 'default', $expire = '') {
275         if (empty ($group))
276             $group = 'default';
277
278         if (false === $this->get($id, $group, false))
279             return false;
280
281         return $this->set($id, $data, $group, $expire);
282     }
283
284     function set($id, $data, $group = 'default', $expire = '') {
285         if (empty ($group))
286             $group = 'default';
287
288         if (NULL == $data)
289             $data = '';
290
291         $this->cache[$group][$id] = $data;
292         unset ($this->non_existant_objects[$group][$id]);
293         $this->dirty_objects[$group][] = $id;
294
295         return true;
296     }
297
298     function save() {
299         //$this->stats();
300
301         if (!$this->cache_enabled)
302             return true;
303
304         if (empty ($this->dirty_objects))
305             return true;
306
307         // Give the new dirs the same perms as wp-content.
308         $stat = stat(ABSPATH.'wp-content');
309         $dir_perms = $stat['mode'] & 0007777; // Get the permission bits.
310         $file_perms = $dir_perms & 0000666; // Remove execute bits for files.
311
312         // Make the base cache dir.
313         if (!file_exists($this->cache_dir)) {
314             if (! @ mkdir($this->cache_dir))
315                 return false;
316             @ chmod($this->cache_dir, $dir_perms);
317         }
318
319         if (!file_exists($this->cache_dir."index.php")) {
320             @ touch($this->cache_dir."index.php");
321             @ chmod($this->cache_dir."index.php", $file_perms);
322         }
323
324         if ( ! $this->acquire_lock() )
325             return false;
326
327         // Loop over dirty objects and save them.
328         $errors = 0;
329         foreach ($this->dirty_objects as $group => $ids) {
330             $group_dir = $this->make_group_dir($group, $dir_perms);
331
332             $ids = array_unique($ids);
333             foreach ($ids as $id) {
334                 $cache_file = $group_dir.$this->hash($id).'.php';
335
336                 // Remove the cache file if the key is not set.
337                 if (!isset ($this->cache[$group][$id])) {
338                     if (file_exists($cache_file))
339                         @ unlink($cache_file);
340                     continue;
341                 }
342
343                 $temp_file = tempnam($group_dir, 'tmp');
344                 $serial = CACHE_SERIAL_HEADER.base64_encode(serialize($this->cache[$group][$id])).CACHE_SERIAL_FOOTER;
345                 $fd = @fopen($temp_file, 'w');
346                 if ( false === $fd ) {
347                     $errors++;
348                     continue;
349                 }
350                 fputs($fd, $serial);
351                 fclose($fd);
352                 if (!@ rename($temp_file, $cache_file)) {
353                     if (@ copy($temp_file, $cache_file))
354                         @ unlink($temp_file);
355                     else
356                         $errors++;
357                 }
358                 @ chmod($cache_file, $file_perms);
359             }
360         }
361
362         $this->dirty_objects = array();
363
364         $this->release_lock();
365
366         if ( $errors )
367             return false;
368
369         return true;
370     }
371
372     function stats() {
373         echo "<p>";
374         echo "<strong>Cold Cache Hits:</strong> {$this->cold_cache_hits}<br/>";
375         echo "<strong>Warm Cache Hits:</strong> {$this->warm_cache_hits}<br/>";
376         echo "<strong>Cache Misses:</strong> {$this->cache_misses}<br/>";
377         echo "</p>";
378
379         foreach ($this->cache as $group => $cache) {
380             echo "<p>";
381             echo "<strong>Group:</strong> $group<br/>";
382             echo "<strong>Cache:</strong>";
383             echo "<pre>";
384             print_r($cache);
385             echo "</pre>";
386             if (isset ($this->dirty_objects[$group])) {
387                 echo "<strong>Dirty Objects:</strong>";
388                 echo "<pre>";
389                 print_r(array_unique($this->dirty_objects[$group]));
390                 echo "</pre>";
391                 echo "</p>";
392             }
393         }
394     }
395
396     function WP_Object_Cache() {
397         global $blog_id;
398
399         if (defined('DISABLE_CACHE'))
400             return;
401
402         if ( ! defined('ENABLE_CACHE') )
403             return;
404
405         // Disable the persistent cache if safe_mode is on.
406         if ( ini_get('safe_mode') && ! defined('ENABLE_CACHE') )
407             return;
408
409         if (defined('CACHE_PATH'))
410             $this->cache_dir = CACHE_PATH;
411         else
412             // Using the correct separator eliminates some cache flush errors on Windows
413             $this->cache_dir = ABSPATH.'wp-content'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR;
414
415         if (is_writable($this->cache_dir) && is_dir($this->cache_dir)) {
416                 $this->cache_enabled = true;
417         } else {
418             if (is_writable(ABSPATH.'wp-content')) {
419                 $this->cache_enabled = true;
420             }
421         }
422
423         if (defined('CACHE_EXPIRATION_TIME'))
424             $this->expiration_time = CACHE_EXPIRATION_TIME;
425
426         if ( defined('WP_SECRET') )
427             $this->secret = WP_SECRET;
428         else
429             $this->secret = DB_PASSWORD . DB_USER . DB_NAME . DB_HOST . ABSPATH;
430
431         $this->blog_id = $this->hash($blog_id);
432     }
433 }
434 ?>
435
Note: See TracBrowser for help on using the browser.