prefix . 'thinkrank_ai_cache', $wpdb->prefix . 'thinkrank_ai_usage', $wpdb->prefix . 'thinkrank_content_briefs', $wpdb->prefix . 'thinkrank_seo_scores', ]; foreach ($tables as $table) { // Check WordPress version for %i support (introduced in 6.2) if (version_compare($GLOBALS['wp_version'], '6.2', '>=')) { // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnsupportedIdentifierPlaceholder,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange $wpdb->query($wpdb->prepare("DROP TABLE IF EXISTS %i", $table)); // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnsupportedIdentifierPlaceholder,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange } else { // Fallback for older WordPress versions - table name is from our controlled list $escaped_table = esc_sql($table); // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange $wpdb->query("DROP TABLE IF EXISTS `{$escaped_table}`"); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange } } } /** * Remove plugin options * * @return void */ private static function remove_options(): void { $options = [ 'thinkrank_version', 'thinkrank_free_credits', 'thinkrank_ai_provider', 'thinkrank_cache_duration', 'thinkrank_max_requests_per_minute', 'thinkrank_enable_logging', 'thinkrank_auto_optimize', 'thinkrank_seo_score_threshold', 'thinkrank_activated', 'thinkrank_activation_time', 'thinkrank_show_welcome', 'thinkrank_encryption_key', 'thinkrank_keep_data_on_uninstall', 'thinkrank_last_deactivation', ]; foreach ($options as $option) { delete_option($option); } // Remove transients with proper escaping global $wpdb; // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Core table name is safe, uninstall cleanup requires direct database access $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", $wpdb->esc_like('_transient_thinkrank_') . '%', $wpdb->esc_like('_transient_timeout_thinkrank_') . '%' ) ); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching } /** * Remove user meta data * * @return void */ private static function remove_user_meta(): void { global $wpdb; // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Core table name is safe, uninstall cleanup requires direct database access $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE %s", $wpdb->esc_like('thinkrank_') . '%' ) ); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching } /** * Remove post meta data * * @return void */ private static function remove_post_meta(): void { global $wpdb; // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Core table name is safe, uninstall cleanup requires direct database access $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE %s OR meta_key LIKE %s", $wpdb->esc_like('thinkrank_') . '%', $wpdb->esc_like('_thinkrank_') . '%' ) ); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching } /** * Clear all caches * * @return void */ private static function clear_caches(): void { // Clear object cache if (function_exists('wp_cache_flush_group')) { wp_cache_flush_group('thinkrank'); } // Clear any external caches if (function_exists('wp_cache_flush')) { wp_cache_flush(); } } /** * Remove scheduled cron jobs * * @return void */ private static function remove_cron_jobs(): void { $cron_jobs = [ 'thinkrank_monthly_credit_reset', 'thinkrank_cache_cleanup', 'thinkrank_usage_analytics', ]; foreach ($cron_jobs as $job) { wp_clear_scheduled_hook($job); } } /** * Remove custom capabilities * * @return void */ private static function remove_capabilities(): void { $capabilities = [ 'thinkrank_manage_settings', 'thinkrank_view_analytics', 'thinkrank_manage_credits', 'thinkrank_use_ai_features', ]; $roles = wp_roles(); foreach ($roles->roles as $role_name => $role_info) { $role = get_role($role_name); if ($role) { foreach ($capabilities as $cap) { $role->remove_cap($cap); } } } } } // Run the uninstaller ThinkRank_Uninstaller::uninstall();