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 +203 -42 1.0.3trunk 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,29 +69,94 @@
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 - public static function get_object_cache($key, $post_id = false, $meta_name = false, $cache_expire = false) {
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 = ($post_id == false ? '' : $post_id) . $meta_name . '_' . $key;
135 + $data = self::get_cached_value($cache_key, $found);
136 + if ($found) {
137 + return $data;
138 + }
71 139
72 - if ($cache_enabled) {
73 - $data = wp_cache_get($cache_key, Schema::getConstant('CACHE_GROUP'));
74 - if ($data !== false) return $data;
140 + if ($is_user_meta) {
141 + $meta_data = get_user_meta($post_id, $meta_name, true);
142 + } else {
143 + $meta_data = $post_id == false ? get_option($meta_name, []) : get_post_meta($post_id, $meta_name, true);
75 144 }
145 +
146 + if( !Utils::is_empty($key) && is_array($meta_data) && array_key_exists($key, $meta_data) ) {
147 + // If key exists in meta data, return it
148 + $data = $meta_data[$key];
149 + } elseif (is_array($meta_data) && Utils::is_empty($key)) {
150 + // If no key is provided, return the entire meta data
151 + $data = $meta_data;
152 + } else {
153 + // If key does not exist, return false
154 + $data = false;
155 + }
76 156
77 - $meta_data = $post_id == false ? get_option($meta_name, []) : get_post_meta($post_id, $meta_name, true);
78 - $data = isset($meta_data[$key]) ? $meta_data[$key] : false;
157 + self::sync_object_cache($key, $data, $post_id, $meta_name, $cache_expire, $is_user_meta);
79 158
80 - if ($data && $cache_enabled) {
81 - wp_cache_set($cache_key, $data, Schema::getConstant('CACHE_GROUP'), $cache_expire);
82 - }
83 -
84 159 return $data;
85 160 }
86 161
87 162 /**
@@ -86,35 +161,54 @@
86 161
87 162 /**
88 163 * Set Object Cache
89 164 */
90 - public static function set_object_cache($key, $data, $post_id = false, $meta_name = false, $cache_expire = false) {
165 + public static function set_object_cache($key, $data, $post_id = false, $meta_name = false, $cache_expire = false, $is_user_meta = false) {
91 166 $meta_name = $meta_name == false ? Schema::getConstant('META_KEY') : $meta_name;
92 167 $cache_expire = $cache_expire == false ? Schema::getConstant('CACHE_EXPIRE') : $cache_expire;
93 168
94 - $meta_data = $post_id == false ? get_option($meta_name, []) : get_post_meta($post_id, $meta_name, true);
169 + if( $is_user_meta) {
170 + $meta_data = get_user_meta($post_id, $meta_name, true);
171 + } else {
172 + $meta_data = $post_id == false ? get_option($meta_name, []) : get_post_meta($post_id, $meta_name, true);
173 + }
95 174
96 - if (is_array($meta_data)) {
97 - if(isset($meta_data[$key])){
98 - if($meta_data[$key] == $data) {
99 - return true;
100 - }
101 - }
175 + // If data is not an array, convert it to an array
176 + if (!is_array($meta_data)) {
177 + $meta_data = [];
178 + }
179 +
180 + if(Utils::is_empty($key)) {
181 + // If key is empty, set the entire meta data
182 + $meta_data = $data;
183 + $cache_value = $data;
184 + } else {
102 185 $meta_data[$key] = $data;
186 + $cache_value = $data;
187 + }
188 +
189 + // Update the meta data
190 + if ($is_user_meta) {
191 + $update_result = update_user_meta($post_id, $meta_name, $meta_data);
103 192 } else {
104 - $meta_data = [$key => $data];
193 + $update_result = $post_id == false ? update_option($meta_name, $meta_data) : update_post_meta($post_id, $meta_name, $meta_data);
105 194 }
106 195
107 - $update_result = $post_id == false ? update_option($meta_name, $meta_data) : update_post_meta($post_id, $meta_name, $meta_data);
108 -
109 - // Set cache only if data update was successful
110 - if (wp_using_ext_object_cache() && $update_result) {
111 - $cache_key = ($post_id == false ? '' : $post_id) . $meta_name . '_' . $key;
112 - wp_cache_set($cache_key, $data, Schema::getConstant('CACHE_GROUP'), Schema::getConstant('CACHE_EXPIRE'));
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;
113 201 }
114 202
115 - // Return success if data update was successful
116 - return $update_result;
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);
208 + }
209 +
210 + return maybe_serialize($stored) === maybe_serialize($meta_data);
117 211 }
118 212
119 213
120 214
@@ -120,19 +214,26 @@
120 214
121 215 /**
122 216 * Delete object cache
123 217 */
124 - public static function delete_object_cache($key = false, $post_id = false, $meta_name = false) {
218 + public static function delete_object_cache($key = false, $post_id = false, $meta_name = false, $is_user_meta = false) {
125 219 $meta_name = $meta_name == false ? Schema::getConstant('META_KEY') : $meta_name;
126 220 $update_result = false;
127 - $meta_data = $post_id == false ? get_option($meta_name, []) : get_post_meta($post_id, $meta_name, true);
128 -
129 - if (is_array($meta_data)) {
130 - if($key == false) {
131 - $update_result = $post_id == false ? delete_option($meta_name) : delete_post_meta($post_id, $meta_name);
132 - } else if (array_key_exists($key, $meta_data)) {
221 + if ($is_user_meta) {
222 + $meta_data = get_user_meta($post_id, $meta_name, true);
223 + if (is_array($meta_data) && array_key_exists($key, $meta_data)) {
133 224 unset($meta_data[$key]);
134 - if(empty($meta_data)) {
225 + if (empty($meta_data)) {
226 + $update_result = delete_user_meta($post_id, $meta_name);
227 + } else {
228 + $update_result = update_user_meta($post_id, $meta_name, $meta_data);
229 + }
230 + }
231 + } else {
232 + $meta_data = $post_id == false ? get_option($meta_name, []) : get_post_meta($post_id, $meta_name, true);
233 + if (is_array($meta_data) && array_key_exists($key, $meta_data)) {
234 + unset($meta_data[$key]);
235 + if (empty($meta_data)) {
135 236 $update_result = $post_id == false ? delete_option($meta_name) : delete_post_meta($post_id, $meta_name);
136 237 } else {
137 238 $update_result = $post_id == false ? update_option($meta_name, $meta_data) : update_post_meta($post_id, $meta_name, $meta_data);
138 239 }
@@ -138,18 +239,78 @@
138 239 }
139 240 }
140 241 }
141 242
142 - // Delete from object cache if caching is enabled
143 - if (wp_using_ext_object_cache() && $update_result && $key) {
144 - $cache_key = ($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);
145 245 wp_cache_delete($cache_key, Schema::getConstant('CACHE_GROUP'));
146 246 }
147 247
148 - // Return success if data delete was successful
149 248 return $update_result;
150 249 }
151 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 +
307 +
308 + /**
309 + * Gets the instance of this class.
310 + *
311 + * @return Cache
312 + */
152 313 public static function instance(){
153 314 if (is_null(self::$instance)) {
154 315 self::$instance = new self();
155 316 }
@@ -154,5 +315,5 @@
154 315 self::$instance = new self();
155 316 }
156 317 return self::$instance;
157 318 }
158 -}
319 +}