PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 1.32.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v1.32.0
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
thinkrank / includes / core / class-deactivator.php

class-deactivator.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 1.32.0, at includes/core/class-deactivator.php

143 lines 4.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Deactivator Class
4 *
5 * Handles plugin deactivation tasks
6 *
7 * @package ThinkRank\Core
8 * @since 1.0.0
9 */
10
11 declare(strict_types=1);
12
13 namespace ThinkRank\Core;
14
15 // Prevent direct access
16 if (!defined('ABSPATH')) {
17 exit;
18 }
19
20 /**
21 * Deactivator Class
22 *
23 * Single Responsibility: Handle plugin deactivation tasks only
24 *
25 * @since 1.0.0
26 */
27 class Deactivator {
28
29 /**
30 * Plugin deactivation tasks
31 *
32 * @return void
33 */
34 public function deactivate(): void {
35 $this->clear_scheduled_hooks();
36 $this->clear_cache();
37 $this->remove_published_files();
38 $this->log_deactivation();
39
40 // Note: We don't delete user data on deactivation
41 // Data is only removed on uninstall
42 }
43
44 /**
45 * Remove physically published files so they stop serving once the plugin
46 * is inactive (the published llms.txt would otherwise keep serving forever).
47 *
48 * @return void
49 */
50 private function remove_published_files(): void {
51 if (class_exists('ThinkRank\\SEO\\LLMs_Txt_Manager')) {
52 (new \ThinkRank\SEO\LLMs_Txt_Manager())->delete_llms_txt_file();
53 }
54 // Static /.well-known/ OAuth discovery files (published by the MCP
55 // self-test on hosts whose proxy intercepts that directory) — a
56 // static copy must not keep advertising a server that is now off.
57 if (class_exists('ThinkRank\\Mcp\\Mcp_Static_Discovery')) {
58 \ThinkRank\Mcp\Mcp_Static_Discovery::remove();
59 }
60 }
61
62 /**
63 * Clear all scheduled hooks
64 *
65 * @return void
66 */
67 private function clear_scheduled_hooks(): void {
68 $scheduled_hooks = [
69 'thinkrank_cache_cleanup',
70 'thinkrank_usage_analytics',
71 // Email report tick (see ThinkRank\SEO\Email_Report_Scheduler::CRON_HOOK)
72 // — was previously left scheduled after deactivation.
73 'thinkrank_email_report_tick',
74 // Brand Visibility drain + its recurring stall watchdog
75 // (ThinkRank\AI\Brand_Visibility_Runner::TICK_HOOK / WATCHDOG_HOOK).
76 'thinkrank_bv_tick',
77 'thinkrank_bv_watchdog',
78 ];
79
80 // Clear all instances of our hooks
81 foreach ($scheduled_hooks as $hook) {
82 wp_clear_scheduled_hook($hook);
83 }
84 }
85
86 /**
87 * Clear plugin cache
88 *
89 * @return void
90 */
91 private function clear_cache(): void {
92 global $wpdb;
93
94 // Clear AI cache table with proper escaping
95 $cache_table = $wpdb->prefix . 'thinkrank_ai_cache';
96 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Plugin deactivation requires direct database access to check table existence
97 $table_exists = $wpdb->get_var(
98 $wpdb->prepare("SHOW TABLES LIKE %s", $cache_table)
99 );
100 if ($table_exists === $cache_table) {
101 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name is properly constructed from controlled prefix, plugin deactivation requires direct database access
102 $wpdb->query("TRUNCATE TABLE {$cache_table}");
103 }
104
105 // Clear WordPress transients with proper escaping
106 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- $wpdb->options is a WordPress core property, plugin deactivation requires direct database access
107 $wpdb->query(
108 $wpdb->prepare(
109 "DELETE FROM {$wpdb->options}
110 WHERE option_name LIKE %s
111 OR option_name LIKE %s",
112 $wpdb->esc_like('_transient_thinkrank_') . '%',
113 $wpdb->esc_like('_transient_timeout_thinkrank_') . '%'
114 )
115 );
116
117 // Clear object cache if available
118 if (function_exists('wp_cache_flush_group')) {
119 wp_cache_flush_group('thinkrank');
120 }
121 }
122
123 /**
124 * Log deactivation for analytics
125 *
126 * @return void
127 */
128 private function log_deactivation(): void {
129 $deactivation_data = [
130 'timestamp' => time(),
131 'version' => THINKRANK_VERSION,
132 'wp_version' => get_bloginfo('version'),
133 'php_version' => PHP_VERSION,
134 ];
135
136 // Store deactivation data for potential feedback
137 update_option('thinkrank_last_deactivation', $deactivation_data);
138
139 // Remove activation flag
140 delete_option('thinkrank_activated');
141 }
142 }
143