PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 2.0.3
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v2.0.3
trunk 1.0 1.0.1 1.1 1.1.1 1.1.2 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1 2.1.1 2.1.2 2.2 2.2.1 All 69 releases
powered-cache / includes / dropins / memcached-object-cache.php

memcached-object-cache.php in Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score 2.0.3, at includes/dropins/memcached-object-cache.php

521 lines 12.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Memcached Redux drop-in, Mika's fork
4 *
5 * @link https://github.com/skopco/memcached-redux
6 * Upstream: 0.1.7
7 */
8
9 if ( ! defined( 'WP_CACHE_KEY_SALT' ) ) {
10 define( 'WP_CACHE_KEY_SALT', DB_NAME );
11 }
12
13 if ( class_exists( 'Memcached' ) ):
14
15 function wp_cache_add( $key, $data, $group = '', $expire = 0 ) {
16 global $wp_object_cache;
17
18 return $wp_object_cache->add( $key, $data, $group, $expire );
19 }
20
21 function wp_cache_incr( $key, $n = 1, $group = '' ) {
22 global $wp_object_cache;
23
24 return $wp_object_cache->incr( $key, $n, $group );
25 }
26
27 function wp_cache_decr( $key, $n = 1, $group = '' ) {
28 global $wp_object_cache;
29
30 return $wp_object_cache->decr( $key, $n, $group );
31 }
32
33 function wp_cache_close() {
34 global $wp_object_cache;
35
36 return $wp_object_cache->close();
37 }
38
39 function wp_cache_delete( $key, $group = '' ) {
40 global $wp_object_cache;
41
42 return $wp_object_cache->delete( $key, $group );
43 }
44
45 function wp_cache_flush() {
46 global $wp_object_cache;
47
48 return $wp_object_cache->flush();
49 }
50
51 function wp_cache_get( $key, $group = '', $force = false, &$found = null ) {
52 global $wp_object_cache;
53
54 return $wp_object_cache->get( $key, $group, $force, $found );
55 }
56
57 /**
58 * $keys_and_groups = array(
59 * array( 'key', 'group' ),
60 * array( 'key', '' ),
61 * array( 'key', 'group' ),
62 * array( 'key' )
63 * );
64 */
65 function wp_cache_get_multi( $key_and_groups, $bucket = 'default' ) {
66 global $wp_object_cache;
67
68 return $wp_object_cache->get_multi( $key_and_groups, $bucket );
69 }
70
71 /**
72 * $items = array(
73 * array( 'key', 'data', 'group' ),
74 * array( 'key', 'data' )
75 * );
76 */
77 function wp_cache_set_multi( $items, $expire = 0, $group = 'default' ) {
78 global $wp_object_cache;
79
80 return $wp_object_cache->set_multi( $items, $expire = 0, $group = 'default' );
81 }
82
83 function wp_cache_init() {
84 global $wp_object_cache;
85
86 $wp_object_cache = new WP_Object_Cache();
87 }
88
89 function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) {
90 global $wp_object_cache;
91
92 return $wp_object_cache->replace( $key, $data, $group, $expire );
93 }
94
95 function wp_cache_set( $key, $data, $group = '', $expire = 0 ) {
96 global $wp_object_cache;
97
98 if ( defined( 'WP_INSTALLING' ) == false ) {
99 return $wp_object_cache->set( $key, $data, $group, $expire );
100 } else {
101 return $wp_object_cache->delete( $key, $group );
102 }
103 }
104
105 function wp_cache_add_global_groups( $groups ) {
106 global $wp_object_cache;
107
108 $wp_object_cache->add_global_groups( $groups );
109 }
110
111 function wp_cache_add_non_persistent_groups( $groups ) {
112 global $wp_object_cache;
113
114 $wp_object_cache->add_non_persistent_groups( $groups );
115 }
116
117 class WP_Object_Cache {
118 var $global_groups = array();
119
120 var $no_mc_groups = array();
121
122 var $cache = array();
123 var $mc = array();
124 var $stats = array();
125 var $group_ops = array();
126
127 var $cache_enabled = true;
128 var $default_expiration = 0;
129
130 function add( $id, $data, $group = 'default', $expire = 0 ) {
131 $key = $this->key( $id, $group );
132
133 if ( is_object( $data ) ) {
134 $data = clone $data;
135 }
136
137 if ( in_array( $group, $this->no_mc_groups ) ) {
138 $this->cache[ $key ] = $data;
139
140 return true;
141 } elseif ( isset( $this->cache[ $key ] ) && $this->cache[ $key ] !== false ) {
142 return false;
143 }
144
145 $mc =& $this->get_mc( $group );
146 $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
147 $result = $mc->add( $key, $data, $expire );
148
149 if ( false !== $result ) {
150 ++ $this->stats['add'];
151 $this->group_ops[ $group ][] = "add $id";
152 $this->cache[ $key ] = $data;
153 }
154
155 return $result;
156 }
157
158 function add_global_groups( $groups ) {
159 if ( ! is_array( $groups ) ) {
160 $groups = (array) $groups;
161 }
162
163 $this->global_groups = array_merge( $this->global_groups, $groups );
164 $this->global_groups = array_unique( $this->global_groups );
165 }
166
167 function add_non_persistent_groups( $groups ) {
168 if ( ! is_array( $groups ) ) {
169 $groups = (array) $groups;
170 }
171
172 $this->no_mc_groups = array_merge( $this->no_mc_groups, $groups );
173 $this->no_mc_groups = array_unique( $this->no_mc_groups );
174 }
175
176 function incr( $id, $n = 1, $group = 'default' ) {
177 $key = $this->key( $id, $group );
178 $mc =& $this->get_mc( $group );
179 $this->cache[ $key ] = $mc->increment( $key, $n );
180
181 return $this->cache[ $key ];
182 }
183
184 function decr( $id, $n = 1, $group = 'default' ) {
185 $key = $this->key( $id, $group );
186 $mc =& $this->get_mc( $group );
187 $this->cache[ $key ] = $mc->decrement( $key, $n );
188
189 return $this->cache[ $key ];
190 }
191
192 function close() {
193 // Silence is Golden.
194 }
195
196 function delete( $id, $group = 'default' ) {
197 $key = $this->key( $id, $group );
198
199 if ( in_array( $group, $this->no_mc_groups ) ) {
200 unset( $this->cache[ $key ] );
201
202 return true;
203 }
204
205 $mc =& $this->get_mc( $group );
206
207 $result = $mc->delete( $key );
208
209 if ( false !== $result ) {
210 ++ $this->stats['delete'];
211 $this->group_ops[ $group ][] = "delete $id";
212 unset( $this->cache[ $key ] );
213 }
214
215 return $result;
216 }
217
218 function flush() {
219 // Don't flush if multi-blog.
220 if ( function_exists( 'is_site_admin' ) || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) {
221 return true;
222 }
223
224 $ret = true;
225 foreach ( array_keys( $this->mc ) as $group ) {
226 $ret &= $this->mc[ $group ]->flush();
227 }
228
229 return $ret;
230 }
231
232 function get( $id, $group = 'default', $force = false, &$found = null ) {
233 $key = $this->key( $id, $group );
234 $mc =& $this->get_mc( $group );
235 $found = false;
236
237 if ( isset( $this->cache[ $key ] ) && ( ! $force || in_array( $group, $this->no_mc_groups ) ) ) {
238 $found = true;
239 if ( is_object( $this->cache[ $key ] ) ) {
240 $value = clone $this->cache[ $key ];
241 } else {
242 $value = $this->cache[ $key ];
243 }
244 } else if ( in_array( $group, $this->no_mc_groups ) ) {
245 $this->cache[ $key ] = $value = false;
246 } else {
247 $value = $mc->get( $key );
248 if ( empty( $value ) || ( is_integer( $value ) && - 1 == $value ) ) {
249 $value = false;
250 $found = $mc->getResultCode() !== Memcached::RES_NOTFOUND;
251 } else {
252 $found = true;
253 }
254 $this->cache[ $key ] = $value;
255 }
256
257 if ( $found ) {
258 ++ $this->stats['get'];
259 $this->group_ops[ $group ][] = "get $id";
260 } else {
261 ++ $this->stats['miss'];
262 }
263
264 if ( 'checkthedatabaseplease' === $value ) {
265 unset( $this->cache[ $key ] );
266 $value = false;
267 }
268
269 return $value;
270 }
271
272 function get_multi( $keys, $group = 'default' ) {
273 $return = array();
274 $gets = array();
275 foreach ( $keys as $i => $values ) {
276 $mc =& $this->get_mc( $group );
277 $values = (array) $values;
278 if ( empty( $values[1] ) ) {
279 $values[1] = 'default';
280 }
281
282 list( $id, $group ) = (array) $values;
283 $key = $this->key( $id, $group );
284
285 if ( isset( $this->cache[ $key ] ) ) {
286
287 if ( is_object( $this->cache[ $key ] ) ) {
288 $return[ $key ] = clone $this->cache[ $key ];
289 } else {
290 $return[ $key ] = $this->cache[ $key ];
291 }
292
293 } else if ( in_array( $group, $this->no_mc_groups ) ) {
294 $return[ $key ] = false;
295
296 } else {
297 $gets[ $key ] = $key;
298 }
299 }
300
301 if ( ! empty( $gets ) ) {
302 $results = $mc->getMulti( $gets, $null, Memcached::GET_PRESERVE_ORDER );
303 $joined = array_combine( array_keys( $gets ), array_values( $results ) );
304 $return = array_merge( $return, $joined );
305 }
306
307 ++ $this->stats['get_multi'];
308 $this->group_ops[ $group ][] = "get_multi $id";
309 $this->cache = array_merge( $this->cache, $return );
310
311 return array_values( $return );
312 }
313
314 function key( $key, $group ) {
315 if ( empty( $group ) ) {
316 $group = 'default';
317 }
318
319 if ( false !== array_search( $group, $this->global_groups ) ) {
320 $prefix = $this->global_prefix;
321 } else {
322 $prefix = $this->blog_prefix;
323 }
324
325 return preg_replace( '/\s+/', '', WP_CACHE_KEY_SALT . "$prefix$group:$key" );
326 }
327
328 function replace( $id, $data, $group = 'default', $expire = 0 ) {
329 $key = $this->key( $id, $group );
330 $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
331 $mc =& $this->get_mc( $group );
332
333 if ( is_object( $data ) ) {
334 $data = clone $data;
335 }
336
337 $result = $mc->replace( $key, $data, $expire );
338 if ( false !== $result ) {
339 $this->cache[ $key ] = $data;
340 }
341
342 return $result;
343 }
344
345 function set( $id, $data, $group = 'default', $expire = 0 ) {
346 $key = $this->key( $id, $group );
347 if ( isset( $this->cache[ $key ] ) && ( 'checkthedatabaseplease' === $this->cache[ $key ] ) ) {
348 return false;
349 }
350
351 if ( is_object( $data ) ) {
352 $data = clone $data;
353 }
354
355 $this->cache[ $key ] = $data;
356
357 if ( in_array( $group, $this->no_mc_groups ) ) {
358 return true;
359 }
360
361 $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
362 $mc =& $this->get_mc( $group );
363 $result = $mc->set( $key, $data, $expire );
364
365 return $result;
366 }
367
368 function set_multi( $items, $expire = 0, $group = 'default' ) {
369 $sets = array();
370 $mc =& $this->get_mc( $group );
371 $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
372
373 foreach ( $items as $i => $item ) {
374 if ( empty( $item[2] ) ) {
375 $item[2] = 'default';
376 }
377
378 list( $id, $data, $group ) = $item;
379
380 $key = $this->key( $id, $group );
381 if ( isset( $this->cache[ $key ] ) && ( 'checkthedatabaseplease' === $this->cache[ $key ] ) ) {
382 continue;
383 }
384
385 if ( is_object( $data ) ) {
386 $data = clone $data;
387 }
388
389 $this->cache[ $key ] = $data;
390
391 if ( in_array( $group, $this->no_mc_groups ) ) {
392 continue;
393 }
394
395 $sets[ $key ] = $data;
396 }
397
398 if ( ! empty( $sets ) ) {
399 $mc->setMulti( $sets, $expire );
400 }
401 }
402
403 function colorize_debug_line( $line ) {
404 $colors = array(
405 'get' => 'green',
406 'set' => 'purple',
407 'add' => 'blue',
408 'delete' => 'red'
409 );
410
411 $cmd = substr( $line, 0, strpos( $line, ' ' ) );
412
413 $cmd2 = "<span style='color:" . esc_attr( $colors[ $cmd ] ) . "'>" . esc_html( $cmd ) . "</span>";
414
415 return $cmd2 . esc_html( substr( $line, strlen( $cmd ) ) ) . "\n";
416 }
417
418 function stats() {
419 echo "<p>\n";
420 foreach ( $this->stats as $stat => $n ) {
421 echo "<strong>" . esc_html( $stat ) . "</strong> " . esc_html( $n );
422 echo "<br/>\n";
423 }
424 echo "</p>\n";
425 echo "<h3>Memcached:</h3>";
426 foreach ( $this->group_ops as $group => $ops ) {
427 if ( ! isset( $_GET['debug_queries'] ) && 500 < count( $ops ) ) {
428 $ops = array_slice( $ops, 0, 500 );
429 echo "<big>Too many to show! <a href='" . esc_url( add_query_arg( 'debug_queries', 'true' ) ) . "'>Show them anyway</a>.</big>\n";
430 }
431 echo "<h4>" . esc_html( $group ) . " commands</h4>";
432 echo "<pre>\n";
433 $lines = array();
434 foreach ( $ops as $op ) {
435 $lines[] = $this->colorize_debug_line( $op );
436 }
437 print_r( $lines );
438 echo "</pre>\n";
439 }
440
441 if ( ! empty( $this->debug ) && $this->debug ) {
442 var_dump( $this->memcache_debug );
443 }
444 }
445
446 function &get_mc( $group ) {
447 if ( isset( $this->mc[ $group ] ) ) {
448 return $this->mc[ $group ];
449 }
450
451 return $this->mc['default'];
452 }
453
454 function __construct() {
455
456 $this->stats = array(
457 'get' => 0,
458 'get_multi' => 0,
459 'add' => 0,
460 'set' => 0,
461 'delete' => 0,
462 'miss' => 0,
463 );
464
465 global $memcached_servers;
466
467 if ( isset( $memcached_servers ) ) {
468 $buckets = $memcached_servers;
469 } else {
470 $buckets = array( '127.0.0.1:11211' );
471 }
472
473 reset( $buckets );
474 if ( is_int( key( $buckets ) ) ) {
475 $buckets = array( 'default' => $buckets );
476 }
477
478 foreach ( $buckets as $bucket => $servers ) {
479 $this->mc[ $bucket ] = new Memcached();
480
481 $instances = array();
482 foreach ( $servers as $server ) {
483 @list( $node, $port ) = explode( ':', $server );
484 if ( empty( $port ) ) {
485 $port = ini_get( 'memcache.default_port' );
486 }
487 $port = intval( $port );
488 if ( ! $port ) {
489 $port = 11211;
490 }
491
492 $instances[] = array( $node, $port, 1 );
493 }
494 $this->mc[ $bucket ]->addServers( $instances );
495 }
496
497 global $blog_id, $table_prefix;
498 $this->global_prefix = '';
499 $this->blog_prefix = '';
500 if ( function_exists( 'is_multisite' ) ) {
501 $this->global_prefix = ( is_multisite() || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) ? '' : $table_prefix;
502 $this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix ) . ':';
503 }
504
505 $this->cache_hits =& $this->stats['get'];
506 $this->cache_misses =& $this->stats['miss'];
507 }
508 }
509 else: // No Memcached
510
511 // In 3.7+, we can handle this smoothly
512 if ( function_exists( 'wp_using_ext_object_cache' ) ) {
513 wp_using_ext_object_cache( false );
514
515 // In earlier versions, there isn't a clean bail-out method.
516 } else {
517 wp_die( 'Memcached class not available.' );
518 }
519
520 endif;
521