PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 2.6.0
ShopBuilder – WooCommerce Builder For Elementor v2.6.0
3.4.2 3.4.1 3.4.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 All 63 releases
← All changes | app/Helpers/Cache.php +40 -502 3.4.22.6.0 View file →
@@ -8,11 +8,10 @@
8 8 */
9 9
10 10 namespace RadiusTheme\SB\Helpers;
11 11
12 +use RadiusTheme\SB\Traits\SingletonTrait;
12 13 use W3TC\Dispatcher;
13 -use RadiusTheme\SB\Traits\SingletonTrait;
14 -use RadiusTheme\SB\Controllers\AssetRegistry;
15 14
16 15 // Do not allow directly accessing this file.
17 16 if ( ! defined( 'ABSPATH' ) ) {
18 17 exit( 'This script cannot be accessed directly.' );
@@ -23,330 +22,17 @@
23 22 */
24 23 class Cache {
25 24
26 25 /**
27 - * Option key holding the list of transients owned by the plugin.
28 - *
29 - * @var string
30 - */
31 - const TRANSIENT_INDEX_KEY = 'shopbuilder_transient_cached_data';
32 -
33 - /**
34 - * Lifetime of the transient index.
35 - *
36 - * Must outlive the individual transients it tracks (12 hours) so a purge can
37 - * still find them, while keeping the option out of the autoloaded set.
38 - *
39 - * @var int
40 - */
41 - const TRANSIENT_INDEX_TTL = WEEK_IN_SECONDS;
42 -
43 - /**
44 - * Transient key prefixes owned by ShopBuilder.
45 - *
46 - * A purge resolves these against wp_options directly instead of consulting a
47 - * stored list of keys. The stored list had to be read, appended to, deduped
48 - * and rewritten on every frontend cache miss, which is O(n) per write and so
49 - * O(n^2) to rebuild after an update wipes it. A prefix lookup moves that cost
50 - * off the frontend entirely and onto the purge, which runs a few times a day.
51 - *
52 - * The template_for_* families are listed individually rather than collapsed
53 - * into a single 'template_for_' prefix: that stem carries no plugin
54 - * namespace, so a broad match could delete another plugin's transients.
55 - *
56 - * @var string[]
57 - */
58 - const TRANSIENT_PREFIXES = [
59 - 'rtsb_',
60 - 'template_for_product_page_',
61 - 'template_for_archive_page_',
62 - 'template_for_tag_archive_page_',
63 - 'template_for_brand_archive_page_',
64 - ];
65 -
66 - /**
67 - * Option holding the cache group rotation state.
68 - *
69 - * A single combined option rather than separate keys, so the signature, the
70 - * group it produced and the generation counter can never drift apart: they
71 - * are always written together in one atomic update_option() call.
72 - *
73 - * Shape: [ 'sig' => '<free>|<pro>', 'group' => '<group name>', 'gen' => int ]
74 - *
75 - * Autoloaded on purpose - it is a three key array, and once group() becomes
76 - * version derived it is read on every request.
77 - *
78 - * @var string
79 - */
80 - const STATE_OPTION = 'rtsb_cache_state';
81 -
82 - /**
83 - * Group name used before the group became version derived.
84 - *
85 - * Also the prefix every derived name is built from, so a derived name can
86 - * never collide with it: derived names always carry a `_<hash>` suffix.
87 - *
88 - * @var string
89 - */
90 - const LEGACY_GROUP = 'shopbuilder';
91 -
92 - /**
93 - * Resolved group for this request.
94 - *
95 - * A property rather than a function local static so maybe_rotate_group() can
96 - * clear it after rotating, and the remainder of the request then uses the
97 - * group we moved to instead of the one primed before rotation ran.
98 - *
99 - * @var string|null
100 - */
101 - private static $group_memo = null;
102 -
103 - /**
104 - * Whether a full purge already ran during this request.
105 - *
106 - * Both the free and the pro plugin hook `upgrader_process_complete`, and a
107 - * bulk update can match both at once. Without this guard a single update
108 - * would run the whole purge (including every third-party cache plugin) more
109 - * than once in the same request.
110 - *
111 - * @var bool
112 - */
113 - private static $all_cache_cleared = false;
114 -
115 - /**
116 - * Object cache group owned by the plugin.
117 - *
118 - * Single source of truth for every wp_cache_* call in both the free and the
119 - * pro plugin, so the group can be changed in one place instead of at ninety
120 - * separate call sites.
121 - *
122 - * @return string
123 - */
124 - public static function group() {
125 - if ( null !== self::$group_memo ) {
126 - return self::$group_memo;
127 - }
128 -
129 - if ( self::is_legacy_group_pinned() ) {
130 - self::$group_memo = self::LEGACY_GROUP;
131 -
132 - return self::$group_memo;
133 - }
134 -
135 - $state = self::get_state();
136 -
137 - // The stored group is canonical. It is only derived here when no state
138 - // exists yet, which means a fresh install or a wiped option.
139 - self::$group_memo = '' !== $state['group']
140 - ? $state['group']
141 - : self::compute_group( self::state_signature(), $state['gen'] );
142 -
143 - return self::$group_memo;
144 - }
145 -
146 - /**
147 - * Whether the site has pinned the group back to the pre-derivation name.
148 - *
149 - * Escape hatch for sites that need the old behaviour restored without a
150 - * downgrade. It suspends the derived naming only. Signature tracking and the
151 - * generation counter keep running underneath, so removing the hatch moves the
152 - * site onto a group name that has never been used before.
153 - *
154 - * @return bool
155 - */
156 - private static function is_legacy_group_pinned() {
157 - return defined( 'RTSB_LEGACY_CACHE_GROUP' ) && RTSB_LEGACY_CACHE_GROUP;
158 - }
159 -
160 - /**
161 - * Derive a group name from a signature and generation.
162 - *
163 - * Deterministic on purpose: two requests racing the same rotation compute the
164 - * same name and converge, rather than each inventing a group of its own.
165 - *
166 - * @param string $signature Version signature.
167 - * @param int $generation Monotonic counter.
168 - *
169 - * @return string
170 - */
171 - private static function compute_group( $signature, $generation ) {
172 - return self::LEGACY_GROUP . '_' . substr( md5( $signature . '|' . absint( $generation ) ), 0, 8 );
173 - }
174 -
175 - /**
176 - * Signature of the currently installed plugin versions.
177 - *
178 - * Reads the constants directly rather than rtsb()->has_pro(), which depends
179 - * on function_exists( 'rtsbpro' ) and is therefore load order sensitive. Both
180 - * constants are defined while the plugin files are being included, so this is
181 - * stable from the first hook onwards.
182 - *
183 - * @return string
184 - */
185 - public static function state_signature() {
186 - return RTSB_VERSION . '|' . ( defined( 'RTSBPRO_VERSION' ) ? RTSBPRO_VERSION : '' );
187 - }
188 -
189 - /**
190 - * Read the rotation state, normalised against missing or malformed data.
191 - *
192 - * Never throws and never returns a partial shape, so a hand edited or
193 - * truncated option cannot break a request.
194 - *
195 - * @return array
196 - */
197 - public static function get_state() {
198 - $state = get_option( self::STATE_OPTION, [] );
199 -
200 - if ( ! is_array( $state ) ) {
201 - $state = [];
202 - }
203 -
204 - return [
205 - 'sig' => ( isset( $state['sig'] ) && is_string( $state['sig'] ) ) ? $state['sig'] : '',
206 - 'group' => ( isset( $state['group'] ) && is_string( $state['group'] ) && '' !== $state['group'] ) ? $state['group'] : '',
207 - 'gen' => isset( $state['gen'] ) ? absint( $state['gen'] ) : 0,
208 - ];
209 - }
210 -
211 - /**
212 - * Rotate the cache group when the installed version pair changes.
213 - *
214 - * Rotation is keyed on the version signature only, never on the group name.
215 - * Once group() becomes version derived, the generation counter feeds into the
216 - * group name, so comparing against the group name instead would change the
217 - * name on every rotation and loop forever.
218 - *
219 - * The generation counter increments on ANY signature change, in either
220 - * direction. That is what makes a downgrade safe: reverting to an older
221 - * version produces a third, previously unused group rather than reusing the
222 - * one that version had before, which could still hold stale entries.
223 - *
224 - * @return void
225 - */
226 - public static function maybe_rotate_group() {
227 - $state = self::get_state();
228 - $signature = self::state_signature();
229 -
230 - // First run, or the option was wiped or malformed. Record the current
231 - // state without touching the cache: there is no known previous group to
232 - // reclaim, and incrementing here would burn a generation for nothing.
233 - if ( '' === $state['sig'] || '' === $state['group'] ) {
234 - self::store_state( $signature, self::group(), $state['gen'] );
235 -
236 - return;
237 - }
238 -
239 - $pinned = self::is_legacy_group_pinned();
240 - $signature_changed = ( $signature !== $state['sig'] );
241 -
242 - /*
243 - * Invariant: the stored group must equal what the stored signature and
244 - * generation produce. A violation means the naming scheme changed under
245 - * us, which is exactly how the legacy group migrates to a derived one
246 - * without requiring a version bump. It also self heals a hand edited or
247 - * corrupted generation, in a single rotation.
248 - *
249 - * Suspended while pinned: a pinned group deliberately does not follow the
250 - * derivation, so the check would report a violation on every request and
251 - * rotate endlessly.
252 - */
253 - $invariant_broken = ( ! $pinned && self::compute_group( $state['sig'], $state['gen'] ) !== $state['group'] );
254 -
255 - if ( ! $signature_changed && ! $invariant_broken ) {
256 - return;
257 - }
258 -
259 - $previous = $state['group'];
260 - $generation = $state['gen'] + 1;
261 - $next = $pinned ? self::LEGACY_GROUP : self::compute_group( $signature, $generation );
262 -
263 - self::store_state( $signature, $next, $generation );
264 -
265 - // Drop any value primed before this ran so the rest of the request uses
266 - // the group we just moved to.
267 - self::$group_memo = null;
268 -
269 - // Reclaim only the group we left. Never the one now in use, which is the
270 - // case whenever the name has not actually changed.
271 - if ( $next !== $previous ) {
272 - self::flush_group_if_supported( $previous );
273 - }
274 - }
275 -
276 - /**
277 - * Persist the rotation state.
278 - *
279 - * @param string $signature Version signature.
280 - * @param string $group Group name in use for that signature.
281 - * @param int $generation Monotonic counter, never reused.
282 - *
283 - * @return void
284 - */
285 - private static function store_state( $signature, $group, $generation ) {
286 - update_option(
287 - self::STATE_OPTION,
288 - [
289 - 'sig' => (string) $signature,
290 - 'group' => (string) $group,
291 - 'gen' => absint( $generation ),
292 - ],
293 - true
294 - );
295 - }
296 -
297 - /**
298 - * Flush a single object cache group, only where the drop-in supports it.
299 - *
300 - * Deliberately has no fallback. wp_cache_flush() would empty every group on
301 - * the site, including WooCommerce sessions and every other plugin's data,
302 - * which is the exact behaviour this work exists to remove. When group
303 - * flushing is unavailable the abandoned entries are simply left to expire.
304 - *
305 - * @param string $group Group to flush.
306 - *
307 - * @return bool Whether the flush was performed.
308 - */
309 - public static function flush_group_if_supported( $group ) {
310 - if ( ! is_string( $group ) || '' === $group ) {
311 - return false;
312 - }
313 -
314 - if ( ! function_exists( 'wp_cache_supports' ) || ! function_exists( 'wp_cache_flush_group' ) ) {
315 - return false;
316 - }
317 -
318 - if ( ! wp_cache_supports( 'flush_group' ) ) {
319 - return false;
320 - }
321 -
322 - wp_cache_flush_group( $group );
323 -
324 - return true;
325 - }
326 -
327 - /**
328 26 * Clear the template cache.
329 27 *
330 28 * @since 4.3.0
331 29 */
332 30 public static function clear_all_cache() {
333 - if ( self::$all_cache_cleared ) {
334 - return;
335 - }
336 -
337 - self::$all_cache_cleared = true;
338 -
339 31 self::clear_data_cache();
340 32 self::clear_template_cache();
33 + self::clear_plugins_cache();
341 34 self::clear_transient_cache();
342 - self::clear_asset_cache();
343 -
344 - // Purged last on purpose. This is what empties the external page caches,
345 - // so it must not run until ShopBuilder's own transients and asset bundles
346 - // have been rebuilt - otherwise live traffic arrives while the plugin is
347 - // still mid-teardown and has to render against half-cleared state.
348 - self::clear_plugins_cache();
349 35 }
350 36 /**
351 37 * Clear the template cache.
352 38 *
@@ -367,11 +53,8 @@
367 53 }
368 54 if ( method_exists( 'LiteSpeed_Cache_API', 'purge_all' ) ) {
369 55 \LiteSpeed_Cache_API::purge_all();
370 56 }
371 - if ( class_exists( '\LiteSpeed\Purge' ) ) {
372 - \LiteSpeed\Purge::purge_all();
373 - }
374 57 if ( class_exists( 'Endurance_Page_Cache' ) ) {
375 58 $epc = new \Endurance_Page_Cache();
376 59 $epc->purge_all();
377 60 }
@@ -443,15 +126,16 @@
443 126 do_action( 'ce_clear_cache' );
444 127 }
445 128 // When plugins have a simple method, add them to the array ('Plugin Name' => 'method_name').
446 129 $others = [
447 - 'WP Fastest Cache' => 'wpfc_clear_all_cache',
448 - 'Cachify' => 'cachify_flush_cache',
449 - 'Comet Cache' => [ 'comet_cache', 'clear' ],
450 - 'SG Optimizer' => 'sg_cachepress_purge_cache',
451 - 'Pantheon' => 'pantheon_wp_clear_edge_all',
452 - 'Zen Cache' => [ 'zencache', 'clear' ],
453 - 'Breeze' => [ 'Breeze_PurgeCache', 'breeze_cache_flush' ],
130 + 'WP Fastest Cache' => 'wpfc_clear_all_cache',
131 + 'Cachify' => 'cachify_flush_cache',
132 + 'Comet Cache' => [ 'comet_cache', 'clear' ],
133 + 'SG Optimizer' => 'sg_cachepress_purge_cache',
134 + 'Pantheon' => 'pantheon_wp_clear_edge_all',
135 + 'Zen Cache' => [ 'zencache', 'clear' ],
136 + 'Breeze' => [ 'Breeze_PurgeCache', 'breeze_cache_flush' ],
137 + 'Swift Performance' => [ 'Swift_Performance_Cache', 'clear_all_cache' ],
454 138 ];
455 139 foreach ( $others as $plugin => $method ) {
456 140 if ( is_callable( $method ) ) {
457 141 call_user_func( $method );
@@ -470,17 +154,16 @@
470 154 * Purge GoDaddy Managed WordPress Hosting (Varnish)
471 155 *
472 156 * Source: https://github.com/wp-media/wp-rocket/blob/master/inc/3rd-party/hosting/godaddy.php
473 157 *
474 - * @param string $method The request method.
475 - * @param string $url The request URL.
476 - *
477 - * @return void
158 + * @param String $method
159 + * @param String|Null $url
478 160 */
479 161 public static function godaddy_request( $method, $url = null ) {
480 162 $url = empty( $url ) ? home_url() : $url;
481 163 $host = wp_parse_url( $url, PHP_URL_HOST );
482 164 $url = set_url_scheme( str_replace( $host, \WPaas\Plugin::vip(), $url ), 'http' );
165 + wp_cache_flush();
483 166 update_option( 'gd_system_last_cache_flush', time() ); // purge apc.
484 167 wp_remote_request(
485 168 esc_url_raw( $url ),
486 169 [
@@ -498,18 +181,16 @@
498 181 * @param string $cache_key Object cache key.
499 182 * @param string $template Located template.
500 183 */
501 184 public static function set_template_cache( $cache_key, $template ) {
502 - wp_cache_set( $cache_key, $template, self::group(), 12 * HOUR_IN_SECONDS );
503 - $cached_templates = wp_cache_get( 'shopbuilder_cached_templates', self::group() );
185 + wp_cache_set( $cache_key, $template, 'shopbuilder' );
186 + $cached_templates = wp_cache_get( 'shopbuilder_cached_templates', 'shopbuilder' );
504 187 if ( is_array( $cached_templates ) ) {
505 188 $cached_templates[] = $cache_key;
506 189 } else {
507 190 $cached_templates = [ $cache_key ];
508 191 }
509 - // The index must outlive the entries it tracks so a purge can still find
510 - // them, hence the longer lifetime than the templates themselves.
511 - wp_cache_set( 'shopbuilder_cached_templates', $cached_templates, self::group(), WEEK_IN_SECONDS );
192 + wp_cache_set( 'shopbuilder_cached_templates', $cached_templates, 'shopbuilder' );
512 193 }
513 194 /**
514 195 * Clear the template cache.
515 196 *
@@ -515,14 +196,14 @@
515 196 *
516 197 * @since 4.3.0
517 198 */
518 199 public static function clear_template_cache() {
519 - $cached_templates = wp_cache_get( 'shopbuilder_cached_templates', self::group() );
200 + $cached_templates = wp_cache_get( 'shopbuilder_cached_templates', 'shopbuilder' );
520 201 if ( is_array( $cached_templates ) ) {
521 202 foreach ( $cached_templates as $cache_key ) {
522 - wp_cache_delete( $cache_key, self::group() );
203 + wp_cache_delete( $cache_key, 'shopbuilder' );
523 204 }
524 - wp_cache_delete( 'shopbuilder_cached_templates', self::group() );
205 + wp_cache_delete( 'shopbuilder_cached_templates', 'shopbuilder' );
525 206 }
526 207 }
527 208 /**
528 209 * Added data cache.
@@ -528,21 +209,18 @@
528 209 * Added data cache.
529 210 *
530 211 * @since 4.3.0
531 212 * @param string $cache_key Object cache key.
532 - *
533 - * @return void
213 + * @param string $data Located template.
534 214 */
535 215 public static function set_data_cache_key( $cache_key ) {
536 - $cached_data = wp_cache_get( 'shopbuilder_cached_data', self::group() );
216 + $cached_data = wp_cache_get( 'shopbuilder_cached_data', 'shopbuilder' );
537 217 if ( is_array( $cached_data ) ) {
538 218 $cached_data[] = $cache_key;
539 219 } else {
540 220 $cached_data = [ $cache_key ];
541 221 }
542 - // The index must outlive the entries it tracks so a purge can still find
543 - // them, hence the longer lifetime than the cached data itself.
544 - wp_cache_set( 'shopbuilder_cached_data', $cached_data, self::group(), WEEK_IN_SECONDS );
222 + wp_cache_set( 'shopbuilder_cached_data', $cached_data, 'shopbuilder' );
545 223 }
546 224 /**
547 225 * Clear data cache.
548 226 *
@@ -548,186 +226,46 @@
548 226 *
549 227 * @since 4.3.0
550 228 */
551 229 public static function clear_data_cache() {
552 - $cached_templates = wp_cache_get( 'shopbuilder_cached_data', self::group() );
230 + $cached_templates = wp_cache_get( 'shopbuilder_cached_data', 'shopbuilder' );
553 231 if ( is_array( $cached_templates ) ) {
554 232 foreach ( $cached_templates as $cache_key ) {
555 - wp_cache_delete( $cache_key, self::group() );
233 + wp_cache_delete( $cache_key, 'shopbuilder' );
556 234 }
557 - wp_cache_delete( 'shopbuilder_cached_data', self::group() );
235 + wp_cache_delete( 'shopbuilder_cached_data', 'shopbuilder' );
558 236 }
559 237 }
560 238
561 - /**
562 - * Clear asset cache.
563 - *
564 - * @return void
565 - */
566 - public static function clear_asset_cache() {
567 - if ( ! Fns::is_optimization_enabled() ) {
568 - return;
569 - }
570 239
571 - $upload_dir = wp_upload_dir();
572 - $asset_dir = trailingslashit( $upload_dir['basedir'] ) . 'shopbuilder_uploads/cache/';
573 -
574 - // Delete old assets.
575 - if ( is_dir( $asset_dir ) ) {
576 - self::delete_dir_contents( $asset_dir );
577 - }
578 -
579 - // Re-generate assets.
580 - AssetRegistry::instance()->regenerate_bundles();
581 - }
582 -
583 240 /**
584 - * Recursively delete directory contents.
241 + * Added data cache.
585 242 *
586 - * @param string $dir Directory path.
587 - * @return void
588 - */
589 - public static function delete_dir_contents( $dir ) {
590 - global $wp_filesystem;
591 -
592 - if ( ! function_exists( 'WP_Filesystem' ) ) {
593 - require_once ABSPATH . 'wp-admin/includes/file.php';
594 - }
595 -
596 - if ( ! $wp_filesystem ) {
597 - WP_Filesystem();
598 - }
599 -
600 - if ( ! $wp_filesystem->is_dir( $dir ) ) {
601 - return;
602 - }
603 -
604 - $contents = $wp_filesystem->dirlist( $dir );
605 -
606 - if ( ! is_array( $contents ) ) {
607 - return;
608 - }
609 -
610 - foreach ( $contents as $item ) {
611 - $path = trailingslashit( $dir ) . $item['name'];
612 -
613 - if ( 'f' === $item['type'] ) {
614 - $wp_filesystem->delete( $path, false );
615 - } elseif ( 'd' === $item['type'] ) {
616 - self::delete_dir_contents( $path );
617 -
618 - $wp_filesystem->delete( $path, true );
619 - }
620 - }
621 - }
622 -
623 -
624 - /**
625 - * Register a transient key with the purge index.
626 - *
627 - * @deprecated No longer required. Purges now resolve the plugin's transients
628 - * from wp_options by key prefix, see TRANSIENT_PREFIXES.
629 - *
630 - * Maintaining the index meant reading, appending to, deduplicating and
631 - * rewriting the whole list of keys on every frontend cache miss. That is
632 - * O(n) per write, so rebuilding it after an update wiped it cost O(n^2):
633 - * measured at 13.8 seconds of CPU for a 10,000 product catalogue. It was
634 - * also lossy under concurrency, because the read-modify-write was unguarded.
635 - *
636 - * Kept as a no-op so third-party callers do not fatal.
637 - *
638 243 * @since 4.3.0
639 244 * @param string $cache_key Transient cache key.
640 245 */
641 246 public static function set_transient_cache_key( $cache_key ) {
642 - // Intentionally empty. Retained so third-party code calling this does not
643 - // fatal; ShopBuilder itself no longer calls it.
644 - unset( $cache_key );
247 + $cached_data = get_transient( 'shopbuilder_transient_cached_data' );
248 + if ( is_array( $cached_data ) ) {
249 + $cached_data[] = $cache_key;
250 + } else {
251 + $cached_data = [ $cache_key ];
252 + }
253 +
254 + set_transient( 'shopbuilder_transient_cached_data', array_unique( $cached_data ) );
645 255 }
646 256
647 257 /**
648 - * Clear every transient owned by the plugin.
258 + * Clear data cache.
649 259 *
650 260 * @since 4.3.0
651 - *
652 - * @return int Number of transients deleted.
653 261 */
654 262 public static function clear_transient_cache() {
655 - $deleted = self::delete_transients_by_prefix( self::TRANSIENT_PREFIXES );
656 -
657 - /*
658 - * Nothing writes the index any more, but installations upgrading from an
659 - * earlier version still hold one, and it can be megabytes on a large
660 - * catalogue. Its key predates the rtsb_ namespace so no prefix matches
661 - * it; remove it explicitly. Harmless when already absent.
662 - */
663 - delete_transient( self::TRANSIENT_INDEX_KEY );
664 -
665 - return $deleted;
666 - }
667 -
668 - /**
669 - * Delete every transient whose key starts with one of the given prefixes.
670 - *
671 - * Runs one indexed lookup against wp_options per prefix and deletes through
672 - * delete_transient(), so the timeout row is removed with its value and any
673 - * persistent object cache stays consistent.
674 - *
675 - * Only ever called from a purge. It must not be reachable from a normal
676 - * frontend request.
677 - *
678 - * @param string[] $prefixes Transient key prefixes, without the _transient_ part.
679 - *
680 - * @return int Number of transients deleted.
681 - */
682 - public static function delete_transients_by_prefix( array $prefixes ) {
683 - global $wpdb;
684 -
685 - $deleted = 0;
686 -
687 - foreach ( $prefixes as $prefix ) {
688 - if ( '' === $prefix ) {
689 - continue;
263 + $cached_templates = get_transient( 'shopbuilder_transient_cached_data' );
264 + if ( is_array( $cached_templates ) ) {
265 + foreach ( $cached_templates as $cache_key ) {
266 + delete_transient( $cache_key );
690 267 }
691 -
692 - /*
693 - * esc_like() is applied to the whole literal, including the
694 - * _transient_ part: the underscore is a single character wildcard in
695 - * LIKE, so an unescaped '_transient_rtsb_' would also match keys such
696 - * as 'Xtransient_rtsbY'.
697 - */
698 - $like = $wpdb->esc_like( '_transient_' . $prefix ) . '%';
699 -
700 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
701 - $option_names = $wpdb->get_col(
702 - $wpdb->prepare(
703 - "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
704 - $like
705 - )
706 - );
707 -
708 - foreach ( $option_names as $option_name ) {
709 - // Trim only the leading marker; str_replace() would also corrupt
710 - // a key that happens to contain '_transient_' further along.
711 - $key = substr( $option_name, strlen( '_transient_' ) );
712 -
713 - if ( '' === $key ) {
714 - continue;
715 - }
716 -
717 - delete_transient( $key );
718 - ++$deleted;
719 - }
268 + delete_transient( 'shopbuilder_transient_cached_data' );
720 269 }
721 -
722 - return $deleted;
723 - }
724 -
725 - /**
726 - * Clear all theme css handle transients.
727 - *
728 - * @return void
729 - */
730 - public static function delete_all_theme_css_handle_transients() {
731 - self::delete_transients_by_prefix( [ 'rtsb_theme_css_handle_' ] );
732 270 }
733 271 }