| [ Index ] |
WordPress Source Cross Reference |
[Summary view] [Print] [Text view]
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'); 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 ?>