PluginProbe
Media Cloud Sync / trunk
Media Cloud Sync vtrunk
1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 All 34 releases
← All changes | includes/base/cache.php +144 -33 1.2.13trunk View file →
@@ -2,8 +2,18 @@
2 2 namespace Dudlewebs\WPMCS;
3 3
4 4 defined('ABSPATH') || exit;
5 5
6 +/**
7 + * Plugin object cache wrapper.
8 + *
9 + * Supports all common WordPress environments on PHP 8.1+ and WordPress 5.9+:
10 + * 1. Default WordPress object cache (in-memory, per request — no Redis/Memcached plugin).
11 + * 2. Persistent object cache drop-ins (Redis Object Cache, Memcached, etc.).
12 + *
13 + * Reads and writes always go through wp_cache_* so behaviour is consistent;
14 + * flush strategy differs based on whether a persistent drop-in is active.
15 + */
6 16 class Cache {
7 17 private static $instance = null;
8 18 private $assets_url;
9 19 private $version;
@@ -59,20 +69,73 @@
59 69 return true;
60 70 }
61 71
62 72 /**
73 + * Whether a persistent object cache drop-in is active (Redis, Memcached, etc.).
74 + */
75 + private static function uses_persistent_object_cache() {
76 + return function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache();
77 + }
78 +
79 + /**
80 + * Build a consistent object cache key for get/set/delete.
81 + */
82 + private static function build_cache_key($key, $post_id, $meta_name, $is_user_meta = false) {
83 + return ($is_user_meta ? 'user_' : '')
84 + . ($post_id == false ? '' : $post_id . '_')
85 + . $meta_name
86 + . (!Utils::is_empty($key) ? '_' . $key : '');
87 + }
88 +
89 + /**
90 + * Whether the active object cache supports a feature (WP 6.1+ or drop-in).
91 + */
92 + private static function cache_supports($feature) {
93 + return function_exists('wp_cache_supports') && wp_cache_supports($feature);
94 + }
95 +
96 + /**
97 + * Read from object cache with WP 5.5+ $found support.
98 + */
99 + private static function get_cached_value($cache_key, &$found) {
100 + $group = Schema::getConstant('CACHE_GROUP');
101 + $found = false;
102 +
103 + if (!function_exists('wp_cache_get')) {
104 + return false;
105 + }
106 +
107 + // wp_cache_get( ..., &$found ) is available from WordPress 5.5 (plugin requires 5.9).
108 + return wp_cache_get($cache_key, $group, false, $found);
109 + }
110 +
111 + /**
112 + * Write a value to the plugin object cache group.
113 + */
114 + private static function sync_object_cache($key, $data, $post_id, $meta_name, $cache_expire, $is_user_meta = false) {
115 + if (!function_exists('wp_cache_set') || !function_exists('wp_cache_delete')) {
116 + return;
117 + }
118 +
119 + $group = Schema::getConstant('CACHE_GROUP');
120 + $cache_key = self::build_cache_key($key, $post_id, $meta_name, $is_user_meta);
121 +
122 + wp_cache_delete($cache_key, $group);
123 + wp_cache_set($cache_key, $data, $group, $cache_expire);
124 + }
125 +
126 + /**
63 127 * Get Object Cache
64 128 */
65 129 public static function get_object_cache($key = '', $post_id = false, $meta_name = false, $cache_expire = false, $is_user_meta = false) {
66 130 $meta_name = $meta_name == false ? Schema::getConstant('META_KEY') : $meta_name;
67 131 $cache_expire = $cache_expire == false ? Schema::getConstant('CACHE_EXPIRE') : $cache_expire;
132 + $cache_key = self::build_cache_key($key, $post_id, $meta_name, $is_user_meta);
133 + $found = false;
68 134
69 - $cache_enabled = wp_using_ext_object_cache();
70 - $cache_key = ($is_user_meta ? 'user_' : '').($post_id == false ? '' : $post_id) . $meta_name . !empty($key) ? '_'.$key : '';
71 -
72 - if ($cache_enabled) {
73 - $data = wp_cache_get($cache_key, Schema::getConstant('CACHE_GROUP'));
74 - if ($data !== false) return $data;
135 + $data = self::get_cached_value($cache_key, $found);
136 + if ($found) {
137 + return $data;
75 138 }
76 139
77 140 if ($is_user_meta) {
78 141 $meta_data = get_user_meta($post_id, $meta_name, true);
@@ -90,11 +153,9 @@
90 153 // If key does not exist, return false
91 154 $data = false;
92 155 }
93 156
94 - if ($cache_enabled) {
95 - wp_cache_set($cache_key, $data, Schema::getConstant('CACHE_GROUP'), $cache_expire);
96 - }
157 + self::sync_object_cache($key, $data, $post_id, $meta_name, $cache_expire, $is_user_meta);
97 158
98 159 return $data;
99 160 }
100 161
@@ -118,16 +179,12 @@
118 179
119 180 if(Utils::is_empty($key)) {
120 181 // If key is empty, set the entire meta data
121 182 $meta_data = $data;
122 - } elseif (is_array($meta_data) && array_key_exists($key, $meta_data)) {
123 - if($meta_data[$key] == $data) {
124 - return true;
125 - }
126 - // If key exists, update the value
183 + $cache_value = $data;
184 + } else {
127 185 $meta_data[$key] = $data;
128 - } else {
129 - $meta_data[$key] = $data;
186 + $cache_value = $data;
130 187 }
131 188
132 189 // Update the meta data
133 190 if ($is_user_meta) {
@@ -134,24 +191,24 @@
134 191 $update_result = update_user_meta($post_id, $meta_name, $meta_data);
135 192 } else {
136 193 $update_result = $post_id == false ? update_option($meta_name, $meta_data) : update_post_meta($post_id, $meta_name, $meta_data);
137 194 }
138 - // If update failed, return false
139 - if (!$update_result) {
140 - return false;
195 +
196 + // Always sync object cache so Redis and default WP cache stay current.
197 + self::sync_object_cache($key, $cache_value, $post_id, $meta_name, $cache_expire, $is_user_meta);
198 +
199 + if ($update_result) {
200 + return true;
141 201 }
142 202
143 - // If object cache is enabled, set the cache
144 - // and delete the old cache entry if it exists
145 - if (wp_using_ext_object_cache()) {
146 - wp_cache_delete($meta_name, Schema::getConstant('CACHE_GROUP'));
147 - $cache_key = ($is_user_meta ? 'user_' : '') . ($post_id == false ? '' : $post_id) . $meta_name . '_' . $key;
148 - wp_cache_set($cache_key, $data, Schema::getConstant('CACHE_GROUP'), $cache_expire);
203 + // update_option/update_meta return false when the value is unchanged — treat as success once cache is synced.
204 + if ($is_user_meta) {
205 + $stored = get_user_meta($post_id, $meta_name, true);
206 + } else {
207 + $stored = $post_id == false ? get_option($meta_name, []) : get_post_meta($post_id, $meta_name, true);
149 208 }
150 209
151 -
152 - // Return success if data update was successful
153 - return $update_result;
210 + return maybe_serialize($stored) === maybe_serialize($meta_data);
154 211 }
155 212
156 213
157 214
@@ -182,18 +239,72 @@
182 239 }
183 240 }
184 241 }
185 242
186 - // Delete from object cache if caching is enabled
187 - if (wp_using_ext_object_cache() && $update_result && $key) {
188 - $cache_key = ($is_user_meta ? 'user_' : '') . ($post_id == false ? '' : $post_id) . $meta_name . '_' . $key;
243 + if ($update_result && !Utils::is_empty($key) && function_exists('wp_cache_delete')) {
244 + $cache_key = self::build_cache_key($key, $post_id, $meta_name, $is_user_meta);
189 245 wp_cache_delete($cache_key, Schema::getConstant('CACHE_GROUP'));
190 246 }
191 247
192 - // Return success if data delete was successful
193 248 return $update_result;
194 249 }
195 250
251 +
252 + /**
253 + * Flush Cache
254 + * @since 1.3.2
255 + */
256 + public static function flush_object_cache() {
257 + $group = Schema::getConstant('CACHE_GROUP');
258 +
259 + if (self::uses_persistent_object_cache()) {
260 + // Redis / Memcached drop-in: flush only this plugin's cache group.
261 + self::flush_cache_group($group);
262 + } else {
263 + // Default WordPress in-memory cache: safe to flush the runtime cache.
264 + self::flush_runtime_cache();
265 + }
266 +
267 + self::$cached_data = [];
268 + return true;
269 + }
270 +
271 + /**
272 + * Flush the in-memory runtime cache (default WordPress, no drop-in plugin).
273 + */
274 + private static function flush_runtime_cache() {
275 + if (self::cache_supports('flush_runtime') && function_exists('wp_cache_flush_runtime')) {
276 + wp_cache_flush_runtime();
277 + return;
278 + }
279 +
280 + if (function_exists('wp_cache_flush')) {
281 + wp_cache_flush();
282 + }
283 + }
284 +
285 + /**
286 + * Flush all keys in the plugin cache group when the drop-in supports it.
287 + */
288 + private static function flush_cache_group($group) {
289 + if (!function_exists('wp_cache_flush_group')) {
290 + return false;
291 + }
292 +
293 + // Prefer the supported API so core never falls back to a full cache flush.
294 + if (self::cache_supports('flush_group')) {
295 + return (bool) wp_cache_flush_group($group);
296 + }
297 +
298 + // Persistent drop-ins (e.g. Redis Object Cache on WP 5.9) may provide flush_group
299 + // without wp_cache_supports() being available in core.
300 + if (self::uses_persistent_object_cache()) {
301 + return (bool) wp_cache_flush_group($group);
302 + }
303 +
304 + return false;
305 + }
306 +
196 307
197 308 /**
198 309 * Gets the instance of this class.
199 310 *
@@ -204,5 +315,5 @@
204 315 self::$instance = new self();
205 316 }
206 317 return self::$instance;
207 318 }
208 -}
319 +}