PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.4
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.4
1.3.4 1.3.3 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 All 30 releases
← All changes | includes/class-conflict-registry.php +43 -204 1.0.61.3.4 View file →
@@ -2,9 +2,11 @@
2 2 /**
3 3 * Conflict_Registry — the strategy matrix for coexisting (or refusing to
4 4 * coexist) with other caching / optimization plugins.
5 5 *
6 - * Source of truth: IMPLEMENTATION.md §6.2. Each entry says:
6 + * Source of truth: Cache_Plugin_Catalog (which plugins exist, what they do,
7 + * how they conflict) projected through IMPLEMENTATION.md §6.2's contract.
8 + * Each entry says:
7 9 * - which plugin we're conflicting with (its main file path),
8 10 * - which "kind" of plugin it is (page-cache / minify / lazy-load / etc.),
9 11 * - per-feature strategy (refuse | warn | allow),
10 12 * - a human-readable reason rendered in UI + REST errors.
@@ -41,218 +43,51 @@
41 43 */
42 44 private static $cache = null;
43 45
44 46 /**
45 - * Static strategy matrix. Keys are plugin file paths as they appear in
46 - * the `active_plugins` option. Values declare:
47 - * - label : human-readable plugin name
48 - * - kind : page-cache | minify | lazy-load | image-opt | cdn | preload | mixed
49 - * - strategy : map of feature_key → strategy (refuse | warn | allow)
47 + * Per-feature strategy matrix, projected from Cache_Plugin_Catalog.
50 48 *
49 + * The catalog is the single description of every plugin we know about;
50 + * this projection keeps the shape older callers expect — plugin file =>
51 + * label / kind / strategy. `kind` is derived from the catalog's
52 + * capabilities (see kind_for()) rather than stored twice.
53 + *
51 54 * Feature keys are namespaced `<module-slug>.<sub-feature>` or just
52 - * `<module-slug>` for whole-module conflicts. Modules can extend this
53 - * matrix via their conflicts() method.
55 + * `<module-slug>` for whole-module conflicts. Modules extend the matrix
56 + * through the `xspeed_conflict_matrix` filter, and a Pro module can
57 + * describe a whole new plugin through `xspeed_cache_plugin_catalog`.
54 58 */
55 59 private static function matrix(): array {
56 - return array(
57 - 'wp-rocket/wp-rocket.php' => array(
58 - 'label' => 'WP Rocket',
59 - 'kind' => 'mixed',
60 - 'strategy' => array(
61 - 'cache.page' => self::STRATEGY_REFUSE,
62 - 'minify.html' => self::STRATEGY_REFUSE,
63 - 'minify.css' => self::STRATEGY_REFUSE,
64 - 'minify.js' => self::STRATEGY_REFUSE,
65 - 'preload.crawler' => self::STRATEGY_REFUSE,
66 - 'cdn.rewrite' => self::STRATEGY_REFUSE,
67 - 'images.lazyload' => self::STRATEGY_REFUSE,
68 - ),
69 - ),
70 - 'litespeed-cache/litespeed-cache.php' => array(
71 - 'label' => 'LiteSpeed Cache',
72 - 'kind' => 'mixed',
73 - 'strategy' => array(
74 - 'cache.page' => self::STRATEGY_REFUSE,
75 - 'minify.html' => self::STRATEGY_REFUSE,
76 - 'minify.css' => self::STRATEGY_REFUSE,
77 - 'minify.js' => self::STRATEGY_REFUSE,
78 - 'preload.crawler' => self::STRATEGY_REFUSE,
79 - 'cdn.rewrite' => self::STRATEGY_WARN,
80 - 'images.lazyload' => self::STRATEGY_REFUSE,
81 - ),
82 - ),
83 - 'w3-total-cache/w3-total-cache.php' => array(
84 - 'label' => 'W3 Total Cache',
85 - 'kind' => 'mixed',
86 - 'strategy' => array(
87 - 'cache.page' => self::STRATEGY_REFUSE,
88 - 'minify.html' => self::STRATEGY_REFUSE,
89 - 'minify.css' => self::STRATEGY_REFUSE,
90 - 'minify.js' => self::STRATEGY_REFUSE,
91 - 'preload.crawler' => self::STRATEGY_REFUSE,
92 - 'cdn.rewrite' => self::STRATEGY_WARN,
93 - ),
94 - ),
95 - 'wp-super-cache/wp-cache.php' => array(
96 - 'label' => 'WP Super Cache',
97 - 'kind' => 'page-cache',
98 - 'strategy' => array(
99 - 'cache.page' => self::STRATEGY_REFUSE,
100 - 'preload.crawler' => self::STRATEGY_REFUSE,
101 - ),
102 - ),
103 - 'wp-fastest-cache/wpFastestCache.php' => array(
104 - 'label' => 'WP Fastest Cache',
105 - 'kind' => 'mixed',
106 - 'strategy' => array(
107 - 'cache.page' => self::STRATEGY_REFUSE,
108 - 'minify.html' => self::STRATEGY_REFUSE,
109 - 'minify.css' => self::STRATEGY_REFUSE,
110 - 'minify.js' => self::STRATEGY_REFUSE,
111 - 'preload.crawler' => self::STRATEGY_REFUSE,
112 - 'images.lazyload' => self::STRATEGY_REFUSE,
113 - ),
114 - ),
115 - 'cache-enabler/cache-enabler.php' => array(
116 - 'label' => 'Cache Enabler',
117 - 'kind' => 'page-cache',
118 - 'strategy' => array(
119 - 'cache.page' => self::STRATEGY_REFUSE,
120 - 'preload.crawler' => self::STRATEGY_REFUSE,
121 - ),
122 - ),
123 - 'comet-cache/comet-cache.php' => array(
124 - 'label' => 'Comet Cache',
125 - 'kind' => 'page-cache',
126 - 'strategy' => array(
127 - 'cache.page' => self::STRATEGY_REFUSE,
128 - ),
129 - ),
130 - 'hummingbird-performance/wp-hummingbird.php' => array(
131 - 'label' => 'Hummingbird',
132 - 'kind' => 'mixed',
133 - 'strategy' => array(
134 - 'cache.page' => self::STRATEGY_REFUSE,
135 - 'minify.html' => self::STRATEGY_REFUSE,
136 - 'minify.css' => self::STRATEGY_REFUSE,
137 - 'minify.js' => self::STRATEGY_REFUSE,
138 - 'images.lazyload' => self::STRATEGY_REFUSE,
139 - 'preload.crawler' => self::STRATEGY_WARN,
140 - 'cdn.rewrite' => self::STRATEGY_WARN,
141 - ),
142 - ),
143 - 'sg-cachepress/sg-cachepress.php' => array(
144 - 'label' => 'SG Optimizer',
145 - 'kind' => 'mixed',
146 - 'strategy' => array(
147 - 'cache.page' => self::STRATEGY_REFUSE,
148 - 'minify.html' => self::STRATEGY_REFUSE,
149 - 'minify.css' => self::STRATEGY_REFUSE,
150 - 'minify.js' => self::STRATEGY_REFUSE,
151 - 'preload.crawler' => self::STRATEGY_REFUSE,
152 - 'images.lazyload' => self::STRATEGY_WARN,
153 - ),
154 - ),
155 - 'breeze/breeze.php' => array(
156 - 'label' => 'Breeze',
157 - 'kind' => 'mixed',
158 - 'strategy' => array(
159 - 'cache.page' => self::STRATEGY_REFUSE,
160 - 'minify.html' => self::STRATEGY_REFUSE,
161 - 'minify.css' => self::STRATEGY_REFUSE,
162 - 'minify.js' => self::STRATEGY_REFUSE,
163 - 'preload.crawler' => self::STRATEGY_REFUSE,
164 - 'cdn.rewrite' => self::STRATEGY_WARN,
165 - ),
166 - ),
167 - 'autoptimize/autoptimize.php' => array(
168 - 'label' => 'Autoptimize',
169 - 'kind' => 'minify',
170 - 'strategy' => array(
171 - 'minify.html' => self::STRATEGY_REFUSE,
172 - 'minify.css' => self::STRATEGY_REFUSE,
173 - 'minify.js' => self::STRATEGY_REFUSE,
174 - ),
175 - ),
176 - 'flying-press/flying-press.php' => array(
177 - 'label' => 'FlyingPress',
178 - 'kind' => 'mixed',
179 - 'strategy' => array(
180 - 'cache.page' => self::STRATEGY_REFUSE,
181 - 'minify.html' => self::STRATEGY_REFUSE,
182 - 'minify.css' => self::STRATEGY_REFUSE,
183 - 'minify.js' => self::STRATEGY_REFUSE,
184 - 'preload.crawler' => self::STRATEGY_REFUSE,
185 - 'cdn.rewrite' => self::STRATEGY_REFUSE,
186 - 'images.lazyload' => self::STRATEGY_REFUSE,
187 - ),
188 - ),
189 - 'nitropack/main.php' => array(
190 - 'label' => 'NitroPack',
191 - 'kind' => 'mixed',
192 - 'strategy' => array(
193 - 'cache.page' => self::STRATEGY_REFUSE,
194 - 'minify.html' => self::STRATEGY_REFUSE,
195 - 'minify.css' => self::STRATEGY_REFUSE,
196 - 'minify.js' => self::STRATEGY_REFUSE,
197 - 'preload.crawler' => self::STRATEGY_REFUSE,
198 - 'cdn.rewrite' => self::STRATEGY_REFUSE,
199 - 'images.lazyload' => self::STRATEGY_REFUSE,
200 - ),
201 - ),
202 - 'wp-optimize/wp-optimize.php' => array(
203 - 'label' => 'WP-Optimize',
204 - 'kind' => 'mixed',
205 - 'strategy' => array(
206 - 'cache.page' => self::STRATEGY_WARN,
207 - 'minify.html' => self::STRATEGY_WARN,
208 - 'minify.css' => self::STRATEGY_WARN,
209 - 'minify.js' => self::STRATEGY_WARN,
210 - ),
211 - ),
212 - 'jetpack-boost/jetpack-boost.php' => array(
213 - 'label' => 'Jetpack Boost',
214 - 'kind' => 'mixed',
215 - 'strategy' => array(
216 - 'minify.css' => self::STRATEGY_WARN,
217 - 'minify.js' => self::STRATEGY_WARN,
218 - 'images.lazyload' => self::STRATEGY_WARN,
219 - ),
220 - ),
221 - 'wp-asset-clean-up/wpacu.php' => array(
222 - 'label' => 'Asset CleanUp',
223 - 'kind' => 'minify',
224 - 'strategy' => array(
225 - 'minify.css' => self::STRATEGY_WARN,
226 - 'minify.js' => self::STRATEGY_WARN,
227 - ),
228 - ),
229 - 'ewww-image-optimizer/ewww-image-optimizer.php' => array(
230 - 'label' => 'EWWW Image Optimizer',
231 - 'kind' => 'image-opt',
232 - 'strategy' => array(
233 - 'images.optimize' => self::STRATEGY_REFUSE,
234 - ),
235 - ),
236 - 'shortpixel-image-optimiser/wp-shortpixel.php' => array(
237 - 'label' => 'ShortPixel',
238 - 'kind' => 'image-opt',
239 - 'strategy' => array(
240 - 'images.optimize' => self::STRATEGY_REFUSE,
241 - ),
242 - ),
243 - 'wp-smushit/wp-smush.php' => array(
244 - 'label' => 'Smush',
245 - 'kind' => 'image-opt',
246 - 'strategy' => array(
247 - 'images.optimize' => self::STRATEGY_REFUSE,
248 - 'images.lazyload' => self::STRATEGY_WARN,
249 - ),
250 - ),
251 - );
60 + $out = array();
61 + foreach ( Cache_Plugin_Catalog::all() as $file => $entry ) {
62 + if ( empty( $entry['strategy'] ) ) {
63 + // Catalogued for detection only (e.g. an object-cache plugin);
64 + // it conflicts with nothing, so it is not a matrix row.
65 + continue;
66 + }
67 + $out[ $file ] = array(
68 + 'label' => $entry['label'],
69 + 'kind' => self::kind_for( $entry['capabilities'] ),
70 + 'strategy' => $entry['strategy'],
71 + );
72 + }
73 + return $out;
252 74 }
253 75
254 76 /**
77 + * Collapse a capability list into the single legacy `kind` label: the one
78 + * capability when there is only one, 'mixed' when there are several.
79 + *
80 + * @param string[] $capabilities
81 + */
82 + private static function kind_for( array $capabilities ): string {
83 + if ( empty( $capabilities ) ) {
84 + return 'mixed';
85 + }
86 + return count( $capabilities ) === 1 ? (string) $capabilities[0] : 'mixed';
87 + }
88 +
89 + /**
255 90 * Active conflicts on this site. Returns one entry per active
256 91 * conflicting plugin: [
257 92 * 'plugin' => 'wp-rocket/wp-rocket.php',
258 93 * 'label' => 'WP Rocket',
@@ -352,8 +187,12 @@
352 187 * deactivated_plugin so the next read reflects the new state.
353 188 */
354 189 public static function invalidate(): void {
355 190 self::$cache = null;
191 + // The matrix is a projection of the catalog, and the catalog memoizes
192 + // its own filter pass — flushing one without the other would serve a
193 + // stale projection of fresh data.
194 + Cache_Plugin_Catalog::invalidate();
356 195 }
357 196
358 197 /**
359 198 * Bootstrap hooks. Call once from Plugin::init().