PluginProbe ʕ •ᴥ•ʔ
Atarim – AI Agency for WordPress: Edit Pages, Fix Code, Update Plugins, SEO & Client Feedback / 5.1
Atarim – AI Agency for WordPress: Edit Pages, Fix Code, Update Plugins, SEO & Client Feedback v5.1
5.1.3 5.1.2 5.1.1 5.1 5.0 trunk 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 3.2.0 3.2.1 3.22 3.22.1 3.22.2 3.22.3 3.22.4 3.22.5 3.22.6 3.3.0 3.3.1 3.3.2 3.3.2.1 3.3.2.2 3.3.3 3.30 3.31 3.32 3.4 3.4.1 3.4.3 3.4.4 3.5 3.5.1 3.6 3.6.1 3.7 3.8 3.9 3.9.1 3.9.2 3.9.3 3.9.4 3.9.6 3.9.6.1 4.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.1.3 4.2 4.2.1 4.2.2 4.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.4
atarim-visual-collaboration / doit / abilities / class-avcf-abilities-cache.php
atarim-visual-collaboration / doit / abilities Last commit date
class-avcf-abilities-base.php 3 weeks ago class-avcf-abilities-block-navigation.php 3 weeks ago class-avcf-abilities-cache.php 3 weeks ago class-avcf-abilities-content.php 3 weeks ago class-avcf-abilities-core.php 3 weeks ago class-avcf-abilities-global-styles.php 3 weeks ago class-avcf-abilities-gutenberg.php 3 weeks ago class-avcf-abilities-media.php 3 weeks ago class-avcf-abilities-metadata.php 3 weeks ago class-avcf-abilities-navigation.php 3 weeks ago class-avcf-abilities-patterns.php 3 weeks ago class-avcf-abilities-plugins.php 3 weeks ago class-avcf-abilities-settings.php 3 weeks ago class-avcf-abilities-taxonomies.php 3 weeks ago class-avcf-abilities-templates.php 3 weeks ago class-avcf-abilities-theme-files.php 3 weeks ago class-avcf-abilities-themes.php 3 weeks ago class-avcf-abilities-users.php 3 weeks ago
class-avcf-abilities-cache.php
465 lines
1 <?php
2 /**
3 * Cache / CDN purge MCP abilities.
4 *
5 * WordPress core has no universal cache- or CDN-purge API: object caches, page
6 * cache plugins, and CDN/host edge layers each expose their own mechanism. This
7 * category provides a GENERIC, best-effort purge that dispatches to every cache
8 * layer it can detect on the site and honestly reports which ones actually
9 * fired — rather than pretending a single call cleared everything.
10 *
11 * Detection is guarded (function_exists / class_exists / has_action) so nothing
12 * here hard-depends on any cache plugin being installed. Every provider call is
13 * wrapped in try/catch so one broken provider never aborts the rest.
14 *
15 * Extensibility: after the built-in providers run, the following actions fire so
16 * site owners can purge custom or host-specific layers:
17 * do_action( 'atarim_purge_all_caches' )
18 * do_action( 'atarim_purge_url_cache', $url )
19 *
20 * Limitation: true CDN edge purge WITHOUT an installed integration plugin
21 * (e.g. raw Cloudflare with no Cloudflare plugin) needs per-CDN API credentials
22 * and is out of scope. This covers everything reachable through hooks/functions
23 * already loaded on the site.
24 *
25 * Exposed abilities:
26 * atarim/purge-all-caches Flush object cache + every detected page/CDN cache.
27 * atarim/purge-url-cache Purge a single URL where the provider supports it.
28 * atarim/get-cache-status Report which cache layers are detected (read-only).
29 *
30 * @package atarim-visual-collaboration
31 */
32
33 if ( ! defined('ABSPATH') ) {
34 exit;
35 }
36
37 class AVCF_Abilities_Cache extends AVCF_Abilities_Base {
38
39 /**
40 * Register all cache abilities.
41 * Called from AVCF_MCP::avcf_mcp_register_abilities() on wp_abilities_api_init.
42 */
43 public function register() {
44
45 // ---- purge-all-caches ----
46 wp_register_ability( 'atarim/purge-all-caches', [
47 'label' => 'Purge All Caches',
48 'description' => 'Best-effort purge of every cache layer detected on the site: the WordPress object cache plus popular page-cache and CDN/host plugins (WP Rocket, W3 Total Cache, WP Super Cache, LiteSpeed, WP Fastest Cache, Cache Enabler, Autoptimize, SG Optimizer, Nginx Helper, WP Engine, Kinsta, Cloudways Breeze). Each provider is detected before it is called, and the response reports exactly which ones flushed, which were absent, and which errored. Note: purging a CDN edge with NO integration plugin installed (e.g. raw Cloudflare) needs API credentials and is NOT handled here. Also fires the atarim_purge_all_caches action so custom providers can hook in.',
49 'category' => 'atarim',
50 'input_schema' => [
51 'type' => 'object',
52 'properties' => new \stdClass(),
53 'additionalProperties' => false,
54 ],
55 'output_schema' => [
56 'type' => 'object',
57 'properties' => [
58 'success' => [ 'type' => 'boolean' ],
59 'purged' => [ 'type' => 'array', 'items' => [ 'type' => 'string' ] ],
60 'skipped' => [ 'type' => 'array', 'items' => [ 'type' => 'string' ] ],
61 'errors' => [
62 'type' => 'array',
63 'items' => [
64 'type' => 'object',
65 'properties' => [
66 'provider' => [ 'type' => 'string' ],
67 'message' => [ 'type' => 'string' ],
68 ],
69 ],
70 ],
71 'message' => [ 'type' => 'string' ],
72 ],
73 'required' => [ 'success', 'purged', 'skipped', 'errors', 'message' ],
74 ],
75 'execute_callback' => function( $input = [] ) {
76 $purged = [];
77 $skipped = [];
78 $errors = [];
79
80 foreach ( $this->avcf_cache_providers() as $provider ) {
81 if ( ! $this->avcf_provider_detected( $provider ) ) {
82 $skipped[] = $provider['label'];
83 continue;
84 }
85 try {
86 call_user_func( $provider['purge_all'] );
87 $purged[] = $provider['label'];
88 } catch ( \Throwable $e ) {
89 $errors[] = [ 'provider' => $provider['label'], 'message' => $e->getMessage() ];
90 }
91 }
92
93 // Extensibility hook for custom / host-specific layers.
94 do_action( 'atarim_purge_all_caches' );
95
96 $success = empty( $errors );
97 $message = sprintf(
98 'Purged %d cache layer(s): %s. Skipped %d (not present). %d error(s).',
99 count( $purged ),
100 $purged ? implode( ', ', $purged ) : 'none',
101 count( $skipped ),
102 count( $errors )
103 );
104
105 return [
106 'success' => $success,
107 'purged' => $purged,
108 'skipped' => $skipped,
109 'errors' => $errors,
110 'message' => $message,
111 ];
112 },
113 'permission_callback' => function() {
114 return current_user_can( 'manage_options' );
115 },
116 'meta' => [
117 'mcp' => [ 'public' => true, 'type' => 'tool' ],
118 'annotations' => [ 'readonly' => false, 'destructive' => false, 'idempotent' => true ],
119 ],
120 ] );
121
122 // ---- purge-url-cache ----
123 wp_register_ability( 'atarim/purge-url-cache', [
124 'label' => 'Purge URL Cache',
125 'description' => 'Best-effort purge of the cached copy of a single URL. Only providers that support per-URL purging are invoked (WP Rocket, W3 Total Cache, LiteSpeed, Cache Enabler); others are reported as unsupported. The object cache is not touched. Also fires the atarim_purge_url_cache action with the URL so custom providers can hook in. For a full site-wide flush use atarim/purge-all-caches instead.',
126 'category' => 'atarim',
127 'input_schema' => [
128 'type' => 'object',
129 'properties' => [
130 'url' => [
131 'type' => 'string',
132 'description' => 'Absolute URL whose cached copy should be purged, e.g. https://example.com/about/.',
133 ],
134 ],
135 'required' => [ 'url' ],
136 'additionalProperties' => false,
137 ],
138 'output_schema' => [
139 'type' => 'object',
140 'properties' => [
141 'success' => [ 'type' => 'boolean' ],
142 'url' => [ 'type' => 'string' ],
143 'purged' => [ 'type' => 'array', 'items' => [ 'type' => 'string' ] ],
144 'unsupported' => [ 'type' => 'array', 'items' => [ 'type' => 'string' ] ],
145 'errors' => [
146 'type' => 'array',
147 'items' => [
148 'type' => 'object',
149 'properties' => [
150 'provider' => [ 'type' => 'string' ],
151 'message' => [ 'type' => 'string' ],
152 ],
153 ],
154 ],
155 'message' => [ 'type' => 'string' ],
156 ],
157 'required' => [ 'success', 'url', 'purged', 'unsupported', 'errors', 'message' ],
158 ],
159 'execute_callback' => function( $input = [] ) {
160 $url = isset( $input['url'] ) ? esc_url_raw( trim( (string) $input['url'] ) ) : '';
161 if ( $url === '' ) {
162 return [
163 'success' => false,
164 'url' => '',
165 'purged' => [],
166 'unsupported' => [],
167 'errors' => [],
168 'message' => 'url must be a non-empty, valid absolute URL.',
169 ];
170 }
171
172 $purged = [];
173 $unsupported = [];
174 $errors = [];
175
176 foreach ( $this->avcf_cache_providers() as $provider ) {
177 if ( empty( $provider['purge_url'] ) ) {
178 continue; // Provider has no per-URL mechanism at all — not reported.
179 }
180 if ( ! $this->avcf_provider_detected( $provider ) ) {
181 continue; // Not installed — silent; only report installed-but-unsupported below.
182 }
183 try {
184 call_user_func( $provider['purge_url'], $url );
185 $purged[] = $provider['label'];
186 } catch ( \Throwable $e ) {
187 $errors[] = [ 'provider' => $provider['label'], 'message' => $e->getMessage() ];
188 }
189 }
190
191 // Report detected page/CDN providers that can only flush site-wide.
192 // The object cache is intentionally excluded — it is not a per-URL layer.
193 foreach ( $this->avcf_cache_providers() as $provider ) {
194 if ( $provider['key'] === 'object_cache' || ! empty( $provider['purge_url'] ) ) {
195 continue;
196 }
197 if ( $this->avcf_provider_detected( $provider ) ) {
198 $unsupported[] = $provider['label'];
199 }
200 }
201
202 // Extensibility hook for custom / host-specific layers.
203 do_action( 'atarim_purge_url_cache', $url );
204
205 $success = empty( $errors );
206 $message = sprintf(
207 'Purged URL from %d provider(s): %s. %d detected provider(s) support site-wide flush only. %d error(s).',
208 count( $purged ),
209 $purged ? implode( ', ', $purged ) : 'none',
210 count( $unsupported ),
211 count( $errors )
212 );
213
214 return [
215 'success' => $success,
216 'url' => $url,
217 'purged' => $purged,
218 'unsupported' => $unsupported,
219 'errors' => $errors,
220 'message' => $message,
221 ];
222 },
223 'permission_callback' => function() {
224 return current_user_can( 'manage_options' );
225 },
226 'meta' => [
227 'mcp' => [ 'public' => true, 'type' => 'tool' ],
228 'annotations' => [ 'readonly' => false, 'destructive' => false, 'idempotent' => true ],
229 ],
230 ] );
231
232 // ---- get-cache-status ----
233 wp_register_ability( 'atarim/get-cache-status', [
234 'label' => 'Get Cache Status',
235 'description' => 'Reports which cache layers are currently detected on the site (object cache, page-cache plugins, CDN/host integrations) without purging anything. Use this before purging to understand what a purge-all-caches call would affect. Also reports whether a persistent object cache backend is active.',
236 'category' => 'atarim',
237 'input_schema' => [
238 'type' => 'object',
239 'properties' => new \stdClass(),
240 'additionalProperties' => false,
241 ],
242 'output_schema' => [
243 'type' => 'object',
244 'properties' => [
245 'object_cache_persistent' => [ 'type' => 'boolean' ],
246 'detected' => [ 'type' => 'array', 'items' => [ 'type' => 'string' ] ],
247 'not_detected' => [ 'type' => 'array', 'items' => [ 'type' => 'string' ] ],
248 'supports_url_purge' => [ 'type' => 'array', 'items' => [ 'type' => 'string' ] ],
249 'message' => [ 'type' => 'string' ],
250 ],
251 'required' => [ 'detected', 'not_detected', 'message' ],
252 ],
253 'execute_callback' => function( $input = [] ) {
254 $detected = [];
255 $not_detected = [];
256 $url_purge = [];
257
258 foreach ( $this->avcf_cache_providers() as $provider ) {
259 if ( $provider['key'] === 'object_cache' ) {
260 continue; // Reported separately below.
261 }
262 if ( $this->avcf_provider_detected( $provider ) ) {
263 $detected[] = $provider['label'];
264 if ( ! empty( $provider['purge_url'] ) ) {
265 $url_purge[] = $provider['label'];
266 }
267 } else {
268 $not_detected[] = $provider['label'];
269 }
270 }
271
272 return [
273 'object_cache_persistent' => (bool) wp_using_ext_object_cache(),
274 'detected' => $detected,
275 'not_detected' => $not_detected,
276 'supports_url_purge' => $url_purge,
277 'message' => sprintf(
278 '%d cache layer(s) detected: %s.',
279 count( $detected ),
280 $detected ? implode( ', ', $detected ) : 'none (object cache only)'
281 ),
282 ];
283 },
284 'permission_callback' => function() {
285 return current_user_can( 'manage_options' );
286 },
287 'meta' => [
288 'mcp' => [ 'public' => true, 'type' => 'tool' ],
289 'annotations' => [ 'readonly' => true, 'destructive' => false, 'idempotent' => true ],
290 ],
291 ] );
292 }
293
294 /**
295 * Whether a provider is present on this site.
296 *
297 * @param array $provider
298 * @return bool
299 */
300 protected function avcf_provider_detected( $provider ) {
301 try {
302 return (bool) call_user_func( $provider['detect'] );
303 } catch ( \Throwable $e ) {
304 return false;
305 }
306 }
307
308 /**
309 * Best-effort cache provider dispatch table.
310 *
311 * Each entry:
312 * key string stable identifier
313 * label string human-readable name (used in reports)
314 * detect callable ():bool — is this provider present?
315 * purge_all callable ():void — flush everything for this provider
316 * purge_url callable|null (string):void — purge one URL, or null if unsupported
317 *
318 * @return array<int,array>
319 */
320 protected function avcf_cache_providers() {
321 return [
322 // --- Object cache (WordPress core; always available) ---
323 [
324 'key' => 'object_cache',
325 'label' => 'WordPress object cache',
326 'detect' => function() { return function_exists( 'wp_cache_flush' ); },
327 'purge_all' => function() { wp_cache_flush(); },
328 'purge_url' => null,
329 ],
330
331 // --- Page cache plugins ---
332 [
333 'key' => 'wp_rocket',
334 'label' => 'WP Rocket',
335 'detect' => function() { return function_exists( 'rocket_clean_domain' ); },
336 'purge_all' => function() { rocket_clean_domain(); },
337 'purge_url' => function( $url ) {
338 if ( function_exists( 'rocket_clean_files' ) ) {
339 rocket_clean_files( $url );
340 }
341 },
342 ],
343 [
344 'key' => 'w3tc',
345 'label' => 'W3 Total Cache',
346 'detect' => function() { return function_exists( 'w3tc_flush_all' ); },
347 'purge_all' => function() { w3tc_flush_all(); },
348 'purge_url' => function( $url ) {
349 if ( function_exists( 'w3tc_flush_url' ) ) {
350 w3tc_flush_url( $url );
351 }
352 },
353 ],
354 [
355 'key' => 'wp_super_cache',
356 'label' => 'WP Super Cache',
357 'detect' => function() { return function_exists( 'wp_cache_clear_cache' ); },
358 'purge_all' => function() { wp_cache_clear_cache(); },
359 'purge_url' => null,
360 ],
361 [
362 'key' => 'litespeed',
363 'label' => 'LiteSpeed Cache',
364 'detect' => function() { return defined( 'LSCWP_V' ) || has_action( 'litespeed_purge_all' ); },
365 'purge_all' => function() { do_action( 'litespeed_purge_all' ); },
366 'purge_url' => function( $url ) { do_action( 'litespeed_purge_url', $url ); },
367 ],
368 [
369 'key' => 'wp_fastest_cache',
370 'label' => 'WP Fastest Cache',
371 'detect' => function() {
372 global $wp_fastest_cache;
373 return is_object( $wp_fastest_cache ) && method_exists( $wp_fastest_cache, 'deleteCache' );
374 },
375 'purge_all' => function() {
376 global $wp_fastest_cache;
377 $wp_fastest_cache->deleteCache( true );
378 },
379 'purge_url' => null,
380 ],
381 [
382 'key' => 'cache_enabler',
383 'label' => 'Cache Enabler',
384 'detect' => function() {
385 return class_exists( 'Cache_Enabler' ) && method_exists( 'Cache_Enabler', 'clear_complete_cache' );
386 },
387 'purge_all' => function() { Cache_Enabler::clear_complete_cache(); },
388 'purge_url' => function( $url ) {
389 if ( method_exists( 'Cache_Enabler', 'clear_page_cache_by_url' ) ) {
390 Cache_Enabler::clear_page_cache_by_url( $url );
391 }
392 },
393 ],
394 [
395 'key' => 'autoptimize',
396 'label' => 'Autoptimize',
397 'detect' => function() {
398 return class_exists( 'autoptimizeCache' ) && method_exists( 'autoptimizeCache', 'clearall' );
399 },
400 'purge_all' => function() { autoptimizeCache::clearall(); },
401 'purge_url' => null,
402 ],
403
404 // --- CDN / host edge integrations ---
405 [
406 'key' => 'sg_optimizer',
407 'label' => 'SG Optimizer (SiteGround)',
408 'detect' => function() {
409 return function_exists( 'sg_cachepress_purge_cache' ) || has_action( 'siteground_optimizer_flush_cache' );
410 },
411 'purge_all' => function() {
412 if ( function_exists( 'sg_cachepress_purge_cache' ) ) {
413 sg_cachepress_purge_cache();
414 } else {
415 do_action( 'siteground_optimizer_flush_cache' );
416 }
417 },
418 'purge_url' => null,
419 ],
420 [
421 'key' => 'nginx_helper',
422 'label' => 'Nginx Helper',
423 'detect' => function() { return has_action( 'rt_nginx_helper_purge_all' ); },
424 'purge_all' => function() { do_action( 'rt_nginx_helper_purge_all' ); },
425 'purge_url' => null,
426 ],
427 [
428 'key' => 'wp_engine',
429 'label' => 'WP Engine',
430 'detect' => function() {
431 return class_exists( 'WpeCommon' ) && method_exists( 'WpeCommon', 'purge_varnish_cache' );
432 },
433 'purge_all' => function() { WpeCommon::purge_varnish_cache(); },
434 'purge_url' => null,
435 ],
436 [
437 'key' => 'kinsta',
438 'label' => 'Kinsta Cache',
439 'detect' => function() { return has_action( 'kinsta_cache_purge_all' ) || function_exists( 'kinsta_cache_purge' ); },
440 'purge_all' => function() {
441 if ( has_action( 'kinsta_cache_purge_all' ) ) {
442 do_action( 'kinsta_cache_purge_all' );
443 } elseif ( function_exists( 'kinsta_cache_purge' ) ) {
444 kinsta_cache_purge();
445 }
446 },
447 'purge_url' => null,
448 ],
449 [
450 'key' => 'breeze',
451 'label' => 'Breeze (Cloudways)',
452 'detect' => function() { return class_exists( 'Breeze_PurgeCache' ) || has_action( 'breeze_clear_all_cache' ); },
453 'purge_all' => function() {
454 if ( class_exists( 'Breeze_PurgeCache' ) && method_exists( 'Breeze_PurgeCache', 'breeze_cache_flush' ) ) {
455 Breeze_PurgeCache::breeze_cache_flush();
456 } else {
457 do_action( 'breeze_clear_all_cache' );
458 }
459 },
460 'purge_url' => null,
461 ],
462 ];
463 }
464 }
465