Make WordPress Core

Ticket #20499: object-cache.php

File object-cache.php, 9.7 KB (added by toddlahman, 13 years ago)

object-cache patch

Line 
1<?php
2
3/*
4Plugin Name: Memcached
5Description: Memcached backend for the WP Object Cache.
6Version: 2.0.2
7Plugin URI: http://wordpress.org/extend/plugins/memcached/
8Author: Ryan Boren, Denis de Bernardy, Matt Martz
9
10Install this file to wp-content/object-cache.php
11*/
12
13function wp_cache_add($key, $data, $group = '', $expire = 0) {
14        global $wp_object_cache;
15
16        return $wp_object_cache->add($key, $data, $group, $expire);
17}
18
19function wp_cache_incr($key, $n = 1, $group = '') {
20        global $wp_object_cache;
21
22        return $wp_object_cache->incr($key, $n, $group);
23}
24
25function wp_cache_decr($key, $n = 1, $group = '') {
26        global $wp_object_cache;
27
28        return $wp_object_cache->decr($key, $n, $group);
29}
30
31function wp_cache_close() {
32        global $wp_object_cache;
33
34        return $wp_object_cache->close();
35}
36
37function wp_cache_delete($key, $group = '') {
38        global $wp_object_cache;
39
40        return $wp_object_cache->delete($key, $group);
41}
42
43function wp_cache_flush() {
44        global $wp_object_cache;
45
46        return $wp_object_cache->flush();
47}
48
49function wp_cache_get($key, $group = '', $force = false) {
50        global $wp_object_cache;
51
52        return $wp_object_cache->get($key, $group, $force);
53}
54
55function wp_cache_init() {
56        global $wp_object_cache;
57
58        $wp_object_cache = new WP_Object_Cache();
59}
60
61function wp_cache_replace($key, $data, $group = '', $expire = 0) {
62        global $wp_object_cache;
63
64        return $wp_object_cache->replace($key, $data, $group, $expire);
65}
66
67function wp_cache_set($key, $data, $group = '', $expire = 0) {
68        global $wp_object_cache;
69
70        if ( defined('WP_INSTALLING') == false )
71                return $wp_object_cache->set($key, $data, $group, $expire);
72        else
73                return $wp_object_cache->delete($key, $group);
74}
75
76function wp_cache_add_global_groups( $groups ) {
77        global $wp_object_cache;
78
79        $wp_object_cache->add_global_groups($groups);
80}
81
82function wp_cache_add_non_persistent_groups( $groups ) {
83        global $wp_object_cache;
84
85        $wp_object_cache->add_non_persistent_groups($groups);
86}
87
88class WP_Object_Cache {
89        var $global_groups = array();
90
91        var $no_mc_groups = array();
92
93        var $cache = array();
94        var $mc = array();
95        var $stats = array();
96        var $group_ops = array();
97
98        var $cache_enabled = true;
99        var $default_expiration = 0;
100
101        function add($id, $data, $group = 'default', $expire = 0) {
102                $key = $this->key($id, $group);
103
104                if ( is_object( $data ) )
105                        $data = clone $data;
106
107                if ( in_array($group, $this->no_mc_groups) ) {
108                        $this->cache[$key] = $data;
109                        return true;
110                } elseif ( isset($this->cache[$key]) && $this->cache[$key] !== false ) {
111                        return false;
112                }
113
114                $mc =& $this->get_mc($group);
115                $expire = ($expire == 0) ? $this->default_expiration : $expire;
116                $result = $mc->add($key, $data, false, $expire);
117
118                if ( false !== $result ) {
119                        @ ++$this->stats['add'];
120                        $this->group_ops[$group][] = "add $id";
121                        $this->cache[$key] = $data;
122                }
123
124                return $result;
125        }
126
127        function add_global_groups($groups) {
128                if ( ! is_array($groups) )
129                        $groups = (array) $groups;
130
131                $this->global_groups = array_merge($this->global_groups, $groups);
132                $this->global_groups = array_unique($this->global_groups);
133        }
134
135        function add_non_persistent_groups($groups) {
136                if ( ! is_array($groups) )
137                        $groups = (array) $groups;
138
139                $this->no_mc_groups = array_merge($this->no_mc_groups, $groups);
140                $this->no_mc_groups = array_unique($this->no_mc_groups);
141        }
142
143        function incr($id, $n = 1, $group = 'default' ) {
144                $key = $this->key($id, $group);
145                $mc =& $this->get_mc($group);
146                $this->cache[ $key ] = $mc->increment( $key, $n );     
147                return $this->cache[ $key ];
148        }
149
150        function decr($id, $n = 1, $group = 'default' ) {
151                $key = $this->key($id, $group);
152                $mc =& $this->get_mc($group);
153                $this->cache[ $key ] = $mc->decrement( $key, $n );
154                return $this->cache[ $key ];
155        }
156
157        function close() {
158
159                foreach ( $this->mc as $bucket => $mc )
160                        $mc->close();
161        }
162
163        function delete($id, $group = 'default') {
164                $key = $this->key($id, $group);
165
166                if ( in_array($group, $this->no_mc_groups) ) {
167                        unset($this->cache[$key]);
168                        return true;
169                }
170
171                $mc =& $this->get_mc($group);
172
173                $result = $mc->delete($key);
174
175                @ ++$this->stats['delete'];
176                $this->group_ops[$group][] = "delete $id";
177
178                if ( false !== $result )
179                        unset($this->cache[$key]);
180
181                return $result; 
182        }
183
184        function flush() {
185                // Don't flush if multi-blog.
186                if ( function_exists('is_site_admin') || defined('CUSTOM_USER_TABLE') && defined('CUSTOM_USER_META_TABLE') )
187                        return true;
188
189                $ret = true;
190                foreach ( array_keys($this->mc) as $group )
191                        $ret &= $this->mc[$group]->flush();
192                return $ret;
193        }
194
195        function get($id, $group = 'default', $force = false) {
196                $key = $this->key($id, $group);
197                $mc =& $this->get_mc($group);
198
199                if ( isset($this->cache[$key]) && ( !$force || in_array($group, $this->no_mc_groups) ) ) {
200                        if ( is_object( $this->cache[$key] ) )
201                                $value = clone $this->cache[$key];
202                        else
203                                $value = $this->cache[$key];
204                } else if ( in_array($group, $this->no_mc_groups) ) {
205                        $this->cache[$key] = $value = false;
206                } else {
207                        $value = $mc->get($key);
208                        if ( NULL === $value )
209                                $value = false;
210                        $this->cache[$key] = $value;
211                }
212
213                @ ++$this->stats['get'];
214                $this->group_ops[$group][] = "get $id";
215
216                if ( 'checkthedatabaseplease' === $value ) {
217                        unset( $this->cache[$key] );
218                        $value = false;
219                }
220
221                return $value;
222        }
223
224        function get_multi( $groups ) {
225                /*
226                format: $get['group-name'] = array( 'key1', 'key2' );
227                */
228                $return = array();
229                foreach ( $groups as $group => $ids ) {
230                        $mc =& $this->get_mc($group);
231                        foreach ( $ids as $id ) {
232                                $key = $this->key($id, $group);
233                                if ( isset($this->cache[$key]) ) {
234                                        if ( is_object( $this->cache[$key] ) )
235                                                $return[$key] = clone $this->cache[$key];
236                                        else
237                                                $return[$key] = $this->cache[$key];
238                                        continue;
239                                } else if ( in_array($group, $this->no_mc_groups) ) {
240                                        $return[$key] = false;
241                                        continue;
242                                } else {
243                                        $return[$key] = $mc->get($key);
244                                }
245                        }
246                        if ( $to_get ) {
247                                $vals = $mc->get_multi( $to_get );
248                                $return = array_merge( $return, $vals );
249                        }
250                }
251                @ ++$this->stats['get_multi'];
252                $this->group_ops[$group][] = "get_multi $id";
253                $this->cache = array_merge( $this->cache, $return );
254                return $return;
255        }
256
257        function key($key, $group) {   
258                if ( empty($group) )
259                        $group = 'default';
260
261                if ( false !== array_search($group, $this->global_groups) )
262                        $prefix = $this->global_prefix;
263                else
264                        $prefix = $this->blog_prefix;
265
266                return preg_replace('/\s+/', '', "$prefix$group:$key");
267        }
268
269        function replace($id, $data, $group = 'default', $expire = 0) {
270                $key = $this->key($id, $group);
271                $expire = ($expire == 0) ? $this->default_expiration : $expire;
272                $mc =& $this->get_mc($group);
273
274                if ( is_object( $data ) )
275                        $data = clone $data;
276
277                $result = $mc->replace($key, $data, false, $expire);
278                if ( false !== $result )
279                        $this->cache[$key] = $data;
280                return $result;
281        }
282
283        function set($id, $data, $group = 'default', $expire = 0) {
284                $key = $this->key($id, $group);
285                if ( isset($this->cache[$key]) && ('checkthedatabaseplease' === $this->cache[$key]) )
286                        return false;
287
288                if ( is_object($data) )
289                        $data = clone $data;
290
291                $this->cache[$key] = $data;
292
293                if ( in_array($group, $this->no_mc_groups) )
294                        return true;
295
296                $expire = ($expire == 0) ? $this->default_expiration : $expire;
297                $mc =& $this->get_mc($group);
298                $result = $mc->set($key, $data, false, $expire);
299
300                return $result;
301        }
302
303        function colorize_debug_line($line) {
304                $colors = array(
305                        'get' => 'green',
306                        'set' => 'purple',
307                        'add' => 'blue',
308                        'delete' => 'red');
309
310                $cmd = substr($line, 0, strpos($line, ' '));
311
312                $cmd2 = "<span style='color:{$colors[$cmd]}'>$cmd</span>";
313
314                return $cmd2 . substr($line, strlen($cmd)) . "\n";
315        }
316
317        function stats() {
318                echo "<p>\n";
319                foreach ( $this->stats as $stat => $n ) {
320                        echo "<strong>$stat</strong> $n";
321                        echo "<br/>\n";
322                }
323                echo "</p>\n";
324                echo "<h3>Memcached:</h3>";
325                foreach ( $this->group_ops as $group => $ops ) {
326                        if ( !isset($_GET['debug_queries']) && 500 < count($ops) ) { 
327                                $ops = array_slice( $ops, 0, 500 ); 
328                                echo "<big>Too many to show! <a href='" . add_query_arg( 'debug_queries', 'true' ) . "'>Show them anyway</a>.</big>\n";
329                        } 
330                        echo "<h4>$group commands</h4>";
331                        echo "<pre>\n";
332                        $lines = array();
333                        foreach ( $ops as $op ) {
334                                $lines[] = $this->colorize_debug_line($op); 
335                        }
336                        print_r($lines);
337                        echo "</pre>\n";
338                }
339
340                if ( $this->debug )
341                        var_dump($this->memcache_debug);
342        }
343
344        function &get_mc($group) {
345                if ( isset($this->mc[$group]) )
346                        return $this->mc[$group];
347                return $this->mc['default'];
348        }
349
350        function failure_callback($host, $port) {
351                //error_log("Connection failure for $host:$port\n", 3, '/tmp/memcached.txt');
352        }
353
354        function WP_Object_Cache() {
355                global $memcached_servers;
356
357                if ( isset($memcached_servers) )
358                        $buckets = $memcached_servers;
359                else
360                        $buckets = array('127.0.0.1');
361
362                reset($buckets);
363                if ( is_int( key($buckets) ) )
364                        $buckets = array('default' => $buckets);
365
366                foreach ( $buckets as $bucket => $servers) {
367                        $this->mc[$bucket] = new Memcache();
368                        foreach ( $servers as $server  ) {
369                                list ( $node, $port ) = explode(':', $server);
370                                if ( !$port )
371                                        $port = ini_get('memcache.default_port');
372                                $port = intval($port);
373                                if ( !$port )
374                                        $port = 11211;
375                                $this->mc[$bucket]->addServer($node, $port, true, 1, 1, 15, true, array($this, 'failure_callback'));
376                                $this->mc[$bucket]->setCompressThreshold(20000, 0.2);
377                        }
378                }
379
380                global $blog_id, $table_prefix;
381                $this->global_prefix = '';
382                $this->blog_prefix = '';
383                if ( function_exists( 'is_multisite' ) && is_multisite() ) {
384                        $this->global_prefix = ( is_multisite() || defined('CUSTOM_USER_TABLE') && defined('CUSTOM_USER_META_TABLE') ) ? '' : $table_prefix;
385                        $this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix ) . ':';
386                } else {
387                        $this->global_prefix = $table_prefix;
388                        $this->blog_prefix = $blog_id;
389                }
390
391                $this->cache_hits =& $this->stats['get'];
392                $this->cache_misses =& $this->stats['add'];
393        }
394}
395?>