# mxchat-basic/3.2.25/includes/class-mxchat-cache-purge.php

MxChat – AI Chatbot &amp; Content Generation for WordPress, version 3.2.25. 171 lines.

- Page: https://pluginprobe.com/plugins/mxchat-basic/3.2.25/code/includes/class-mxchat-cache-purge.php
- Raw: https://pluginprobe.com/plugins/mxchat-basic/3.2.25/raw/includes/class-mxchat-cache-purge.php
- Modified: 2026-10-10T17:08:54+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/mxchat-basic/3.2.25/code/includes/class-mxchat-cache-purge.php#L10-L20`.

```php
<?php
if (!defined('ABSPATH')) {
    exit;
}

/**
 * MxChat_Cache_Purge — purges known full-page caches when MxChat settings save.
 *
 * Widget settings are printed inline into page HTML (wp_localize_script in
 * class-mxchat-integrator.php), so a full-page cache keeps serving the old
 * snapshot after an admin changes a setting — toggles look broken until the
 * cache is cleared by hand. This module clears the page caches we can reach
 * from PHP the moment mxchat_options is written, using each cache plugin's
 * own public purge API (guarded by existence checks, so absent plugins are
 * a no-op). Companion to the 3.2.6 page-cache compatibility filters in
 * mxchat-basic.php, which keep the chat AJAX endpoints out of those same
 * caches; this handles the cached page HTML itself.
 *
 * Host-level caches and CDNs (Cloudflare APO etc.) cannot be purged from
 * PHP — for those, the widget re-fetches its behavior settings when opened
 * (see MxChat_Integrator::get_dynamic_widget_settings()).
 *
 * Debounce: the admin screen autosaves per field, so a settings session can
 * write mxchat_options many times in a minute. We purge at most once per
 * DEBOUNCE_SECONDS; writes that land inside the window schedule one deferred
 * purge via wp-cron so the final state always gets flushed.
 *
 * @since 3.2.9 (plan-32db95)
 */
class MxChat_Cache_Purge {

    const DEBOUNCE_TRANSIENT = 'mxchat_cache_purge_debounce';
    const DEBOUNCE_SECONDS   = 30;
    const DEFERRED_EVENT     = 'mxchat_deferred_cache_purge';

    /**
     * Owner switch (plan b76ed6, Support Ticket 468: a 1,400-page FlyingPress
     * site lost its whole preload on every settings save). Standalone option,
     * 'on' / 'off'; absent means ON so existing sites keep today's behaviour.
     * Rendered on Settings → Optimization → Page Cache Compatibility and saved
     * by the mxchat_cache_purge_enabled case in class-ajax-handler.php.
     *
     * The switch is read when a watched option is actually written (and when
     * a deferred purge fires), not at plugin load: the hooks below cost
     * nothing until a save happens, and a site that never touches the switch
     * never pays a get_option() for it on a visitor request.
     */
    const OPTION_NAME = 'mxchat_cache_purge_enabled';

    private static $instance = null;

    /**
     * Whether this request already queued a shutdown purge (one per request).
     *
     * @var bool
     */
    private $purge_queued = false;

    public static function init() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Whether MxChat clears page caches when its settings are saved.
     * Default ON; only an explicit 'off' switches it off.
     *
     * @return bool
     */
    public static function is_enabled() {
        return get_option(self::OPTION_NAME, 'on') !== 'off';
    }

    private function __construct() {
        foreach ($this->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 || !self::is_enabled()) {
            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() {
        // The owner may have switched the purge off after a deferred purge was
        // scheduled; off means off.
        if (!self::is_enabled()) {
            return;
        }

        // 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');
    }
}

```
