PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.4.2
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.4.2
1.4.2 1.4.1 1.4.0 1.3.7 1.3.6 1.3.5 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 All 36 releases
← All changes | uninstall.php +91 -1 1.2.4 → 1.4.2 View file →
@@ -14,8 +14,17 @@
14 14 /**
15 15 * Run all uninstall cleanup. Wrapped in a function so locals don't pollute global scope.
16 16 */
17 17 function xspeed_uninstall_cleanup() {
18 + /*
19 + * Everything here is ours to delete. `wpdeveloper_xspeed_offer` is NOT — it
20 + * is the site's answer about whether it wants this plugin, written by
21 + * whichever WPDeveloper plugin offered it. Removing xSpeed is itself an
22 + * answer — the siblings read it as one, from that row plus the absent plugin
23 + * files. Deleting it here would make every one of them offer xSpeed again to
24 + * the user who just took it off.
25 + * See docs/guides/installing-from-another-plugin.md.
26 + */
18 27 delete_option( 'xspeed_options' );
19 28
20 29 /*
21 30 * Per-module rows, for the modules THIS plugin ships. The slugs are read
@@ -39,10 +48,20 @@
39 48 delete_option( 'xspeed_data_version' );
40 49 delete_option( 'xspeed_activity_log' );
41 50 delete_option( 'xspeed_server_type' );
42 51 delete_option( 'xspeed_oc_dropin_synced' );
52 + delete_option( 'xspeed_oc_generation' );
53 + delete_option( 'xspeed_oc_sync_attempts' );
54 + delete_option( 'xspeed_overridden_constants' );
43 55 delete_option( 'xspeed_last_mobile_separate' );
56 + delete_option( 'xspeed_rules_inputs' );
57 + // Per-user, so it needs the meta sweep rather than delete_option: every
58 + // admin who ever confirmed they pasted the server rules has one.
59 + if ( function_exists( 'delete_metadata' ) ) {
60 + delete_metadata( 'user', 0, 'xspeed_nginx_rules_copied', '', true );
61 + }
44 62 delete_option( 'xspeed_redirect_to_onboarding' );
63 + delete_option( 'xspeed_preloader_firewall_block' );
45 64 delete_option( 'xspeed_onboarding_complete' );
46 65 // Provenance a host plugin wrote before it activated us, and the profile
47 66 // that install came up with.
48 67 delete_option( 'xspeed_installed_by' );
@@ -52,15 +71,86 @@
52 71 // before xSpeed seeded its own conflict-safe profile on activation. We no
53 72 // longer write it; an older host may have, and it is ours to clean up.
54 73 delete_option( 'xspeed_setup_snapshot' );
55 74 delete_option( 'xspeed_stats' );
75 + // Where the scheduled database clean stopped. Kept here, above the
76 + // filesystem early returns below, so a site whose filesystem cannot be
77 + // reached still loses it.
78 + delete_option( 'xspeed_db_clean_cursor' );
56 79 delete_option( 'xspeed_gc_cursor' );
80 + delete_option( 'xspeed_mcp_rl_gen' );
57 81 wp_clear_scheduled_hook( 'xspeed_gc' );
82 + delete_option( 'xspeed_cache_off_notice_dismissed' );
58 83
84 + global $wpdb;
85 +
86 + // Hit counters and the Hub attachment flag — ours, and simply never named
87 + // here before. xspeed_hit_buffer is BOTH an option and a transient of the
88 + // same name (Hit_Counter uses one string for the memory buffer and the
89 + // durable counter), so both stores need clearing.
90 + delete_option( 'xspeed_hit_buffer' );
91 + delete_transient( 'xspeed_hit_buffer' );
92 + delete_option( 'xspeed_hit_daily' );
93 + delete_option( 'xspeed_hub_site_attached' );
94 +
95 + // Usage-tracking state, which lives in the shared WP Insights rows rather
96 + // than under our own prefix. Left behind, the consent key survives an
97 + // uninstall: a REINSTALL then reads as already opted in before the wizard
98 + // has asked anything, and a later deactivation posts a diagnostic payload
99 + // for a consent this install never collected (#439).
100 + //
101 + // The two keyed rows are SHARED with sibling WPDeveloper plugins, so only
102 + // our own key comes out and the row itself is deleted only once nothing
103 + // else is using it. Deleting them outright would wipe another plugin's
104 + // consent state.
105 + foreach ( array( 'wpins_allow_tracking', 'wpins_last_track_time' ) as $shared ) {
106 + $value = get_option( $shared );
107 + if ( ! is_array( $value ) ) {
108 + continue; // Absent, or a shape we did not write — leave it alone.
109 + }
110 + unset( $value['xspeed'] );
111 + if ( empty( $value ) ) {
112 + delete_option( $shared );
113 + } else {
114 + update_option( $shared, $value );
115 + }
116 + }
117 + // The deactivation-feedback payload. Normally consumed-then-deleted by the
118 + // tracker's own deactivation send, but that only happens when consent
119 + // passes and the send succeeds — a user who deactivates without consent
120 + // leaves both rows behind, and they are exactly the orphaned diagnostic
121 + // payload #439 describes.
122 + delete_option( 'wpins_deactivation_reason_xspeed' );
123 + delete_option( 'wpins_deactivation_details_xspeed' );
124 + // The tracker's recurring send. Cleared on opt-out, but nothing guarantees
125 + // an opt-out ever happened before the uninstall.
126 + wp_clear_scheduled_hook( 'xspeed_do_weekly_action' );
127 + // Health's edge-mode probe: its pending run and its stored verdict.
128 + wp_clear_scheduled_hook( 'xspeed_edge_mode_probe_refresh' );
129 + delete_transient( 'xspeed_edge_mode_probe' );
130 + delete_transient( 'xspeed_edge_mode_probe_token' );
131 + // Our own WP Insights rows: the site id, the original URL, and the last
132 + // payload — whose name embeds the site id. The payload names are
133 + // derivable from the id, but a failed earlier uninstall or a renamed row
134 + // shape would strand them, so sweep the prefix. `xspeed-pro`'s rows
135 + // survive this only because its slug's HYPHEN doesn't match the
136 + // underscore in `wpins_xspeed_%` — that separator is load-bearing.
137 + delete_option( 'wpins_xspeed_site_id' );
138 + delete_option( 'wpins_xspeed_original_url' );
139 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- options API has no prefix delete; uninstall only.
140 + $wpins_rows = $wpdb->get_col(
141 + $wpdb->prepare(
142 + "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
143 + $wpdb->esc_like( 'wpins_xspeed_' ) . '%'
144 + )
145 + );
146 + foreach ( (array) $wpins_rows as $wpins_row ) {
147 + delete_option( $wpins_row );
148 + }
149 +
59 150 // Score history — the plugin's own table, plus the legacy option the
60 151 // table was migrated from (kept on upgrade so a bad migration is
61 152 // recoverable; there is nothing to recover on uninstall).
62 - global $wpdb;
63 153 // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- own table, uninstall.
64 154 $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'xspeed_scores' );
65 155 delete_option( 'xspeed_score_schema' );
66 156 delete_option( 'xspeed_score_history' );