watched_options() as $option_name) { add_action('update_option_' . $option_name, array($this, 'queue_purge'), 10, 0); add_action('add_option_' . $option_name, array($this, 'queue_purge'), 10, 0); } add_action(self::DEFERRED_EVENT, array($this, 'purge_page_caches')); } /** * Option names whose writes trigger a page-cache purge. * * Defaults cover every option whose values reach the cached page's inline * widget payload: mxchat_options (settings/behavior gates), * mxchat_theme_options (active AI theme CSS + bot theme assignments — * written only by the MxChat Theme add-on's admin AJAX actions), and * mxchat_prompts_options (Pinecone toggle — written only by the prompts * autosave). All write sites are admin-action-driven; the debounce * absorbs back-to-back writes like apply-and-save. * * @return string[] */ private function watched_options() { $defaults = array('mxchat_options', 'mxchat_theme_options', 'mxchat_prompts_options'); $options = apply_filters('mxchat_cache_purge_watched_options', $defaults); return is_array($options) ? array_filter(array_map('strval', $options)) : $defaults; } /** * Coalesce all option writes in this request into one shutdown purge. */ public function queue_purge() { if ($this->purge_queued) { return; } $this->purge_queued = true; add_action('shutdown', array($this, 'run_queued_purge'), 5); } /** * Shutdown handler: purge unless one ran inside the debounce window; in * that case schedule a single deferred purge so the last write still wins. */ public function run_queued_purge() { if (get_transient(self::DEBOUNCE_TRANSIENT)) { if (!wp_next_scheduled(self::DEFERRED_EVENT)) { wp_schedule_single_event(time() + self::DEBOUNCE_SECONDS, self::DEFERRED_EVENT); } return; } set_transient(self::DEBOUNCE_TRANSIENT, 1, self::DEBOUNCE_SECONDS); $this->purge_page_caches(); } /** * Purge every supported page cache via its own public API. Each call is * guarded so installs without that plugin skip it silently. */ public function purge_page_caches() { // WP Rocket — public helper, clears the whole domain's page cache. if (function_exists('rocket_clean_domain')) { rocket_clean_domain(); } // LiteSpeed Cache — documented third-party purge API. if (defined('LSCWP_V') || class_exists('LiteSpeed\Core')) { do_action('litespeed_purge_all'); } // W3 Total Cache — page-cache flush helper. if (function_exists('w3tc_pgcache_flush')) { w3tc_pgcache_flush(); } // WP Super Cache — global cache clear. if (function_exists('wp_cache_clear_cache')) { wp_cache_clear_cache(); } // FlyingPress — documented programmatic purge (purge_pages clears the // HTML page cache without rebuilding optimized assets): // https://docs.flyingpress.com/en/articles/11406092-programmatically-purge-and-preload-cache if (class_exists('\FlyingPress\Purge') && method_exists('\FlyingPress\Purge', 'purge_pages')) { \FlyingPress\Purge::purge_pages(); } // Extension point for CDNs / host caches / custom setups. do_action('mxchat_purge_page_caches'); } }