PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 1.28.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v1.28.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.28.0, at includes/core/class-deactivator.php

137 lines 4.5 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 }
55
56 /**
57 * Clear all scheduled hooks
58 *
59 * @return void
60 */
61 private function clear_scheduled_hooks(): void {
62 $scheduled_hooks = [
63 'thinkrank_cache_cleanup',
64 'thinkrank_usage_analytics',
65 // Email report tick (see ThinkRank\SEO\Email_Report_Scheduler::CRON_HOOK)
66 // — was previously left scheduled after deactivation.
67 'thinkrank_email_report_tick',
68 // Brand Visibility drain + its recurring stall watchdog
69 // (ThinkRank\AI\Brand_Visibility_Runner::TICK_HOOK / WATCHDOG_HOOK).
70 'thinkrank_bv_tick',
71 'thinkrank_bv_watchdog',
72 ];
73
74 // Clear all instances of our hooks
75 foreach ($scheduled_hooks as $hook) {
76 wp_clear_scheduled_hook($hook);
77 }
78 }
79
80 /**
81 * Clear plugin cache
82 *
83 * @return void
84 */
85 private function clear_cache(): void {
86 global $wpdb;
87
88 // Clear AI cache table with proper escaping
89 $cache_table = $wpdb->prefix . 'thinkrank_ai_cache';
90 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Plugin deactivation requires direct database access to check table existence
91 $table_exists = $wpdb->get_var(
92 $wpdb->prepare("SHOW TABLES LIKE %s", $cache_table)
93 );
94 if ($table_exists === $cache_table) {
95 // 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
96 $wpdb->query("TRUNCATE TABLE {$cache_table}");
97 }
98
99 // Clear WordPress transients with proper escaping
100 // 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
101 $wpdb->query(
102 $wpdb->prepare(
103 "DELETE FROM {$wpdb->options}
104 WHERE option_name LIKE %s
105 OR option_name LIKE %s",
106 $wpdb->esc_like('_transient_thinkrank_') . '%',
107 $wpdb->esc_like('_transient_timeout_thinkrank_') . '%'
108 )
109 );
110
111 // Clear object cache if available
112 if (function_exists('wp_cache_flush_group')) {
113 wp_cache_flush_group('thinkrank');
114 }
115 }
116
117 /**
118 * Log deactivation for analytics
119 *
120 * @return void
121 */
122 private function log_deactivation(): void {
123 $deactivation_data = [
124 'timestamp' => time(),
125 'version' => THINKRANK_VERSION,
126 'wp_version' => get_bloginfo('version'),
127 'php_version' => PHP_VERSION,
128 ];
129
130 // Store deactivation data for potential feedback
131 update_option('thinkrank_last_deactivation', $deactivation_data);
132
133 // Remove activation flag
134 delete_option('thinkrank_activated');
135 }
136 }
137