2 function wp_cache_add($key, $data, $flag = '', $expire = 0) {
3 global $wp_object_cache;
5 return $wp_object_cache->add($key, $data, $flag, $expire);
8 function wp_cache_close() {
9 global $wp_object_cache;
11 return $wp_object_cache->save();
14 function wp_cache_delete($id, $flag = '') {
15 global $wp_object_cache;
17 return $wp_object_cache->delete($id, $flag);
20 function wp_cache_flush() {
21 global $wp_object_cache;
23 return $wp_object_cache->flush();
26 function wp_cache_get($id, $flag = '') {
27 global $wp_object_cache;
29 return $wp_object_cache->get($id, $flag);
32 function wp_cache_init() {
33 global $wp_object_cache;
35 $wp_object_cache = new WP_Object_Cache();
38 function wp_cache_replace($key, $data, $flag = '', $expire = 0) {
39 global $wp_object_cache;
41 return $wp_object_cache->replace($key, $data, $flag, $expire);
44 function wp_cache_set($key, $data, $flag = '', $expire = 0) {
45 global $wp_object_cache;
47 return $wp_object_cache->set($key, $data, $flag, $expire);
50 define('CACHE_SERIAL_HEADER', "<?php\n/*");
51 define('CACHE_SERIAL_FOOTER', "*/\n?".">");
53 class WP_Object_Cache {
55 var $cache_enabled = false;
56 var $expiration_time = 900;
57 var $flock_filename = 'wp_object_cache.lock';
59 var $cache = array ();
60 var $dirty_objects = array ();
61 var $non_existant_objects = array ();
62 var $global_groups = array ('users', 'userlogins', 'usermeta');
64 var $cold_cache_hits = 0;
65 var $warm_cache_hits = 0;
66 var $cache_misses = 0;
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)
74 flock($this->mutex, LOCK_EX);
78 function add($id, $data, $group = 'default', $expire = '') {
82 if (false !== $this->get($id, $group, false))
85 return $this->set($id, $data, $group, $expire);
88 function delete($id, $group = 'default', $force = false) {
92 if (!$force && false === $this->get($id, $group, false))
95 unset ($this->cache[$group][$id]);
96 $this->non_existant_objects[$group][$id] = true;
97 $this->dirty_objects[$group][] = $id;
102 if ( !$this->cache_enabled )
105 if ( ! $this->acquire_lock() )
108 $this->rm_cache_dir();
109 $this->cache = array ();
110 $this->dirty_objects = array ();
111 $this->non_existant_objects = array ();
113 $this->release_lock();
118 function get($id, $group = 'default', $count_hits = true) {
122 if (isset ($this->cache[$group][$id])) {
124 $this->warm_cache_hits += 1;
125 return $this->cache[$group][$id];
128 if (isset ($this->non_existant_objects[$group][$id]))
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);
136 if (isset ($this->cache[$group][$id])) {
137 $this->cold_cache_hits += 1;
138 return $this->cache[$group][$id];
141 $this->non_existant_objects[$group][$id] = true;
142 $this->cache_misses += 1;
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;
153 // If the object has expired, remove it from the cache and return false to force
156 if ((filemtime($cache_file) + $this->expiration_time) <= $now) {
157 $this->cache_misses += 1;
158 $this->delete($id, $group, true);
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] = '';
166 $this->cold_cache_hits += 1;
167 return $this->cache[$group][$id];
170 function get_group_dir($group) {
171 if (false !== array_search($group, $this->global_groups))
174 return "{$this->blog_id}/$group";
177 function hash($data) {
178 if ( function_exists('hash_hmac') ) {
179 return hash_hmac('md5', $data, $this->secret);
181 return md5($data . $this->secret);
185 function load_group_from_db($group) {
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;
194 foreach ($this->cache['category'] as $catt) {
195 $curcat = $catt->cat_ID;
196 $fullpath = '/'.$this->cache['category'][$catt->cat_ID]->category_nicename;
197 while ($this->cache['category'][$curcat]->category_parent != 0) {
198 $curcat = $this->cache['category'][$curcat]->category_parent;
199 $fullpath = '/'.$this->cache['category'][$curcat]->category_nicename.$fullpath;
201 $this->cache['category'][$catt->cat_ID]->fullpath = $fullpath;
205 if ('options' == $group) {
206 $wpdb->hide_errors();
207 if (!$options = $wpdb->get_results("SELECT option_name, option_value FROM $wpdb->options WHERE autoload = 'yes'")) {
208 $options = $wpdb->get_results("SELECT option_name, option_value FROM $wpdb->options");
210 $wpdb->show_errors();
215 foreach ($options as $option) {
216 $this->cache['options'][$option->option_name] = $option->option_value;
221 function make_group_dir($group, $perms) {
222 $group_dir = $this->get_group_dir($group);
224 foreach (split('/', $group_dir) as $subdir) {
225 $make_dir .= "$subdir/";
226 if (!file_exists($this->cache_dir.$make_dir)) {
227 if (! @ mkdir($this->cache_dir.$make_dir))
229 @ chmod($this->cache_dir.$make_dir, $perms);
232 if (!file_exists($this->cache_dir.$make_dir."index.php")) {
233 $file_perms = $perms & 0000666;
234 @ touch($this->cache_dir.$make_dir."index.php");
235 @ chmod($this->cache_dir.$make_dir."index.php", $file_perms);
239 return $this->cache_dir."$group_dir/";
242 function rm_cache_dir() {
243 $dir = $this->cache_dir;
244 $dir = rtrim($dir, DIRECTORY_SEPARATOR);
246 $stack = array($dir);
249 while ($index < count($stack)) {
250 # Get indexed directory from stack
251 $dir = $stack[$index];
253 $dh = @ opendir($dir);
257 while (($file = @ readdir($dh)) !== false) {
258 if ($file == '.' or $file == '..')
261 if (@ is_dir($dir . DIRECTORY_SEPARATOR . $file))
262 $stack[] = $dir . DIRECTORY_SEPARATOR . $file;
263 else if (@ is_file($dir . DIRECTORY_SEPARATOR . $file))
264 @ unlink($dir . DIRECTORY_SEPARATOR . $file);
270 $stack = array_reverse($stack); // Last added dirs are deepest
271 foreach($stack as $dir) {
272 if ( $dir != $top_dir)
278 function release_lock() {
279 // Release write lock.
280 flock($this->mutex, LOCK_UN);
281 fclose($this->mutex);
284 function replace($id, $data, $group = 'default', $expire = '') {
288 if (false === $this->get($id, $group, false))
291 return $this->set($id, $data, $group, $expire);
294 function set($id, $data, $group = 'default', $expire = '') {
301 $this->cache[$group][$id] = $data;
302 unset ($this->non_existant_objects[$group][$id]);
303 $this->dirty_objects[$group][] = $id;
311 if (!$this->cache_enabled)
314 if (empty ($this->dirty_objects))
317 // Give the new dirs the same perms as wp-content.
318 $stat = stat(ABSPATH.'wp-content');
319 $dir_perms = $stat['mode'] & 0007777; // Get the permission bits.
320 $file_perms = $dir_perms & 0000666; // Remove execute bits for files.
322 // Make the base cache dir.
323 if (!file_exists($this->cache_dir)) {
324 if (! @ mkdir($this->cache_dir))
326 @ chmod($this->cache_dir, $dir_perms);
329 if (!file_exists($this->cache_dir."index.php")) {
330 @ touch($this->cache_dir."index.php");
331 @ chmod($this->cache_dir."index.php", $file_perms);
334 if ( ! $this->acquire_lock() )
337 // Loop over dirty objects and save them.
339 foreach ($this->dirty_objects as $group => $ids) {
340 $group_dir = $this->make_group_dir($group, $dir_perms);
342 $ids = array_unique($ids);
343 foreach ($ids as $id) {
344 $cache_file = $group_dir.$this->hash($id).'.php';
346 // Remove the cache file if the key is not set.
347 if (!isset ($this->cache[$group][$id])) {
348 if (file_exists($cache_file))
349 @ unlink($cache_file);
353 $temp_file = tempnam($group_dir, 'tmp');
354 $serial = CACHE_SERIAL_HEADER.base64_encode(serialize($this->cache[$group][$id])).CACHE_SERIAL_FOOTER;
355 $fd = @fopen($temp_file, 'w');
356 if ( false === $fd ) {
362 if (!@ rename($temp_file, $cache_file)) {
363 if (@ copy($temp_file, $cache_file))
364 @ unlink($temp_file);
368 @ chmod($cache_file, $file_perms);
372 $this->dirty_objects = array();
374 $this->release_lock();
384 echo "<strong>Cold Cache Hits:</strong> {$this->cold_cache_hits}<br/>";
385 echo "<strong>Warm Cache Hits:</strong> {$this->warm_cache_hits}<br/>";
386 echo "<strong>Cache Misses:</strong> {$this->cache_misses}<br/>";
389 foreach ($this->cache as $group => $cache) {
391 echo "<strong>Group:</strong> $group<br/>";
392 echo "<strong>Cache:</strong>";
396 if (isset ($this->dirty_objects[$group])) {
397 echo "<strong>Dirty Objects:</strong>";
399 print_r(array_unique($this->dirty_objects[$group]));
406 function WP_Object_Cache() {
409 if (defined('DISABLE_CACHE'))
412 if ( ! defined('ENABLE_CACHE') )
415 // Disable the persistent cache if safe_mode is on.
416 if ( ini_get('safe_mode') && ! defined('ENABLE_CACHE') )
419 if (defined('CACHE_PATH'))
420 $this->cache_dir = CACHE_PATH;
422 // Using the correct separator eliminates some cache flush errors on Windows
423 $this->cache_dir = ABSPATH.'wp-content'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR;
425 if (is_writable($this->cache_dir) && is_dir($this->cache_dir)) {
426 $this->cache_enabled = true;
428 if (is_writable(ABSPATH.'wp-content')) {
429 $this->cache_enabled = true;
433 if (defined('CACHE_EXPIRATION_TIME'))
434 $this->expiration_time = CACHE_EXPIRATION_TIME;
436 if ( defined('WP_SECRET') )
437 $this->secret = WP_SECRET;
439 $this->secret = DB_PASSWORD . DB_USER . DB_NAME . DB_HOST . ABSPATH;
441 $this->blog_id = $this->hash($blog_id);