PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.2.0
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.2.0
1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.2.0 All 28 releases
xspeed / includes / class-render-caches.php

class-render-caches.php in xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN 1.2.0, at includes/class-render-caches.php

117 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Render_Caches — invalidate caches of RENDERED output owned by OTHER
4 * plugins.
5 *
6 * Why this exists: not every asset-URL rewrite happens on the finished page.
7 * The CDN module hooks `wp_get_attachment_url`, which fires *during* element
8 * render, and core derives every sized image URL from it (`image_downsize()`
9 * calls it and swaps the basename for the intermediate size). So the CDN host
10 * is already in the markup by the time a page builder captures that markup
11 * into a cache of its own.
12 *
13 * Elementor does exactly that, in two places:
14 *
15 * - `_elementor_element_cache` post meta — the buffered output of a whole
16 * document, written on any ordinary front-end render, 24 h TTL by
17 * default (element caching is ON out of the box: the gate is
18 * `'disable' !== get_option( 'elementor_element_cache_ttl', '' )` and the
19 * option is unset on a stock install).
20 * - `uploads/elementor/css/post-<id>.css` — background-image `url()` values
21 * built from the same attachment URLs. **No TTL at all**; regenerated
22 * only on post save, "Regenerate Files & Data", or an Elementor version
23 * bump.
24 *
25 * `Cache::purge_all()` sweeps only trees xSpeed owns, so neither is reached.
26 * The observable bug: disabling the CDN keeps emitting the old host for a day
27 * (indefinitely from the CSS files), and *enabling* it leaves the heaviest
28 * images — including the LCP hero — on the origin for the same window, which
29 * reads as "the CDN does nothing".
30 *
31 * Deliberately NOT wired to every purge. A post-publish purge fires
32 * constantly and blowing away every generated CSS file each time would be a
33 * real regression; Elementor already invalidates its own copy for the post
34 * being saved. This runs only where asset URLs themselves can have changed —
35 * see Cache::purge_render_caches() for the call sites.
36 *
37 * Detection is by class/constant only, never `is_plugin_active()` on a path
38 * string: a renamed plugin folder must not silently turn the integration off.
39 *
40 * @package XSpeed
41 */
42
43 declare(strict_types=1);
44
45 namespace XSpeed;
46
47 defined( 'ABSPATH' ) || exit;
48
49 final class Render_Caches {
50
51 /**
52 * Register the built-in integrations.
53 *
54 * Everything hangs off the public `xspeed_purge_third_party_render_caches`
55 * filter, so Pro — or a site owner with one builder we don't ship support
56 * for — adds a listener rather than patching this class.
57 */
58 public static function boot(): void {
59 add_filter( 'xspeed_purge_third_party_render_caches', array( __CLASS__, 'purge_elementor' ), 10, 2 );
60 }
61
62 /**
63 * Elementor: generated CSS files, `_elementor_css`,
64 * `_elementor_element_cache` and `_elementor_page_assets`.
65 *
66 * `Files_Manager::clear_cache()` clears all four in one pass and is what
67 * Elementor's own *Regenerate Files & Data* tool runs — public since
68 * Elementor 1.2.0. Everything regenerates lazily on the next front-end
69 * render of each page (measured on a 128 KB-CSS Elementor homepage:
70 * ~1.2 s for that first render against ~0.3 s warm, then back to normal).
71 *
72 * @param string[] $cleared Labels of caches cleared so far.
73 * @param string $cause Who asked, threaded through for the log.
74 * @return string[]
75 */
76 public static function purge_elementor( $cleared, $cause = 'manual' ): array {
77 $cleared = is_array( $cleared ) ? $cleared : array();
78 unset( $cause );
79
80 $files_manager = self::elementor_files_manager();
81 if ( null === $files_manager ) {
82 return $cleared;
83 }
84
85 $files_manager->clear_cache();
86 $cleared[] = 'Elementor';
87
88 return $cleared;
89 }
90
91 /**
92 * Elementor's files manager, or null when Elementor is absent or has not
93 * finished booting.
94 *
95 * Every step is guarded rather than assumed: this can run from a REST
96 * settings write, from WP-CLI and from admin-post, and `$instance` is
97 * populated late enough that "class exists" alone is not evidence the
98 * manager is there.
99 *
100 * @return object|null
101 */
102 private static function elementor_files_manager() {
103 if ( ! class_exists( '\\Elementor\\Plugin' ) ) {
104 return null;
105 }
106 $instance = \Elementor\Plugin::$instance;
107 if ( ! is_object( $instance ) || ! isset( $instance->files_manager ) ) {
108 return null;
109 }
110 $files_manager = $instance->files_manager;
111 if ( ! is_object( $files_manager ) || ! method_exists( $files_manager, 'clear_cache' ) ) {
112 return null;
113 }
114 return $files_manager;
115 }
116 }
117