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

138 lines 4.7 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 // Read from the shared manifest rather than a local copy. Deactivation
69 // cleared 5 of the 15 hooks the plugin schedules and uninstall cleared
70 // 2, so a removal left recurring events behind — including
71 // thinkrank_google_token_refresh on the custom thinkrank_45min
72 // recurrence, whose interval no longer resolves once the plugin's
73 // cron_schedules filter is gone (#389).
74 $manifest = require THINKRANK_PLUGIN_DIR . 'includes/cleanup-manifest.php';
75
76 foreach ($manifest['cron_hooks'] as $hook) {
77 wp_clear_scheduled_hook($hook);
78 }
79 }
80
81 /**
82 * Clear plugin cache
83 *
84 * @return void
85 */
86 private function clear_cache(): void {
87 global $wpdb;
88
89 // Clear AI cache table with proper escaping
90 $cache_table = $wpdb->prefix . 'thinkrank_ai_cache';
91 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Plugin deactivation requires direct database access to check table existence
92 $table_exists = $wpdb->get_var(
93 $wpdb->prepare("SHOW TABLES LIKE %s", $cache_table)
94 );
95 if ($table_exists === $cache_table) {
96 // 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
97 $wpdb->query("TRUNCATE TABLE {$cache_table}");
98 }
99
100 // Clear WordPress transients with proper escaping
101 // 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
102 $wpdb->query(
103 $wpdb->prepare(
104 "DELETE FROM {$wpdb->options}
105 WHERE option_name LIKE %s
106 OR option_name LIKE %s",
107 $wpdb->esc_like('_transient_thinkrank_') . '%',
108 $wpdb->esc_like('_transient_timeout_thinkrank_') . '%'
109 )
110 );
111
112 // Clear object cache if available
113 if (function_exists('wp_cache_flush_group')) {
114 wp_cache_flush_group('thinkrank');
115 }
116 }
117
118 /**
119 * Log deactivation for analytics
120 *
121 * @return void
122 */
123 private function log_deactivation(): void {
124 $deactivation_data = [
125 'timestamp' => time(),
126 'version' => THINKRANK_VERSION,
127 'wp_version' => get_bloginfo('version'),
128 'php_version' => PHP_VERSION,
129 ];
130
131 // Store deactivation data for potential feedback
132 update_option('thinkrank_last_deactivation', $deactivation_data);
133
134 // Remove activation flag
135 delete_option('thinkrank_activated');
136 }
137 }
138