PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 3.6
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v3.6
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 3.6, at includes/dropins/memcached-object-cache.php

650 lines 15.7 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/poweredcache/memcached-redux
6 * Upstream: 0.2.1
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 /**
16 * Determines whether the object cache implementation supports a particular feature.
17 *
18 * @param string $feature Name of the feature to check for. Possible values include:
19 * 'add_multiple', 'set_multiple', 'get_multiple', 'delete_multiple',
20 * 'flush_runtime', 'flush_group'.
21 *
22 * @return bool True if the feature is supported, false otherwise.
23 * @since 0.1.9
24 *
25 */
26 function wp_cache_supports( $feature ) {
27 switch ( $feature ) {
28 case 'add_multiple':
29 case 'set_multiple':
30 case 'flush_runtime':
31 case 'get_multiple':
32 case 'delete_multiple':
33 return true;
34
35 default:
36 return false;
37 }
38 }
39
40 function wp_cache_add( $key, $data, $group = '', $expire = 0 ) {
41 global $wp_object_cache;
42
43 return $wp_object_cache->add( $key, $data, $group, $expire );
44 }
45
46 function wp_cache_add_multiple( array $data, $group = '', $expire = 0 ) {
47 global $wp_object_cache;
48
49 return $wp_object_cache->add_multiple( $data, $group, $expire );
50 }
51
52 function wp_cache_incr( $key, $n = 1, $group = '' ) {
53 global $wp_object_cache;
54
55 return $wp_object_cache->incr( $key, $n, $group );
56 }
57
58 function wp_cache_decr( $key, $n = 1, $group = '' ) {
59 global $wp_object_cache;
60
61 return $wp_object_cache->decr( $key, $n, $group );
62 }
63
64 function wp_cache_close() {
65 global $wp_object_cache;
66
67 return $wp_object_cache->close();
68 }
69
70 function wp_cache_delete( $key, $group = '' ) {
71 global $wp_object_cache;
72
73 return $wp_object_cache->delete( $key, $group );
74 }
75
76 function wp_cache_delete_multiple( array $keys, $group = '' ) {
77 global $wp_object_cache;
78
79 return $wp_object_cache->delete_multiple( $keys, $group );
80 }
81
82 function wp_cache_flush() {
83 global $wp_object_cache;
84
85 return $wp_object_cache->flush();
86 }
87
88 function wp_cache_get( $key, $group = '', $force = false, &$found = null ) {
89 global $wp_object_cache;
90
91 return $wp_object_cache->get( $key, $group, $force, $found );
92 }
93
94 function wp_cache_get_multiple( $keys, $group = '', $force = false ) {
95 global $wp_object_cache;
96
97 return $wp_object_cache->get_multiple( $keys, $group, $force );
98 }
99
100 /**
101 * $keys_and_groups = array(
102 * array( 'key', 'group' ),
103 * array( 'key', '' ),
104 * array( 'key', 'group' ),
105 * array( 'key' )
106 * );
107 */
108 function wp_cache_get_multi( $key_and_groups, $bucket = 'default' ) {
109 global $wp_object_cache;
110
111 return $wp_object_cache->get_multi( $key_and_groups, $bucket );
112 }
113
114 function wp_cache_set_multiple( array $data, $group = '', $expire = 0 ) {
115 global $wp_object_cache;
116
117 return $wp_object_cache->set_multiple( $data, $group, $expire );
118 }
119
120 /**
121 * $items = array(
122 * array( 'key', 'data', 'group' ),
123 * array( 'key', 'data' )
124 * );
125 */
126 function wp_cache_set_multi( $items, $expire = 0, $group = 'default' ) {
127 global $wp_object_cache;
128
129 return $wp_object_cache->set_multi( $items, $expire = 0, $group = 'default' );
130 }
131
132 function wp_cache_init() {
133 global $wp_object_cache;
134
135 $wp_object_cache = new WP_Object_Cache();
136 }
137
138 function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) {
139 global $wp_object_cache;
140
141 return $wp_object_cache->replace( $key, $data, $group, $expire );
142 }
143
144 function wp_cache_set( $key, $data, $group = '', $expire = 0 ) {
145 global $wp_object_cache;
146
147 if ( defined( 'WP_INSTALLING' ) == false ) {
148 return $wp_object_cache->set( $key, $data, $group, $expire );
149 } else {
150 return $wp_object_cache->delete( $key, $group );
151 }
152 }
153
154 function wp_cache_add_global_groups( $groups ) {
155 global $wp_object_cache;
156
157 $wp_object_cache->add_global_groups( $groups );
158 }
159
160 function wp_cache_add_non_persistent_groups( $groups ) {
161 global $wp_object_cache;
162
163 $wp_object_cache->add_non_persistent_groups( $groups );
164 }
165
166 function wp_cache_flush_runtime() {
167 global $wp_object_cache;
168
169 return $wp_object_cache->flush_runtime();
170 }
171
172
173 class WP_Object_Cache {
174 var $global_groups = array();
175
176 var $no_mc_groups = array();
177
178 var $cache = array();
179 var $mc = array();
180 var $default_mcs = array();
181 var $stats = array(
182 'get' => 0,
183 'get_local' => 0,
184 'get_multi' => 0,
185 'set' => 0,
186 'set_local' => 0,
187 'add' => 0,
188 'delete' => 0,
189 'delete_local' => 0,
190 'slow-ops' => 0,
191 );
192 var $group_ops = array();
193 var $cache_hits = 0;
194 var $cache_misses = 0;
195 var $global_prefix = '';
196 var $blog_prefix = '';
197 var $key_salt = '';
198
199 var $flush_group = 'WP_Object_Cache';
200 var $global_flush_group = 'WP_Object_Cache_global';
201 var $flush_key = "flush_number_v4";
202 var $old_flush_key = "flush_number";
203 var $flush_number = array();
204 var $global_flush_number = null;
205
206 var $cache_enabled = true;
207 var $default_expiration = 0;
208
209 var $time_start = 0;
210
211 function add( $id, $data, $group = 'default', $expire = 0 ) {
212 $key = $this->key( $id, $group );
213
214 if ( is_object( $data ) ) {
215 $data = clone $data;
216 }
217
218 if ( in_array( $group, $this->no_mc_groups ) ) {
219 $this->cache[ $key ] = $data;
220
221 return true;
222 } elseif ( isset( $this->cache[ $key ] ) && $this->cache[ $key ] !== false ) {
223 return false;
224 }
225
226 $mc =& $this->get_mc( $group );
227 $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
228 $result = $mc->add( $key, $data, $expire );
229
230 if ( false !== $result ) {
231 ++ $this->stats['add'];
232 $this->group_ops[ $group ][] = "add $id";
233 $this->cache[ $key ] = $data;
234 }
235
236 return $result;
237 }
238
239 function add_global_groups( $groups ) {
240 if ( ! is_array( $groups ) ) {
241 $groups = (array) $groups;
242 }
243
244 $this->global_groups = array_merge( $this->global_groups, $groups );
245 $this->global_groups = array_unique( $this->global_groups );
246 }
247
248 public function add_multiple( array $data, $group = '', $expire = 0 ) {
249 $values = array();
250
251 foreach ( $data as $key => $value ) {
252 $values[ $key ] = $this->add( $key, $value, $group, $expire );
253 }
254
255 return $values;
256 }
257
258
259 function add_non_persistent_groups( $groups ) {
260 if ( ! is_array( $groups ) ) {
261 $groups = (array) $groups;
262 }
263
264 $this->no_mc_groups = array_merge( $this->no_mc_groups, $groups );
265 $this->no_mc_groups = array_unique( $this->no_mc_groups );
266 }
267
268 function incr( $id, $n = 1, $group = 'default' ) {
269 $key = $this->key( $id, $group );
270 $mc =& $this->get_mc( $group );
271 $this->cache[ $key ] = $mc->increment( $key, $n );
272
273 return $this->cache[ $key ];
274 }
275
276 function decr( $id, $n = 1, $group = 'default' ) {
277 $key = $this->key( $id, $group );
278 $mc =& $this->get_mc( $group );
279 $this->cache[ $key ] = $mc->decrement( $key, $n );
280
281 return $this->cache[ $key ];
282 }
283
284 function close() {
285 // Silence is Golden.
286 }
287
288 function flush_runtime() {
289 $this->cache = array();
290 $this->group_ops = array();
291
292 return true;
293 }
294
295 function delete( $id, $group = 'default' ) {
296 $key = $this->key( $id, $group );
297
298 if ( in_array( $group, $this->no_mc_groups ) ) {
299 unset( $this->cache[ $key ] );
300
301 return true;
302 }
303
304 $mc =& $this->get_mc( $group );
305
306 $result = $mc->delete( $key );
307
308 if ( false !== $result ) {
309 ++ $this->stats['delete'];
310 $this->group_ops[ $group ][] = "delete $id";
311 unset( $this->cache[ $key ] );
312 }
313
314 return $result;
315 }
316
317 public function delete_multiple( array $keys, $group = '' ) {
318 $values = array();
319
320 foreach ( $keys as $key ) {
321 $values[ $key ] = $this->delete( $key, $group );
322 }
323
324 return $values;
325 }
326
327 function flush() {
328 // Don't flush if multi-blog.
329 if ( function_exists( 'is_site_admin' ) || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) {
330 return true;
331 }
332
333 $ret = true;
334 foreach ( array_keys( $this->mc ) as $group ) {
335 $ret &= $this->mc[ $group ]->flush();
336 }
337
338 return $ret;
339 }
340
341 function get( $id, $group = 'default', $force = false, &$found = null ) {
342 $key = $this->key( $id, $group );
343 $mc =& $this->get_mc( $group );
344 $found = false;
345
346 if ( isset( $this->cache[ $key ] ) && ( ! $force || in_array( $group, $this->no_mc_groups ) ) ) {
347 $found = true;
348 if ( is_object( $this->cache[ $key ] ) ) {
349 $value = clone $this->cache[ $key ];
350 } else {
351 $value = $this->cache[ $key ];
352 }
353 } else if ( in_array( $group, $this->no_mc_groups ) ) {
354 $this->cache[ $key ] = $value = false;
355 } else {
356 $value = $mc->get( $key );
357 if ( empty( $value ) || ( is_integer( $value ) && - 1 == $value ) ) {
358 $value = false;
359 $found = $mc->getResultCode() !== Memcached::RES_NOTFOUND;
360 } else {
361 $found = true;
362 }
363 $this->cache[ $key ] = $value;
364 }
365
366 if ( $found ) {
367 ++ $this->stats['get'];
368 $this->group_ops[ $group ][] = "get $id";
369 } else {
370 ++ $this->stats['miss'];
371 }
372
373 if ( 'checkthedatabaseplease' === $value ) {
374 unset( $this->cache[ $key ] );
375 $value = false;
376 }
377
378 return $value;
379 }
380
381 public function get_multiple( $keys, $group = 'default', $force = false) {
382 $values = array();
383
384 foreach ( $keys as $key ) {
385 $values[ $key ] = $this->get( $key, $group, $force );
386 }
387
388 return $values;
389 }
390
391 function get_multi( $keys, $group = 'default' ) {
392 $return = array();
393 $gets = array();
394 foreach ( $keys as $i => $values ) {
395 $mc =& $this->get_mc( $group );
396 $values = (array) $values;
397 if ( empty( $values[1] ) ) {
398 $values[1] = 'default';
399 }
400
401 list( $id, $group ) = (array) $values;
402 $key = $this->key( $id, $group );
403
404 if ( isset( $this->cache[ $key ] ) ) {
405
406 if ( is_object( $this->cache[ $key ] ) ) {
407 $return[ $key ] = clone $this->cache[ $key ];
408 } else {
409 $return[ $key ] = $this->cache[ $key ];
410 }
411
412 } else if ( in_array( $group, $this->no_mc_groups ) ) {
413 $return[ $key ] = false;
414
415 } else {
416 $gets[ $key ] = $key;
417 }
418 }
419
420 if ( ! empty( $gets ) ) {
421 $results = $mc->getMulti( $gets, $null, Memcached::GET_PRESERVE_ORDER );
422 $joined = array_combine( array_keys( $gets ), array_values( $results ) );
423 $return = array_merge( $return, $joined );
424 }
425
426 ++ $this->stats['get_multi'];
427 $this->group_ops[ $group ][] = "get_multi $id";
428 $this->cache = array_merge( $this->cache, $return );
429
430 return array_values( $return );
431 }
432
433 function key( $key, $group ) {
434 if ( empty( $group ) ) {
435 $group = 'default';
436 }
437
438 if ( false !== array_search( $group, $this->global_groups ) ) {
439 $prefix = $this->global_prefix;
440 } else {
441 $prefix = $this->blog_prefix;
442 }
443
444 return preg_replace( '/\s+/', '', WP_CACHE_KEY_SALT . "$prefix$group:$key" );
445 }
446
447 function replace( $id, $data, $group = 'default', $expire = 0 ) {
448 $key = $this->key( $id, $group );
449 $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
450 $mc =& $this->get_mc( $group );
451
452 if ( is_object( $data ) ) {
453 $data = clone $data;
454 }
455
456 $result = $mc->replace( $key, $data, $expire );
457 if ( false !== $result ) {
458 $this->cache[ $key ] = $data;
459 }
460
461 return $result;
462 }
463
464 function set( $id, $data, $group = 'default', $expire = 0 ) {
465 $key = $this->key( $id, $group );
466 if ( isset( $this->cache[ $key ] ) && ( 'checkthedatabaseplease' === $this->cache[ $key ] ) ) {
467 return false;
468 }
469
470 if ( is_object( $data ) ) {
471 $data = clone $data;
472 }
473
474 $this->cache[ $key ] = $data;
475
476 if ( in_array( $group, $this->no_mc_groups ) ) {
477 return true;
478 }
479
480 $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
481 $mc =& $this->get_mc( $group );
482 $result = $mc->set( $key, $data, $expire );
483
484 return $result;
485 }
486
487 public function set_multiple( array $data, $group = '', $expire = 0 ) {
488 $values = array();
489
490 foreach ( $data as $key => $value ) {
491 $values[ $key ] = $this->set( $key, $value, $group, $expire );
492 }
493
494 return $values;
495 }
496
497 function set_multi( $items, $expire = 0, $group = 'default' ) {
498 $sets = array();
499 $mc =& $this->get_mc( $group );
500 $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
501
502 foreach ( $items as $i => $item ) {
503 if ( empty( $item[2] ) ) {
504 $item[2] = 'default';
505 }
506
507 list( $id, $data, $group ) = $item;
508
509 $key = $this->key( $id, $group );
510 if ( isset( $this->cache[ $key ] ) && ( 'checkthedatabaseplease' === $this->cache[ $key ] ) ) {
511 continue;
512 }
513
514 if ( is_object( $data ) ) {
515 $data = clone $data;
516 }
517
518 $this->cache[ $key ] = $data;
519
520 if ( in_array( $group, $this->no_mc_groups ) ) {
521 continue;
522 }
523
524 $sets[ $key ] = $data;
525 }
526
527 if ( ! empty( $sets ) ) {
528 $mc->setMulti( $sets, $expire );
529 }
530 }
531
532 function colorize_debug_line( $line ) {
533 $colors = array(
534 'get' => 'green',
535 'set' => 'purple',
536 'add' => 'blue',
537 'delete' => 'red'
538 );
539
540 $cmd = substr( $line, 0, strpos( $line, ' ' ) );
541
542 $cmd2 = "<span style='color:" . esc_attr( $colors[ $cmd ] ) . "'>" . esc_html( $cmd ) . "</span>";
543
544 return $cmd2 . esc_html( substr( $line, strlen( $cmd ) ) ) . "\n";
545 }
546
547 function stats() {
548 echo "<p>\n";
549 foreach ( $this->stats as $stat => $n ) {
550 echo "<strong>" . esc_html( $stat ) . "</strong> " . esc_html( $n );
551 echo "<br/>\n";
552 }
553 echo "</p>\n";
554 echo "<h3>Memcached:</h3>";
555 foreach ( $this->group_ops as $group => $ops ) {
556 if ( ! isset( $_GET['debug_queries'] ) && 500 < count( $ops ) ) {
557 $ops = array_slice( $ops, 0, 500 );
558 echo "<big>Too many to show! <a href='" . esc_url( add_query_arg( 'debug_queries', 'true' ) ) . "'>Show them anyway</a>.</big>\n";
559 }
560 echo "<h4>" . esc_html( $group ) . " commands</h4>";
561 echo "<pre>\n";
562 $lines = array();
563 foreach ( $ops as $op ) {
564 $lines[] = $this->colorize_debug_line( $op );
565 }
566 print_r( $lines );
567 echo "</pre>\n";
568 }
569
570 if ( ! empty( $this->debug ) && $this->debug ) {
571 var_dump( $this->memcache_debug );
572 }
573 }
574
575 function &get_mc( $group ) {
576 if ( isset( $this->mc[ $group ] ) ) {
577 return $this->mc[ $group ];
578 }
579
580 return $this->mc['default'];
581 }
582
583 function __construct() {
584
585 $this->stats = array(
586 'get' => 0,
587 'get_multi' => 0,
588 'add' => 0,
589 'set' => 0,
590 'delete' => 0,
591 'miss' => 0,
592 );
593
594 global $memcached_servers;
595
596 if ( isset( $memcached_servers ) ) {
597 $buckets = $memcached_servers;
598 } else {
599 $buckets = array( '127.0.0.1:11211' );
600 }
601
602 reset( $buckets );
603 if ( is_int( key( $buckets ) ) ) {
604 $buckets = array( 'default' => $buckets );
605 }
606
607 foreach ( $buckets as $bucket => $servers ) {
608 $this->mc[ $bucket ] = new Memcached();
609
610 $instances = array();
611 foreach ( $servers as $server ) {
612 @list( $node, $port ) = explode( ':', $server );
613 if ( empty( $port ) ) {
614 $port = ini_get( 'memcache.default_port' );
615 }
616 $port = intval( $port );
617 if ( ! $port ) {
618 $port = 11211;
619 }
620
621 $instances[] = array( $node, $port, 1 );
622 }
623 $this->mc[ $bucket ]->addServers( $instances );
624 }
625
626 global $blog_id, $table_prefix;
627 $this->global_prefix = '';
628 $this->blog_prefix = '';
629 if ( function_exists( 'is_multisite' ) ) {
630 $this->global_prefix = ( is_multisite() || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) ? '' : $table_prefix;
631 $this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix ) . ':';
632 }
633
634 $this->cache_hits =& $this->stats['get'];
635 $this->cache_misses =& $this->stats['miss'];
636 }
637 }
638 else: // No Memcached
639
640 // In 3.7+, we can handle this smoothly
641 if ( function_exists( 'wp_using_ext_object_cache' ) ) {
642 wp_using_ext_object_cache( false );
643
644 // In earlier versions, there isn't a clean bail-out method.
645 } else {
646 wp_die( 'Memcached class not available.' );
647 }
648
649 endif;
650