PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 1.2.8
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v1.2.8
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 / memcache-object-cache.php

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

496 lines 12.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined( 'ABSPATH' ) || exit;
3
4 // We need Memcache to continue
5 if ( class_exists( 'Memcache' ) ):
6
7 /**
8 * Upstream: 3.0.2
9 * @author: Ryan Boren, Denis de Bernardy, Matt Martz, Andy Skelton
10 * @link https://wordpress.org/plugins/memcached
11 */
12
13 // Users with setups where multiple installs share a common wp-config.php or $table_prefix
14 // can use this to guarantee uniqueness for the keys generated by this object cache
15 if ( ! defined( 'WP_CACHE_KEY_SALT' ) ) {
16 define( 'WP_CACHE_KEY_SALT', DB_NAME );
17 }
18
19 function wp_cache_add( $key, $data, $group = '', $expire = 0 ) {
20 global $wp_object_cache;
21
22 return $wp_object_cache->add( $key, $data, $group, $expire );
23 }
24
25 function wp_cache_incr( $key, $n = 1, $group = '' ) {
26 global $wp_object_cache;
27
28 return $wp_object_cache->incr( $key, $n, $group );
29 }
30
31 function wp_cache_decr( $key, $n = 1, $group = '' ) {
32 global $wp_object_cache;
33
34 return $wp_object_cache->decr( $key, $n, $group );
35 }
36
37 function wp_cache_close() {
38 global $wp_object_cache;
39
40 return $wp_object_cache->close();
41 }
42
43 function wp_cache_delete( $key, $group = '' ) {
44 global $wp_object_cache;
45
46 return $wp_object_cache->delete( $key, $group );
47 }
48
49 function wp_cache_flush() {
50 global $wp_object_cache;
51
52 return $wp_object_cache->flush();
53 }
54
55 function wp_cache_get( $key, $group = '', $force = false ) {
56 global $wp_object_cache;
57
58 return $wp_object_cache->get( $key, $group, $force );
59 }
60
61 function wp_cache_init() {
62 global $wp_object_cache;
63
64 $wp_object_cache = new WP_Object_Cache();
65 }
66
67 function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) {
68 global $wp_object_cache;
69
70 return $wp_object_cache->replace( $key, $data, $group, $expire );
71 }
72
73 function wp_cache_set( $key, $data, $group = '', $expire = 0 ) {
74 global $wp_object_cache;
75
76 if ( defined( 'WP_INSTALLING' ) == false ) {
77 return $wp_object_cache->set( $key, $data, $group, $expire );
78 } else {
79 return $wp_object_cache->delete( $key, $group );
80 }
81 }
82
83 function wp_cache_switch_to_blog( $blog_id ) {
84 global $wp_object_cache;
85
86 return $wp_object_cache->switch_to_blog( $blog_id );
87 }
88
89 function wp_cache_add_global_groups( $groups ) {
90 global $wp_object_cache;
91
92 $wp_object_cache->add_global_groups( $groups );
93 }
94
95 function wp_cache_add_non_persistent_groups( $groups ) {
96 global $wp_object_cache;
97
98 $wp_object_cache->add_non_persistent_groups( $groups );
99 }
100
101 class WP_Object_Cache {
102 var $global_groups = array( 'WP_Object_Cache_global' );
103
104 var $no_mc_groups = array();
105
106 var $cache = array();
107 var $mc = array();
108 var $stats = array();
109 var $group_ops = array();
110 var $flush_number = array();
111 var $global_flush_number = null;
112
113 var $cache_enabled = true;
114 var $default_expiration = 0;
115
116 function add( $id, $data, $group = 'default', $expire = 0 ) {
117 $key = $this->key( $id, $group );
118
119 if ( is_object( $data ) ) {
120 $data = clone $data;
121 }
122
123 if ( in_array( $group, $this->no_mc_groups ) ) {
124 $this->cache[ $key ] = $data;
125
126 return true;
127 } elseif ( isset( $this->cache[ $key ] ) && $this->cache[ $key ] !== false ) {
128 return false;
129 }
130
131 $mc =& $this->get_mc( $group );
132 $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
133 $result = $mc->add( $key, $data, false, $expire );
134
135 if ( false !== $result ) {
136 @ ++ $this->stats['add'];
137 $this->group_ops[ $group ][] = "add $id";
138 $this->cache[ $key ] = $data;
139 }
140
141 return $result;
142 }
143
144 function add_global_groups( $groups ) {
145 if ( ! is_array( $groups ) ) {
146 $groups = (array) $groups;
147 }
148
149 $this->global_groups = array_merge( $this->global_groups, $groups );
150 $this->global_groups = array_unique( $this->global_groups );
151 }
152
153 function add_non_persistent_groups( $groups ) {
154 if ( ! is_array( $groups ) ) {
155 $groups = (array) $groups;
156 }
157
158 $this->no_mc_groups = array_merge( $this->no_mc_groups, $groups );
159 $this->no_mc_groups = array_unique( $this->no_mc_groups );
160 }
161
162 function incr( $id, $n = 1, $group = 'default' ) {
163 $key = $this->key( $id, $group );
164 $mc =& $this->get_mc( $group );
165 $this->cache[ $key ] = $mc->increment( $key, $n );
166
167 return $this->cache[ $key ];
168 }
169
170 function decr( $id, $n = 1, $group = 'default' ) {
171 $key = $this->key( $id, $group );
172 $mc =& $this->get_mc( $group );
173 $this->cache[ $key ] = $mc->decrement( $key, $n );
174
175 return $this->cache[ $key ];
176 }
177
178 function close() {
179
180 foreach ( $this->mc as $bucket => $mc ) {
181 $mc->close();
182 }
183 }
184
185 function delete( $id, $group = 'default' ) {
186 $key = $this->key( $id, $group );
187
188 if ( in_array( $group, $this->no_mc_groups ) ) {
189 unset( $this->cache[ $key ] );
190
191 return true;
192 }
193
194 $mc =& $this->get_mc( $group );
195
196 $result = $mc->delete( $key );
197
198 @ ++ $this->stats['delete'];
199 $this->group_ops[ $group ][] = "delete $id";
200
201 if ( false !== $result ) {
202 unset( $this->cache[ $key ] );
203 }
204
205 return $result;
206 }
207
208 function flush() {
209 // Do not use the memcached flush method. It acts on an
210 // entire memcached server, affecting all sites.
211 // Flush is also unusable in some setups, e.g. twemproxy.
212 // Instead, rotate the key prefix for the current site.
213 // Global keys are rotated when flushing on the main site.
214 $this->cache = array();
215 $this->rotate_site_keys();
216 if ( is_main_site() ) {
217 $this->rotate_global_keys();
218 }
219 }
220
221 function rotate_site_keys() {
222 $this->add( 'flush_number', intval( microtime( true ) * 1e6 ), 'WP_Object_Cache' );
223 $this->flush_number[ $this->blog_prefix ] = $this->incr( 'flush_number', 1, 'WP_Object_Cache' );
224 }
225
226 function rotate_global_keys() {
227 $this->add( 'flush_number', intval( microtime( true ) * 1e6 ), 'WP_Object_Cache_global' );
228 $this->global_flush_number = $this->incr( 'flush_number', 1, 'WP_Object_Cache_global' );
229 }
230
231 function get( $id, $group = 'default', $force = false ) {
232 $key = $this->key( $id, $group );
233 $mc =& $this->get_mc( $group );
234
235 if ( isset( $this->cache[ $key ] ) && ( ! $force || in_array( $group, $this->no_mc_groups ) ) ) {
236 if ( is_object( $this->cache[ $key ] ) ) {
237 $value = clone $this->cache[ $key ];
238 } else {
239 $value = $this->cache[ $key ];
240 }
241 } else if ( in_array( $group, $this->no_mc_groups ) ) {
242 $this->cache[ $key ] = $value = false;
243 } else {
244 $value = $mc->get( $key );
245 if ( null === $value ) {
246 $value = false;
247 }
248 $this->cache[ $key ] = $value;
249 }
250
251 @ ++ $this->stats['get'];
252 $this->group_ops[ $group ][] = "get $id";
253
254 if ( 'checkthedatabaseplease' === $value ) {
255 unset( $this->cache[ $key ] );
256 $value = false;
257 }
258
259 return $value;
260 }
261
262 function get_multi( $groups ) {
263 /*
264 format: $get['group-name'] = array( 'key1', 'key2' );
265 */
266 $return = array();
267 foreach ( $groups as $group => $ids ) {
268 $mc =& $this->get_mc( $group );
269 foreach ( $ids as $id ) {
270 $key = $this->key( $id, $group );
271 if ( isset( $this->cache[ $key ] ) ) {
272 if ( is_object( $this->cache[ $key ] ) ) {
273 $return[ $key ] = clone $this->cache[ $key ];
274 } else {
275 $return[ $key ] = $this->cache[ $key ];
276 }
277 continue;
278 } else if ( in_array( $group, $this->no_mc_groups ) ) {
279 $return[ $key ] = false;
280 continue;
281 } else {
282 $return[ $key ] = $mc->get( $key );
283 }
284 }
285 if ( $to_get ) {
286 $vals = $mc->get_multi( $to_get );
287 $return = array_merge( $return, $vals );
288 }
289 }
290 @ ++ $this->stats['get_multi'];
291 $this->group_ops[ $group ][] = "get_multi $id";
292 $this->cache = array_merge( $this->cache, $return );
293
294 return $return;
295 }
296
297 function flush_prefix( $group ) {
298 if ( $group === 'WP_Object_Cache' || $group === 'WP_Object_Cache_global' ) {
299 // Never flush the flush numbers.
300 $number = '_';
301 } elseif ( false !== array_search( $group, $this->global_groups ) ) {
302 if ( ! isset( $this->global_flush_number ) ) {
303 $this->global_flush_number = intval( $this->get( 'flush_number', 'WP_Object_Cache_global' ) );
304 }
305 if ( $this->global_flush_number === 0 ) {
306 $this->rotate_global_keys();
307 }
308 $number = $this->global_flush_number;
309 } else {
310 if ( ! isset( $this->flush_number[ $this->blog_prefix ] ) ) {
311 $this->flush_number[ $this->blog_prefix ] = intval( $this->get( 'flush_number', 'WP_Object_Cache' ) );
312 }
313 if ( $this->flush_number[ $this->blog_prefix ] === 0 ) {
314 $this->rotate_site_keys();
315 }
316 $number = $this->flush_number[ $this->blog_prefix ];
317 }
318
319 return $number . ':';
320 }
321
322 function key( $key, $group ) {
323 if ( empty( $group ) ) {
324 $group = 'default';
325 }
326
327 $prefix = $this->key_salt;
328
329 $prefix .= $this->flush_prefix( $group );
330
331 if ( false !== array_search( $group, $this->global_groups ) ) {
332 $prefix .= $this->global_prefix;
333 } else {
334 $prefix .= $this->blog_prefix;
335 }
336
337 return preg_replace( '/\s+/', '', "$prefix:$group:$key" );
338 }
339
340 function replace( $id, $data, $group = 'default', $expire = 0 ) {
341 $key = $this->key( $id, $group );
342 $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
343 $mc =& $this->get_mc( $group );
344
345 if ( is_object( $data ) ) {
346 $data = clone $data;
347 }
348
349 $result = $mc->replace( $key, $data, false, $expire );
350 if ( false !== $result ) {
351 $this->cache[ $key ] = $data;
352 }
353
354 return $result;
355 }
356
357 function set( $id, $data, $group = 'default', $expire = 0 ) {
358 $key = $this->key( $id, $group );
359 if ( isset( $this->cache[ $key ] ) && ( 'checkthedatabaseplease' === $this->cache[ $key ] ) ) {
360 return false;
361 }
362
363 if ( is_object( $data ) ) {
364 $data = clone $data;
365 }
366
367 $this->cache[ $key ] = $data;
368
369 if ( in_array( $group, $this->no_mc_groups ) ) {
370 return true;
371 }
372
373 $expire = ( $expire == 0 ) ? $this->default_expiration : $expire;
374 $mc =& $this->get_mc( $group );
375 $result = $mc->set( $key, $data, false, $expire );
376
377 return $result;
378 }
379
380 function switch_to_blog( $blog_id ) {
381 global $table_prefix;
382 $blog_id = (int) $blog_id;
383 $this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix );
384 }
385
386 function colorize_debug_line( $line ) {
387 $colors = array(
388 'get' => 'green',
389 'set' => 'purple',
390 'add' => 'blue',
391 'delete' => 'red'
392 );
393
394 $cmd = substr( $line, 0, strpos( $line, ' ' ) );
395
396 $cmd2 = "<span style='color:" . esc_attr( $colors[ $cmd ] ) . "'>" . esc_html( $cmd ) . "</span>";
397
398 return $cmd2 . esc_html( substr( $line, strlen( $cmd ) ) ) . "\n";
399 }
400
401 function stats() {
402 echo "<p>\n";
403 foreach ( $this->stats as $stat => $n ) {
404 echo "<strong>" . esc_html( $stat ) . "</strong> " . esc_html( $n );
405 echo "<br/>\n";
406 }
407 echo "</p>\n";
408 echo "<h3>Memcached:</h3>";
409 foreach ( $this->group_ops as $group => $ops ) {
410 if ( ! isset( $_GET['debug_queries'] ) && 500 < count( $ops ) ) {
411 $ops = array_slice( $ops, 0, 500 );
412 echo "<big>Too many to show! <a href='" . esc_url( add_query_arg( 'debug_queries', 'true' ) ) . "'>Show them anyway</a>.</big>\n";
413 }
414 echo "<h4>" . esc_html( $group ) . " commands</h4>";
415 echo "<pre>\n";
416 $lines = array();
417 foreach ( $ops as $op ) {
418 $lines[] = $this->colorize_debug_line( $op );
419 }
420 print_r( $lines );
421 echo "</pre>\n";
422 }
423 }
424
425 function &get_mc( $group ) {
426 if ( isset( $this->mc[ $group ] ) ) {
427 return $this->mc[ $group ];
428 }
429
430 return $this->mc['default'];
431 }
432
433 function failure_callback( $host, $port ) {
434 //error_log("Connection failure for $host:$port\n", 3, '/tmp/memcached.txt');
435 }
436
437 function salt_keys( $key_salt ) {
438 if ( strlen( $key_salt ) ) {
439 $this->key_salt = $key_salt . ':';
440 } else {
441 $this->key_salt = '';
442 }
443 }
444
445 function __construct() {
446 global $memcached_servers;
447
448 if ( isset( $memcached_servers ) ) {
449 $buckets = $memcached_servers;
450 } else {
451 $buckets = array( '127.0.0.1:11211' );
452 }
453
454 reset( $buckets );
455 if ( is_int( key( $buckets ) ) ) {
456 $buckets = array( 'default' => $buckets );
457 }
458
459 foreach ( $buckets as $bucket => $servers ) {
460 $this->mc[ $bucket ] = new Memcache();
461 foreach ( $servers as $server ) {
462 if ( 'unix://' == substr( $server, 0, 7 ) ) {
463 $node = $server;
464 $port = 0;
465 } else {
466 list ( $node, $port ) = explode( ':', $server );
467 if ( ! $port ) {
468 $port = ini_get( 'memcache.default_port' );
469 }
470 $port = intval( $port );
471 if ( ! $port ) {
472 $port = 11211;
473 }
474 }
475 $this->mc[ $bucket ]->addServer( $node, $port, true, 1, 1, 15, true, array( $this, 'failure_callback' ) );
476 $this->mc[ $bucket ]->setCompressThreshold( 20000, 0.2 );
477 }
478 }
479
480 global $blog_id, $table_prefix;
481 $this->global_prefix = '';
482 $this->blog_prefix = '';
483 if ( function_exists( 'is_multisite' ) ) {
484 $this->global_prefix = ( is_multisite() || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) ? '' : $table_prefix;
485 $this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix );
486 }
487
488 $this->salt_keys( WP_CACHE_KEY_SALT );
489
490 $this->cache_hits =& $this->stats['get'];
491 $this->cache_misses =& $this->stats['add'];
492 }
493 }
494
495 endif;
496