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 / apcu-object-cache.php

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

1,387 lines 33.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * APCu drop-in
5 *
6 * @link https://github.com/l3rady/WordPress-APCu-Object-Cache
7 * @revision 9401d4bf8b8adfdcd94e16a4d01bf81b9b88e968
8 */
9
10 // Stop direct access
11 defined( 'ABSPATH' ) or exit;
12
13 if ( ! function_exists( 'apcu_add' ) ) {
14 return;
15 }
16
17 /**
18 * Adds data to the cache, if the cache key does not already exist.
19 *
20 * @param int|string $key The cache key to use for retrieval later
21 * @param mixed $data The data to add to the cache store
22 * @param string $group The group to add the cache to
23 * @param int $expire When the cache data should be expired
24 *
25 * @return bool False if cache key and group already exist, true on success
26 */
27 function wp_cache_add($key, $data, $group = 'default', $expire = 0)
28 {
29 return WP_Object_Cache::instance()->add($key, $data, $group, $expire);
30 }
31
32
33 /**
34 * Adds multiple values to the cache in one call.
35 *
36 * @param array $data Array of keys and values to be set.
37 * @param string $group Optional. Where the cache contents are grouped. Default default.
38 * @param int $expire Optional. When to expire the cache contents, in seconds.
39 * Default 0 (no expiration).
40 * @return bool[] Array of return values, grouped by key. Each value is either
41 * true on success, or false if cache key and group already exist.
42 */
43 function wp_cache_add_multiple(array $data, $group = 'default', $expire = 0)
44 {
45 return WP_Object_Cache::instance()->add_multiple($data, $group, $expire);
46 }
47
48
49 /**
50 * Closes the cache.
51 *
52 * This function has ceased to do anything since WordPress 2.5. The
53 * functionality was removed along with the rest of the persistent cache. This
54 * does not mean that plugins can't implement this function when they need to
55 * make sure that the cache is cleaned up after WordPress no longer needs it.
56 *
57 * @return bool Always returns True
58 */
59 function wp_cache_close()
60 {
61 return true;
62 }
63
64
65 /**
66 * Decrement numeric cache item's value
67 *
68 * @param int|string $key The cache key to increment
69 * @param int $offset The amount by which to decrement the item's value. Default is 1.
70 * @param string $group The group the key is in.
71 *
72 * @return false|int False on failure, the item's new value on success.
73 */
74 function wp_cache_decr($key, $offset = 1, $group = 'default')
75 {
76 return WP_Object_Cache::instance()->decr($key, $offset, $group);
77 }
78
79
80 /**
81 * Removes the cache contents matching key and group.
82 *
83 * @param int|string $key What the contents in the cache are called
84 * @param string $group Where the cache contents are grouped
85 *
86 * @return bool True on successful removal, false on failure
87 */
88 function wp_cache_delete($key, $group = 'default')
89 {
90 return WP_Object_Cache::instance()->delete($key, $group);
91 }
92
93
94 /**
95 * Deletes multiple values from the cache in one call.
96 *
97 * @param array $keys Array of keys under which the cache to deleted.
98 * @param string $group Optional. Where the cache contents are grouped. Default default.
99 * @return bool[] Array of return values, grouped by key. Each value is either
100 * true on success, or false if the contents were not deleted.
101 */
102 function wp_cache_delete_multiple(array $keys, $group = 'default')
103 {
104 return WP_Object_Cache::instance()->delete_multiple($keys, $group);
105 }
106
107
108 /**
109 * Removes all cache items.
110 *
111 * @return bool False on failure, true on success
112 */
113 function wp_cache_flush()
114 {
115 return WP_Object_Cache::instance()->flush();
116 }
117
118
119 /**
120 * Removes all cache items from the in-memory runtime cache.
121 *
122 * @return bool True on success, false on failure.
123 */
124 function wp_cache_flush_runtime()
125 {
126 return WP_Object_Cache::instance()->flush_runtime();
127 }
128
129
130 /**
131 * Retrieves the cache contents from the cache by key and group.
132 *
133 * @param int|string $key What the contents in the cache are called
134 * @param string $group Where the cache contents are grouped
135 * @param bool $force Does nothing with APCu object cache
136 * @param bool &$found Whether key was found in the cache. Disambiguates a return of false, a storable value.
137 *
138 * @return bool|mixed False on failure to retrieve contents or the cache contents on success
139 */
140 function wp_cache_get($key, $group = 'default', $force = false, &$found = null)
141 {
142 return WP_Object_Cache::instance()->get($key, $group, $force, $found);
143 }
144
145
146 /**
147 * Retrieves multiple values from the cache in one call.
148 *
149 * @param array $keys Array of keys under which the cache contents are stored.
150 * @param string $group Optional. Where the cache contents are grouped. Default empty.
151 * @param bool $force Optional. Whether to force an update of the local cache
152 * from the persistent cache. Default false.
153 * @return array Array of return values, grouped by key. Each value is either
154 * the cache contents on success, or false on failure.
155 */
156 function wp_cache_get_multiple($keys, $group = 'default', $force = false)
157 {
158 return WP_Object_Cache::instance()->get_multiple($keys, $group, $force);
159 }
160
161
162 /**
163 * Increment numeric cache item's value
164 *
165 * @param int|string $key The cache key to increment
166 * @param int $offset The amount by which to increment the item's value. Default is 1.
167 * @param string $group The group the key is in.
168 *
169 * @return false|int False on failure, the item's new value on success.
170 */
171 function wp_cache_incr($key, $offset = 1, $group = 'default')
172 {
173 return WP_Object_Cache::instance()->incr($key, $offset, $group);
174 }
175
176
177 /**
178 * Sets up Object Cache Global and assigns it.
179 *
180 * @global WP_Object_Cache $wp_object_cache WordPress Object Cache
181 */
182 function wp_cache_init()
183 {
184 $GLOBALS['wp_object_cache'] = WP_Object_Cache::instance();
185 }
186
187
188 /**
189 * Replaces the contents of the cache with new data.
190 *
191 * @param int|string $key What to call the contents in the cache
192 * @param mixed $data The contents to store in the cache
193 * @param string $group Where to group the cache contents
194 * @param int $expire When to expire the cache contents
195 *
196 * @return bool False if not exists, true if contents were replaced
197 */
198 function wp_cache_replace($key, $data, $group = 'default', $expire = 0)
199 {
200 return WP_Object_Cache::instance()->replace($key, $data, $group, $expire);
201 }
202
203
204 /**
205 * Saves the data to the cache.
206 *
207 * @param int|string $key What to call the contents in the cache
208 * @param mixed $data The contents to store in the cache
209 * @param string $group Where to group the cache contents
210 * @param int $expire When to expire the cache contents
211 *
212 * @return bool False on failure, true on success
213 */
214 function wp_cache_set($key, $data, $group = 'default', $expire = 0)
215 {
216 return WP_Object_Cache::instance()->set($key, $data, $group, $expire);
217 }
218
219
220 /**
221 * Sets multiple values to the cache in one call.
222 *
223 * @param array $data Array of keys and values to be set.
224 * @param string $group Optional. Where the cache contents are grouped. Default default.
225 * @param int $expire Optional. When to expire the cache contents, in seconds.
226 * Default 0 (no expiration).
227 * @return bool[] Array of return values, grouped by key. Each value is either
228 * true on success, or false on failure.
229 */
230 function wp_cache_set_multiple(array $data, $group = 'default', $expire = 0)
231 {
232 return WP_Object_Cache::instance()->set_multiple($data, $group, $expire);
233 }
234
235
236 /**
237 * Switch the internal blog id.
238 *
239 * This changes the blog id used to create keys in blog specific groups.
240 *
241 * @param int $blog_id Blog ID
242 */
243 function wp_cache_switch_to_blog($blog_id)
244 {
245 WP_Object_Cache::instance()->switch_to_blog($blog_id);
246 }
247
248
249 /**
250 * Adds a group or set of groups to the list of global groups.
251 *
252 * @param string|array $groups A group or an array of groups to add
253 */
254 function wp_cache_add_global_groups($groups)
255 {
256 WP_Object_Cache::instance()->add_global_groups($groups);
257 }
258
259
260 /**
261 * Adds a group or set of groups to the list of non-persistent groups.
262 *
263 * @param string|array $groups A group or an array of groups to add
264 */
265 function wp_cache_add_non_persistent_groups($groups)
266 {
267 WP_Object_Cache::instance()->add_non_persistent_groups($groups);
268 }
269
270
271 /**
272 * Function was depreciated and now does nothing
273 *
274 * @return bool Always returns false
275 */
276 function wp_cache_reset()
277 {
278 _deprecated_function(__FUNCTION__, '3.5', 'wp_cache_switch_to_blog()');
279 return false;
280 }
281
282
283 /**
284 * Invalidate a site's object cache
285 *
286 * @param mixed $sites Sites ID's that want flushing.
287 * Don't pass a site to flush current site
288 *
289 * @return bool
290 */
291 function wp_cache_flush_site($sites = null)
292 {
293 return WP_Object_Cache::instance()->flush_sites($sites);
294 }
295
296
297 /**
298 * Invalidate a groups object cache
299 *
300 * @param mixed $groups A group or an array of groups to invalidate
301 *
302 * @return bool
303 */
304 function wp_cache_flush_group($groups = 'default')
305 {
306 return WP_Object_Cache::instance()->flush_groups($groups);
307 }
308
309
310 /**
311 * Determines whether the object cache implementation supports a particular feature.
312 *
313 * @since 6.1.0
314 *
315 * @param string $feature Name of the feature to check for. Possible values include:
316 * 'add_multiple', 'set_multiple', 'get_multiple', 'delete_multiple',
317 * 'flush_runtime', 'flush_group'.
318 * @return bool True if the feature is supported, false otherwise.
319 */
320 function wp_cache_supports( $feature ) {
321 switch ( $feature ) {
322 case 'add_multiple':
323 case 'set_multiple':
324 case 'get_multiple':
325 case 'delete_multiple':
326 case 'flush_runtime':
327 case 'flush_group':
328 return true;
329
330 default:
331 return false;
332 }
333 }
334
335
336 /**
337 * WordPress APCu Object Cache Backend
338 *
339 * The WordPress Object Cache is used to save on trips to the database. The
340 * APCu Object Cache stores all of the cache data to APCu and makes the cache
341 * contents available by using a key, which is used to name and later retrieve
342 * the cache contents.
343 */
344 class WP_Object_Cache
345 {
346
347 /**
348 * @var string MD5 hash of the current installation ABSPATH
349 */
350 private $_absPath;
351
352 /**
353 * @var bool Stores if APCu is available.
354 */
355 private $_apcuAvailable;
356
357 /**
358 * @var int The sites current blog ID. This only
359 * differs if running a multi-site installations
360 */
361 private $_blogPrefix;
362
363 /**
364 * @var int Keeps count of how many times the
365 * cache was successfully received from APCu
366 */
367 public $cache_hits = 0;
368
369 /**
370 * @var int Keeps count of how many times the
371 * cache was not successfully received from APCu
372 */
373 public $cache_misses = 0;
374
375 /**
376 * @var array Holds a list of cache groups that are
377 * shared across all sites in a multi-site installation
378 */
379 private $_globalGroups = [];
380
381 /**
382 * @var array Holds an array of versions of the retrieved groups
383 */
384 private $_groupVersions = [];
385
386 /**
387 * @var bool True if the current installation is a multi-site
388 */
389 private $_multiSite;
390
391 /**
392 * @var array Holds cache that is to be non persistent
393 */
394 private $_nonPersistentCache = [];
395
396 /**
397 * @var array Holds a list of cache groups that are not to be saved to APCu
398 */
399 private $_nonPersistentGroups = [];
400
401 /**
402 * @var array
403 */
404 private $_localCache = [];
405
406 /**
407 * @var array Holds an array of versions of the retrieved sites
408 */
409 private $_siteVersions = [];
410
411 private static $_instance;
412
413 /**
414 * Singleton. Return instance of WP_Object_Cache
415 *
416 * @return WP_Object_Cache
417 */
418 public static function instance()
419 {
420 if (self::$_instance === null) {
421 self::$_instance = new WP_Object_Cache();
422 }
423
424 return self::$_instance;
425 }
426
427 /**
428 * __clone not allowed
429 */
430 private function __clone()
431 {
432 }
433
434 /**
435 * Direct access to __construct not allowed.
436 */
437 private function __construct()
438 {
439 global $blog_id;
440
441 if (!defined('WP_APCU_KEY_SALT')) {
442 /**
443 * Set in config if you are using some sort of shared
444 * config where ABSPATH is the same on all sites
445 */
446 define('WP_APCU_KEY_SALT', 'wp');
447 }
448
449 /**
450 * define('WP_APCU_LOCAL_CACHE', false) to disable local
451 * array cache and force all cache to be returned from APCu
452 */
453 if (!defined('WP_APCU_LOCAL_CACHE')) {
454 define('WP_APCU_LOCAL_CACHE', true);
455 }
456
457 $this->_absPath = md5(ABSPATH);
458 $this->_apcuAvailable = (extension_loaded('apcu') && ini_get('apc.enabled'));
459 $this->_multiSite = is_multisite();
460 $this->_blogPrefix = $this->_multiSite ? $blog_id : 1;
461 }
462
463 public function stats()
464 {
465 echo '<p>';
466 echo "<strong>Cache Hits:</strong> {$this->cache_hits}<br />";
467 echo "<strong>Cache Misses:</strong> {$this->cache_misses}<br />";
468 echo '</p>';
469 echo '<ul>';
470
471 foreach ( $this->_localCache as $group => $cache ) {
472 echo '<li><strong>Group:</strong> ' . esc_html( $group ) . ' - ( ' . number_format( strlen( serialize( $cache ) ) / KB_IN_BYTES, 2 ) . 'k )</li>';
473 }
474 echo '</ul>';
475 }
476
477 /**
478 * Adds data to the cache, if the cache key does not already exist.
479 *
480 * @param int|string $key The cache key to use for retrieval later
481 * @param mixed $var The data to add to the cache store
482 * @param string $group The group to add the cache to
483 * @param int $ttl When the cache data should be expired
484 *
485 * @return bool False if cache key and group already exist, true on success
486 */
487 public function add($key, $var, $group = 'default', $ttl = 0)
488 {
489 if (function_exists('wp_suspend_cache_addition') && wp_suspend_cache_addition()) {
490 return false;
491 }
492
493 $key = $this->_key($key, $group);
494
495 if (!$this->_apcuAvailable || $this->_is_non_persistent_group($group)) {
496 return $this->_add_np($key, $var);
497 }
498
499 return $this->_add($key, $var, $ttl);
500 }
501
502 /**
503 * Adds multiple values to the cache in one call.
504 *
505 * @param array $data Array of keys and values to be added.
506 * @param string $group Optional. Where the cache contents are grouped. Default default.
507 * @param int $ttl Optional. When to expire the cache contents, in seconds.
508 * Default 0 (no expiration).
509 * @return bool[] Array of return values, grouped by key. Each value is either
510 * true on success, or false if cache key and group already exist.
511 */
512 public function add_multiple(array $data, $group = 'default', $ttl = 0)
513 {
514 $values = [];
515
516 foreach ($data as $key => $value) {
517 $values[$key] = $this->add($key, $value, $group, $ttl);
518 }
519
520 return $values;
521 }
522
523 /**
524 * Adds data to APCu cache, if the cache key does not already exist.
525 *
526 * @param string $key The cache key to use for retrieval later
527 * @param mixed $var The data to add to the cache store
528 * @param int $ttl When the cache data should be expired
529 *
530 * @return bool False if cache key and group already exist, true on success
531 */
532 private function _add($key, $var, $ttl)
533 {
534 if (apcu_add($key, $var, max((int)$ttl, 0))) {
535 if (WP_APCU_LOCAL_CACHE) {
536 $this->_localCache[$key] = is_object($var) ? clone $var : $var;
537 }
538 return true;
539 }
540 return false;
541 }
542
543 /**
544 * Adds data to non persistent cache, if the cache key does not already exist.
545 *
546 * @param string $key The cache key to use for retrieval later
547 * @param mixed $var The data to add to the cache store
548 *
549 * @return bool False if cache key and group already exist, true on success
550 */
551 private function _add_np($key, $var)
552 {
553 if ($this->_exists_np($key)) {
554 return false;
555 }
556
557 return $this->_set_np($key, $var);
558 }
559
560 /**
561 * Sets the list of global groups.
562 *
563 * @param string|array $groups List of groups that are global.
564 */
565 public function add_global_groups($groups)
566 {
567 foreach ((array)$groups as $group) {
568 $this->_globalGroups[$group] = true;
569 }
570 }
571
572 /**
573 * Sets the list of non persistent groups.
574 *
575 * @param string|array $groups List of groups that are non persistent.
576 */
577 public function add_non_persistent_groups($groups)
578 {
579 foreach ((array)$groups as $group) {
580 $this->_nonPersistentGroups[$group] = true;
581 }
582 }
583
584 /**
585 * Decrement numeric cache item's value
586 *
587 * @param int|string $key The cache key to increment
588 * @param int $offset The amount by which to decrement the item's value. Default is 1.
589 * @param string $group The group the key is in.
590 *
591 * @return false|int False on failure, the item's new value on success.
592 */
593 public function decr($key, $offset = 1, $group = 'default')
594 {
595 $key = $this->_key($key, $group);
596
597 if (!$this->_apcuAvailable || $this->_is_non_persistent_group($group)) {
598 return $this->_decr_np($key, $offset);
599 }
600
601 return $this->_decr($key, $offset);
602 }
603
604 /**
605 * Decrement numeric APCu cache item's value
606 *
607 * @param string $key The cache key to increment
608 * @param int $offset The amount by which to decrement the item's value. Default is 1.
609 *
610 * @return false|int False on failure, the item's new value on success.
611 */
612 private function _decr($key, $offset)
613 {
614 $this->_get($key, $success);
615 if (!$success) {
616 return false;
617 }
618
619 $value = apcu_dec($key, max((int)$offset, 0));
620 if ($value !== false && WP_APCU_LOCAL_CACHE) {
621 $this->_localCache[$key] = $value;
622 }
623 return $value;
624 }
625
626 /**
627 * Decrement numeric non persistent cache item's value
628 *
629 * @param string $key The cache key to increment
630 * @param int $offset The amount by which to decrement the item's value. Default is 1.
631 *
632 * @return false|int False on failure, the item's new value on success.
633 */
634 private function _decr_np($key, $offset)
635 {
636 if (!$this->_exists_np($key)) {
637 return false;
638 }
639
640 $offset = max((int)$offset, 0);
641 $var = $this->_get_np($key);
642 $var = is_numeric($var) ? $var : 0;
643 $var -= $offset;
644
645 return $this->_set_np($key, $var);
646 }
647
648 /**
649 * Remove the contents of the cache key in the group
650 *
651 * If the cache key does not exist in the group, then nothing will happen.
652 *
653 * @param int|string $key What the contents in the cache are called
654 * @param string $group Where the cache contents are grouped
655 * @param bool $deprecated Deprecated.
656 *
657 * @return bool False if the contents weren't deleted and true on success
658 */
659 public function delete($key, $group = 'default', $deprecated = false)
660 {
661 $key = $this->_key($key, $group);
662
663 if (!$this->_apcuAvailable || $this->_is_non_persistent_group($group)) {
664 return $this->_delete_np($key);
665 }
666
667 return $this->_delete($key);
668 }
669
670 /**
671 * Deletes multiple values from the cache in one call.
672 *
673 * @param array $keys Array of keys to be deleted.
674 * @param string $group Optional. Where the cache contents are grouped. Default default.
675 * @return bool[] Array of return values, grouped by key. Each value is either
676 * true on success, or false if the contents were not deleted.
677 */
678 public function delete_multiple(array $keys, $group = 'default')
679 {
680 $values = [];
681
682 foreach ($keys as $key) {
683 $values[$key] = $this->delete($key, $group);
684 }
685
686 return $values;
687 }
688
689 /**
690 * Remove the contents of the APCu cache key in the group
691 *
692 * If the cache key does not exist in the group, then nothing will happen.
693 *
694 * @param string $key What the contents in the cache are called
695 *
696 * @return bool False if the contents weren't deleted and true on success
697 */
698 private function _delete($key)
699 {
700 unset($this->_localCache[$key]);
701 return apcu_delete($key);
702 }
703
704 /**
705 * Remove the contents of the non persistent cache key in the group
706 *
707 * If the cache key does not exist in the group, then nothing will happen.
708 *
709 * @param string $key What the contents in the cache are called
710 *
711 * @return bool False if the contents weren't deleted and true on success
712 */
713 private function _delete_np($key)
714 {
715 if (array_key_exists($key, $this->_nonPersistentCache)) {
716 unset($this->_nonPersistentCache[$key]);
717
718 return true;
719 }
720
721 return false;
722 }
723
724 /**
725 * Checks if the cached non persistent key exists
726 *
727 * @param string $key What the contents in the cache are called
728 *
729 * @return bool True if cache key exists else false
730 */
731 private function _exists_np($key)
732 {
733 return array_key_exists($key, $this->_nonPersistentCache);
734 }
735
736 /**
737 * Clears the object cache of all data
738 *
739 * @return bool Always returns true
740 */
741 public function flush()
742 {
743 $this->_nonPersistentCache = [];
744
745 if (WP_APCU_LOCAL_CACHE) {
746 $this->_localCache = [];
747 }
748
749 if ($this->_apcuAvailable) {
750 apcu_clear_cache();
751 }
752
753 return true;
754 }
755
756 /**
757 * if using local cache clear that else flush all
758 *
759 * @return bool Always returns true
760 */
761 public function flush_runtime()
762 {
763 $this->_nonPersistentCache = [];
764
765 if (WP_APCU_LOCAL_CACHE) {
766 $this->_localCache = [];
767 } elseif ($this->_apcuAvailable) {
768 apcu_clear_cache();
769 }
770
771 return true;
772 }
773
774 /**
775 * Invalidate a groups object cache
776 *
777 * @param mixed $groups A group or an array of groups to invalidate
778 *
779 * @return bool
780 */
781 public function flush_groups($groups)
782 {
783 $groups = (array)$groups;
784
785 if (empty($groups)) {
786 return false;
787 }
788
789 foreach ($groups as $group) {
790 $version = $this->_get_group_cache_version($group);
791 $this->_set_group_cache_version($group, $version + 1);
792 }
793
794 return true;
795 }
796
797 /**
798 * Invalidate a site's object cache
799 *
800 * @param mixed $sites Sites ID's that want flushing.
801 * Don't pass a site to flush current site
802 *
803 * @return bool
804 */
805 public function flush_sites($sites)
806 {
807 $sites = (array)$sites;
808
809 if (empty($sites)) {
810 $sites = [$this->_blogPrefix];
811 }
812
813 // Add global groups (site 0) to be flushed.
814 if (!in_array(0, $sites, false)) {
815 $sites[] = 0;
816 }
817
818 foreach ($sites as $site) {
819 $version = $this->_get_site_cache_version($site);
820 $this->_set_site_cache_version($site, $version + 1);
821 }
822
823 return true;
824 }
825
826 /**
827 * Retrieves the cache contents, if it exists
828 *
829 * The contents will be first attempted to be retrieved by searching by the
830 * key in the cache key. If the cache is hit (success) then the contents
831 * are returned.
832 *
833 * On failure, the number of cache misses will be incremented.
834 *
835 * @param int|string $key What the contents in the cache are called
836 * @param string $group Where the cache contents are grouped
837 * @param bool $force Not used.
838 * @param bool &$success
839 *
840 * @return bool|mixed False on failure to retrieve contents or the cache contents on success
841 */
842 public function get($key, $group = 'default', $force = false, &$success = null)
843 {
844 $key = $this->_key($key, $group);
845
846 if (!$this->_apcuAvailable || $this->_is_non_persistent_group($group)) {
847 $var = $this->_get_np($key, $success);
848 } else {
849 $var = $this->_get($key, $success);
850 }
851
852 if ($success) {
853 $this->cache_hits++;
854 } else {
855 $this->cache_misses++;
856 }
857
858 return $var;
859 }
860
861 /**
862 * Retrieves the APCu cache contents, if it exists
863 *
864 * @param string $key What the contents in the cache are called
865 * @param bool &$success
866 *
867 * @return bool|mixed False on failure to retrieve contents or the cache contents on success
868 */
869 private function _get($key, &$success = null)
870 {
871 if (WP_APCU_LOCAL_CACHE && array_key_exists($key, $this->_localCache)
872 ) {
873 $success = true;
874 $var = $this->_localCache[$key];
875 } else {
876 $var = apcu_fetch($key, $success);
877 if ($success && WP_APCU_LOCAL_CACHE) {
878 $this->_localCache[$key] = $var;
879 }
880 }
881
882 if (is_object($var)) {
883 $var = clone $var;
884 }
885
886 return $var;
887 }
888
889 /**
890 * Retrieves the non persistent cache contents, if it exists
891 *
892 * @param string $key What the contents in the cache are called
893 * @param bool &$success
894 *
895 * @return bool|mixed False on failure to retrieve contents or the cache contents on success
896 */
897 private function _get_np($key, &$success = null)
898 {
899 if (array_key_exists($key, $this->_nonPersistentCache)) {
900 $success = true;
901 return $this->_nonPersistentCache[$key];
902 }
903
904 $success = false;
905 return false;
906 }
907
908 /**
909 * Get the cache version of a given key
910 *
911 * @param string $key
912 *
913 * @return int cache version
914 */
915 private function _get_cache_version($key)
916 {
917 if ($this->_apcuAvailable) {
918 $version = (int)apcu_fetch($key);
919 } elseif (array_key_exists($key, $this->_nonPersistentCache)) {
920 $version = (int)$this->_nonPersistentCache[$key];
921 } else {
922 $version = 0;
923 }
924
925 return $version;
926 }
927
928 /**
929 * Build cache version key
930 *
931 * @param string $type Type of key, for site or group
932 * @param mixed $value the group or site id
933 *
934 * @return string The key
935 */
936 private function _get_cache_version_key($type, $value)
937 {
938 return WP_APCU_KEY_SALT . ':' . $this->_absPath . ':' . $type . ':' . $value;
939 }
940
941 /**
942 * Get the groups cache version
943 *
944 * @param string $group The group to get version for
945 *
946 * @return int The group cache version
947 */
948 private function _get_group_cache_version($group)
949 {
950 if (!isset($this->_groupVersions[$group])) {
951 $this->_groupVersions[$group] = $this->_get_cache_version(
952 $this->_get_cache_version_key(
953 'GroupVersion',
954 $group
955 )
956 );
957 }
958
959 return $this->_groupVersions[$group];
960 }
961
962
963 /**
964 * Retrieves multiple values from the cache in one call.
965 *
966 * @param array $keys Array of keys under which the cache contents are stored.
967 * @param string $group Optional. Where the cache contents are grouped. Default 'default'.
968 * @param bool $force Optional. Whether to force an update of the local cache
969 * from the persistent cache. Default false.
970 * @return array Array of return values, grouped by key. Each value is either
971 * the cache contents on success, or false on failure.
972 */
973 public function get_multiple($keys, $group = 'default', $force = false)
974 {
975 $values = [];
976
977 foreach ($keys as $key) {
978 $values[$key] = $this->get($key, $group, $force);
979 }
980
981 return $values;
982 }
983
984
985 /**
986 * Get the sites cache version
987 *
988 * @param int $site The site to get version for
989 *
990 * @return int The site cache version
991 */
992 private function _get_site_cache_version($site)
993 {
994 if (!isset($this->_siteVersions[$site])) {
995 $this->_siteVersions[$site] = $this->_get_cache_version(
996 $this->_get_cache_version_key(
997 'SiteVersion',
998 $site
999 )
1000 );
1001 }
1002
1003 return $this->_siteVersions[$site];
1004 }
1005
1006 /**
1007 * Increment numeric cache item's value
1008 *
1009 * @param int|string $key The cache key to increment
1010 * @param int $offset The amount by which to increment the item's value. Default is 1.
1011 * @param string $group The group the key is in.
1012 *
1013 * @return false|int False on failure, the item's new value on success.
1014 */
1015 public function incr($key, $offset = 1, $group = 'default')
1016 {
1017 $key = $this->_key($key, $group);
1018
1019 if (!$this->_apcuAvailable || $this->_is_non_persistent_group($group)) {
1020 return $this->_incr_np($key, $offset);
1021 }
1022
1023 return $this->_incr($key, $offset);
1024 }
1025
1026 /**
1027 * Increment numeric APCu cache item's value
1028 *
1029 * @param string $key The cache key to increment
1030 * @param int $offset The amount by which to increment the item's value. Default is 1.
1031 *
1032 * @return false|int False on failure, the item's new value on success.
1033 */
1034 private function _incr($key, $offset)
1035 {
1036 $this->_get($key, $success);
1037 if (!$success) {
1038 return false;
1039 }
1040
1041 $value = apcu_inc($key, max((int)$offset, 0));
1042 if ($value !== false && WP_APCU_LOCAL_CACHE) {
1043 $this->_localCache[$key] = $value;
1044 }
1045 return $value;
1046 }
1047
1048 /**
1049 * Increment numeric non persistent cache item's value
1050 *
1051 * @param string $key The cache key to increment
1052 * @param int $offset The amount by which to increment the item's value. Default is 1.
1053 *
1054 * @return false|int False on failure, the item's new value on success.
1055 */
1056 private function _incr_np($key, $offset)
1057 {
1058 if (!$this->_exists_np($key)) {
1059 return false;
1060 }
1061
1062 $offset = max((int)$offset, 0);
1063 $var = $this->_get_np($key);
1064 $var = is_numeric($var) ? $var : 0;
1065 $var += $offset;
1066
1067 return $this->_set_np($key, $var);
1068 }
1069
1070 /**
1071 * Checks if the given group is a non persistent group
1072 *
1073 * @param string $group The group to be checked
1074 *
1075 * @return bool True if the group is a non persistent group else false
1076 */
1077 private function _is_non_persistent_group($group)
1078 {
1079 return isset($this->_nonPersistentGroups[$group]);
1080 }
1081
1082 /**
1083 * Works out a cache key based on a given key and group
1084 *
1085 * @param int|string $key The key
1086 * @param string $group The group
1087 *
1088 * @return string Returns the calculated cache key
1089 */
1090 private function _key($key, $group)
1091 {
1092 if (empty($group)) {
1093 $group = 'default';
1094 }
1095
1096 $prefix = 0;
1097
1098 if (!isset($this->_globalGroups[$group])) {
1099 $prefix = $this->_blogPrefix;
1100 }
1101
1102 $group_version = $this->_get_group_cache_version($group);
1103 $site_version = $this->_get_site_cache_version($prefix);
1104
1105 return WP_APCU_KEY_SALT . ':' . $this->_absPath . ':' . $prefix . ':' . $group . ':' . $key . ':v' . $site_version . '.' . $group_version;
1106 }
1107
1108 /**
1109 * Replace the contents in the cache, if contents already exist
1110 *
1111 * @param int|string $key What to call the contents in the cache
1112 * @param mixed $var The contents to store in the cache
1113 * @param string $group Where to group the cache contents
1114 * @param int $ttl When to expire the cache contents
1115 *
1116 * @return bool False if not exists, true if contents were replaced
1117 */
1118 public function replace($key, $var, $group = 'default', $ttl = 0)
1119 {
1120 $key = $this->_key($key, $group);
1121
1122 if (!$this->_apcuAvailable || $this->_is_non_persistent_group($group)) {
1123 return $this->_replace_np($key, $var);
1124 }
1125
1126 return $this->_replace($key, $var, $ttl);
1127 }
1128
1129 /**
1130 * Replace the contents in the APCu cache, if contents already exist
1131 *
1132 * @param string $key What to call the contents in the cache
1133 * @param mixed $var The contents to store in the cache
1134 * @param int $ttl When to expire the cache contents
1135 *
1136 * @return bool False if not exists, true if contents were replaced
1137 */
1138 private function _replace($key, $var, $ttl)
1139 {
1140 $this->_get($key, $success);
1141 if ($success) {
1142 return false;
1143 }
1144
1145 return $this->_set($key, $var, $ttl);
1146 }
1147
1148 /**
1149 * Replace the contents in the non persistent cache, if contents already exist
1150 *
1151 * @param string $key What to call the contents in the cache
1152 * @param mixed $var The contents to store in the cache
1153 *
1154 * @return bool False if not exists, true if contents were replaced
1155 */
1156 private function _replace_np($key, $var)
1157 {
1158 if (!$this->_exists_np($key)) {
1159 return false;
1160 }
1161
1162 return $this->_set_np($key, $var);
1163 }
1164
1165 /**
1166 * Sets the data contents into the cache
1167 *
1168 * @param int|string $key What to call the contents in the cache
1169 * @param mixed $var The contents to store in the cache
1170 * @param string $group Where to group the cache contents
1171 * @param int $ttl When the cache data should be expired
1172 *
1173 * @return bool True if cache set successfully else false
1174 */
1175 public function set($key, $var, $group = 'default', $ttl = 0)
1176 {
1177 $key = $this->_key($key, $group);
1178
1179 if (!$this->_apcuAvailable || $this->_is_non_persistent_group($group)) {
1180 return $this->_set_np($key, $var);
1181 }
1182
1183 return $this->_set($key, $var, $ttl);
1184 }
1185
1186 /**
1187 * Sets multiple values to the cache in one call.
1188 *
1189 * @param array $data Array of key and value to be set.
1190 * @param string $group Optional. Where the cache contents are grouped. Default default.
1191 * @param int $ttl Optional. When to expire the cache contents, in seconds.
1192 * Default 0 (no expiration).
1193 * @return bool[] Array of return values, grouped by key. Each value is always true.
1194 */
1195 public function set_multiple(array $data, $group = 'default', $ttl = 0)
1196 {
1197 $values = [];
1198
1199 foreach ( $data as $key => $var ) {
1200 $values[$key] = $this->set($key, $var, $group, $ttl);
1201 }
1202
1203 return $values;
1204 }
1205
1206 /**
1207 * Sets the data contents into the APCu cache
1208 *
1209 * @param string $key What to call the contents in the cache
1210 * @param mixed $var The contents to store in the cache
1211 * @param int $ttl When the cache data should be expired
1212 *
1213 * @return bool True if cache set successfully else false
1214 */
1215 private function _set($key, $var, $ttl)
1216 {
1217 if (is_object($var)) {
1218 $var = clone $var;
1219 }
1220
1221 if (apcu_store($key, $var, max((int)$ttl, 0))) {
1222 if (WP_APCU_LOCAL_CACHE) {
1223 $this->_localCache[$key] = $var;
1224 }
1225 return true;
1226 }
1227
1228 return false;
1229 }
1230
1231 /**
1232 * Sets the data contents into the non persistent cache
1233 *
1234 * @param string $key What to call the contents in the cache
1235 * @param mixed $var The contents to store in the cache
1236 *
1237 * @return bool True if cache set successfully else false
1238 */
1239 private function _set_np($key, $var)
1240 {
1241 if (is_object($var)) {
1242 $var = clone $var;
1243 }
1244
1245 return $this->_nonPersistentCache[$key] = $var;
1246 }
1247
1248 /**
1249 * Set the cache version for a given key
1250 *
1251 * @param string $key
1252 * @param int $version
1253 *
1254 * @return mixed
1255 */
1256 private function _set_cache_version($key, $version)
1257 {
1258 if ($this->_apcuAvailable) {
1259 return apcu_store($key, $version);
1260 }
1261
1262 return $this->_nonPersistentCache[$key] = $version;
1263 }
1264
1265 /**
1266 * Set the version for a groups cache
1267 *
1268 * @param string $group
1269 * @param int $version
1270 */
1271 private function _set_group_cache_version($group, $version)
1272 {
1273 $this->_set_cache_version($this->_get_cache_version_key('GroupVersion', $group), $version);
1274 }
1275
1276 /**
1277 * Set the version for a sites cache
1278 *
1279 * @param int $site
1280 * @param int $version
1281 */
1282 private function _set_site_cache_version($site, $version)
1283 {
1284 $this->_set_cache_version($this->_get_cache_version_key('SiteVersion', $site), $version);
1285 }
1286
1287 /**
1288 * Switch the internal blog id.
1289 *
1290 * This changes the blog id used to create keys in blog specific groups.
1291 *
1292 * @param int $blog_id Blog ID
1293 */
1294 public function switch_to_blog($blog_id)
1295 {
1296 $this->_blogPrefix = $this->_multiSite ? $blog_id : 1;
1297 }
1298
1299 /**
1300 * @return string
1301 */
1302 public function getAbsPath()
1303 {
1304 return $this->_absPath;
1305 }
1306
1307 /**
1308 * @return bool
1309 */
1310 public function getApcuAvailable()
1311 {
1312 return $this->_apcuAvailable;
1313 }
1314
1315 /**
1316 * @return int
1317 */
1318 public function getBlogPrefix()
1319 {
1320 return $this->_blogPrefix;
1321 }
1322
1323 /**
1324 * @return int
1325 */
1326 public function getCacheHits()
1327 {
1328 return $this->cache_hits;
1329 }
1330
1331 /**
1332 * @return int
1333 */
1334 public function getCacheMisses()
1335 {
1336 return $this->cache_misses;
1337 }
1338
1339 /**
1340 * @return array
1341 */
1342 public function getGlobalGroups()
1343 {
1344 return $this->_globalGroups;
1345 }
1346
1347 /**
1348 * @return array
1349 */
1350 public function getGroupVersions()
1351 {
1352 return $this->_groupVersions;
1353 }
1354
1355 /**
1356 * @return bool
1357 */
1358 public function getMultiSite()
1359 {
1360 return $this->_multiSite;
1361 }
1362
1363 /**
1364 * @return array
1365 */
1366 public function getNonPersistentCache()
1367 {
1368 return $this->_nonPersistentCache;
1369 }
1370
1371 /**
1372 * @return array
1373 */
1374 public function getNonPersistentGroups()
1375 {
1376 return $this->_nonPersistentGroups;
1377 }
1378
1379 /**
1380 * @return array
1381 */
1382 public function getSiteVersions()
1383 {
1384 return $this->_siteVersions;
1385 }
1386 }
1387