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;
68 function acquire_lock() {
69 // Acquire a write lock.
70 $this->mutex = @fopen($this->cache_dir.$this->flock_filename, 'w');
71 if ( false == $this->mutex)
73 flock($this->mutex, LOCK_EX);
77 function add($id, $data, $group = 'default', $expire = '') {
81 if (false !== $this->get($id, $group, false))
84 return $this->set($id, $data, $group, $expire);
87 function delete($id, $group = 'default', $force = false) {
91 if (!$force && false === $this->get($id, $group, false))
94 unset ($this->cache[$group][$id]);
95 $this->non_existant_objects[$group][$id] = true;
96 $this->dirty_objects[$group][] = $id;
101 if ( !$this->cache_enabled )
104 if ( ! $this->acquire_lock() )
107 $this->rm_cache_dir();
108 $this->cache = array ();
109 $this->dirty_objects = array ();
110 $this->non_existant_objects = array ();
112 $this->release_lock();
117 function get($id, $group = 'default', $count_hits = true) {
121 if (isset ($this->cache[$group][$id])) {
123 $this->warm_cache_hits += 1;
124 return $this->cache[$group][$id];
127 if (isset ($this->non_existant_objects[$group][$id]))
130 // If caching is not enabled, we have to fall back to pulling from the DB.
131 if (!$this->cache_enabled) {
132 if (!isset ($this->cache[$group]))
133 $this->load_group_from_db($group);
135 if (isset ($this->cache[$group][$id])) {
136 $this->cold_cache_hits += 1;
137 return $this->cache[$group][$id];
140 $this->non_existant_objects[$group][$id] = true;
141 $this->cache_misses += 1;
145 $cache_file = $this->cache_dir.$this->get_group_dir($group)."/".md5($id.DB_PASSWORD).'.php';
146 if (!file_exists($cache_file)) {
147 $this->non_existant_objects[$group][$id] = true;
148 $this->cache_misses += 1;
152 // If the object has expired, remove it from the cache and return false to force
155 if ((filemtime($cache_file) + $this->expiration_time) <= $now) {
156 $this->cache_misses += 1;
157 $this->delete($id, $group, true);
161 $this->cache[$group][$id] = unserialize(substr(@ file_get_contents($cache_file), strlen(CACHE_SERIAL_HEADER), -strlen(CACHE_SERIAL_FOOTER)));
162 if (false === $this->cache[$group][$id])
163 $this->cache[$group][$id] = '';
165 $this->cold_cache_hits += 1;
166 return $this->cache[$group][$id];
169 function get_group_dir($group) {
170 if (false !== array_search($group, $this->global_groups))
173 return "{$this->blog_id}/$group";
176 function load_group_from_db($group) {
179 if ('category' == $group) {
180 $this->cache['category'] = array ();
181 if ($dogs = $wpdb->get_results("SELECT * FROM $wpdb->categories")) {
182 foreach ($dogs as $catt)
183 $this->cache['category'][$catt->cat_ID] = $catt;
185 foreach ($this->cache['category'] as $catt) {
186 $curcat = $catt->cat_ID;
187 $fullpath = '/'.$this->cache['category'][$catt->cat_ID]->category_nicename;
188 while ($this->cache['category'][$curcat]->category_parent != 0) {
189 $curcat = $this->cache['category'][$curcat]->category_parent;
190 $fullpath = '/'.$this->cache['category'][$curcat]->category_nicename.$fullpath;
192 $this->cache['category'][$catt->cat_ID]->fullpath = $fullpath;
196 if ('options' == $group) {
197 $wpdb->hide_errors();
198 if (!$options = $wpdb->get_results("SELECT option_name, option_value FROM $wpdb->options WHERE autoload = 'yes'")) {
199 $options = $wpdb->get_results("SELECT option_name, option_value FROM $wpdb->options");
201 $wpdb->show_errors();
206 foreach ($options as $option) {
207 $this->cache['options'][$option->option_name] = $option->option_value;
212 function make_group_dir($group, $perms) {
213 $group_dir = $this->get_group_dir($group);
215 foreach (split('/', $group_dir) as $subdir) {
216 $make_dir .= "$subdir/";
217 if (!file_exists($this->cache_dir.$make_dir)) {
218 if (! @ mkdir($this->cache_dir.$make_dir))
220 @ chmod($this->cache_dir.$make_dir, $perms);
223 if (!file_exists($this->cache_dir.$make_dir."index.php")) {
224 $file_perms = $perms & 0000666;
225 @ touch($this->cache_dir.$make_dir."index.php");
226 @ chmod($this->cache_dir.$make_dir."index.php", $file_perms);
230 return $this->cache_dir."$group_dir/";
233 function rm_cache_dir() {
234 $dir = $this->cache_dir;
235 $dir = rtrim($dir, DIRECTORY_SEPARATOR);
237 $stack = array($dir);
240 while ($index < count($stack)) {
241 # Get indexed directory from stack
242 $dir = $stack[$index];
244 $dh = @ opendir($dir);
248 while (($file = @ readdir($dh)) !== false) {
249 if ($file == '.' or $file == '..')
252 if (@ is_dir($dir . DIRECTORY_SEPARATOR . $file))
253 $stack[] = $dir . DIRECTORY_SEPARATOR . $file;
254 else if (@ is_file($dir . DIRECTORY_SEPARATOR . $file))
255 @ unlink($dir . DIRECTORY_SEPARATOR . $file);
261 $stack = array_reverse($stack); // Last added dirs are deepest
262 foreach($stack as $dir) {
263 if ( $dir != $top_dir)
269 function release_lock() {
270 // Release write lock.
271 flock($this->mutex, LOCK_UN);
272 fclose($this->mutex);
275 function replace($id, $data, $group = 'default', $expire = '') {
279 if (false === $this->get($id, $group, false))
282 return $this->set($id, $data, $group, $expire);
285 function set($id, $data, $group = 'default', $expire = '') {
292 $this->cache[$group][$id] = $data;
293 unset ($this->non_existant_objects[$group][$id]);
294 $this->dirty_objects[$group][] = $id;
302 if (!$this->cache_enabled)
305 if (empty ($this->dirty_objects))
308 // Give the new dirs the same perms as wp-content.
309 $stat = stat(ABSPATH.'wp-content');
310 $dir_perms = $stat['mode'] & 0007777; // Get the permission bits.
311 $file_perms = $dir_perms & 0000666; // Remove execute bits for files.
313 // Make the base cache dir.
314 if (!file_exists($this->cache_dir)) {
315 if (! @ mkdir($this->cache_dir))
317 @ chmod($this->cache_dir, $dir_perms);
320 if (!file_exists($this->cache_dir."index.php")) {
321 @ touch($this->cache_dir."index.php");
322 @ chmod($this->cache_dir."index.php", $file_perms);
325 if ( ! $this->acquire_lock() )
328 // Loop over dirty objects and save them.
330 foreach ($this->dirty_objects as $group => $ids) {
331 $group_dir = $this->make_group_dir($group, $dir_perms);
333 $ids = array_unique($ids);
334 foreach ($ids as $id) {
335 $cache_file = $group_dir.md5($id.DB_PASSWORD).'.php';
337 // Remove the cache file if the key is not set.
338 if (!isset ($this->cache[$group][$id])) {
339 if (file_exists($cache_file))
340 @ unlink($cache_file);
344 $temp_file = tempnam($group_dir, 'tmp');
345 $serial = CACHE_SERIAL_HEADER.serialize($this->cache[$group][$id]).CACHE_SERIAL_FOOTER;
346 $fd = @fopen($temp_file, 'w');
347 if ( false === $fd ) {
353 if (!@ rename($temp_file, $cache_file)) {
354 if (@ copy($temp_file, $cache_file))
355 @ unlink($temp_file);
359 @ chmod($cache_file, $file_perms);
363 $this->dirty_objects = array();
365 $this->release_lock();
375 echo "<strong>Cold Cache Hits:</strong> {$this->cold_cache_hits}<br/>";
376 echo "<strong>Warm Cache Hits:</strong> {$this->warm_cache_hits}<br/>";
377 echo "<strong>Cache Misses:</strong> {$this->cache_misses}<br/>";
380 foreach ($this->cache as $group => $cache) {
382 echo "<strong>Group:</strong> $group<br/>";
383 echo "<strong>Cache:</strong>";
387 if (isset ($this->dirty_objects[$group])) {
388 echo "<strong>Dirty Objects:</strong>";
390 print_r(array_unique($this->dirty_objects[$group]));
397 function WP_Object_Cache() {
400 if (defined('DISABLE_CACHE'))
403 // Disable the persistent cache if safe_mode is on.
404 if ( ini_get('safe_mode') && ! defined('ENABLE_CACHE') )
407 if (defined('CACHE_PATH'))
408 $this->cache_dir = CACHE_PATH;
410 // Using the correct separator eliminates some cache flush errors on Windows
411 $this->cache_dir = ABSPATH.'wp-content'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR;
413 if (is_writable($this->cache_dir) && is_dir($this->cache_dir)) {
414 $this->cache_enabled = true;
416 if (is_writable(ABSPATH.'wp-content')) {
417 $this->cache_enabled = true;
421 if (defined('CACHE_EXPIRATION_TIME'))
422 $this->expiration_time = CACHE_EXPIRATION_TIME;
424 $this->blog_id = md5($blog_id);