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
media-cloud-sync / includes / base / cache.php

cache.php in Media Cloud Sync trunk, at includes/base/cache.php

320 lines 10.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Dudlewebs\WPMCS;
3
4 defined('ABSPATH') || exit;
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 */
16 class Cache {
17 private static $instance = null;
18 private $assets_url;
19 private $version;
20 private $token;
21
22
23 private static $cached_data=[];
24
25 /**
26 * Admin constructor.
27 * @since 1.0.0
28 */
29 public function __construct() {
30 $this->assets_url = WPMCS_ASSETS_URL;
31 $this->version = WPMCS_VERSION;
32 $this->token = WPMCS_TOKEN;
33
34 // Initialize setup
35 $this->init();
36 }
37
38 /**
39 * Initialize datas
40 */
41 public function init() {
42 }
43
44
45 /**
46 * Update Static Cache
47 */
48 public static function update_item_cache( $item_id, $data ){
49 self::$cached_data[$item_id] = $data;
50 return true;
51 }
52
53 /**
54 * Update Static Cache
55 */
56 public static function get_item_cache( $item_id, $default = false ){
57 return isset(self::$cached_data[$item_id]) ? self::$cached_data[$item_id] : $default;
58 }
59
60 /**
61 * Delete Static Cache
62 */
63 public static function delete_item_cache( $item_id = false ){
64 if($item_id === false) {
65 self::$cached_data = [];
66 } else {
67 unset(self::$cached_data[$item_id]);
68 }
69 return true;
70 }
71
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 /**
127 * Get Object Cache
128 */
129 public static function get_object_cache($key = '', $post_id = false, $meta_name = false, $cache_expire = false, $is_user_meta = false) {
130 $meta_name = $meta_name == false ? Schema::getConstant('META_KEY') : $meta_name;
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;
134
135 $data = self::get_cached_value($cache_key, $found);
136 if ($found) {
137 return $data;
138 }
139
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);
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 }
156
157 self::sync_object_cache($key, $data, $post_id, $meta_name, $cache_expire, $is_user_meta);
158
159 return $data;
160 }
161
162 /**
163 * Set Object Cache
164 */
165 public static function set_object_cache($key, $data, $post_id = false, $meta_name = false, $cache_expire = false, $is_user_meta = false) {
166 $meta_name = $meta_name == false ? Schema::getConstant('META_KEY') : $meta_name;
167 $cache_expire = $cache_expire == false ? Schema::getConstant('CACHE_EXPIRE') : $cache_expire;
168
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 }
174
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 {
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);
192 } else {
193 $update_result = $post_id == false ? update_option($meta_name, $meta_data) : update_post_meta($post_id, $meta_name, $meta_data);
194 }
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;
201 }
202
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);
211 }
212
213
214
215 /**
216 * Delete object cache
217 */
218 public static function delete_object_cache($key = false, $post_id = false, $meta_name = false, $is_user_meta = false) {
219 $meta_name = $meta_name == false ? Schema::getConstant('META_KEY') : $meta_name;
220 $update_result = false;
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)) {
224 unset($meta_data[$key]);
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)) {
236 $update_result = $post_id == false ? delete_option($meta_name) : delete_post_meta($post_id, $meta_name);
237 } else {
238 $update_result = $post_id == false ? update_option($meta_name, $meta_data) : update_post_meta($post_id, $meta_name, $meta_data);
239 }
240 }
241 }
242
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);
245 wp_cache_delete($cache_key, Schema::getConstant('CACHE_GROUP'));
246 }
247
248 return $update_result;
249 }
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 */
313 public static function instance(){
314 if (is_null(self::$instance)) {
315 self::$instance = new self();
316 }
317 return self::$instance;
318 }
319 }
320