Delete in the WordPress admin. * Removes all plugin database tables and WordPress options. * * @package AdsdReferralCouponcoreEcosystem */ // WordPress uninstall guard — must be present. if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) { exit; } global $wpdb; $adsd_rcc_prefix = $wpdb->prefix . 'adsd_rcc_'; // Drop all plugin tables in reverse dependency order. $adsd_rcc_tables = array( 'logs', 'passkeys', 'email_templates', 'settings', 'pending_verifications', 'rewards', 'referrals', 'referral_links', ); // Intentional: uninstall.php runs only on explicit admin-initiated plugin deletion. // Table suffixes come from a hard-coded whitelist defined above — never from user input. // DROP TABLE does not support $wpdb->prepare() placeholders; this is the accepted WP pattern. // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter -- DROP TABLE cannot use prepare(); table names from $wpdb->prefix + hard-coded whitelist, never user input. foreach ( $adsd_rcc_tables as $adsd_rcc_suffix ) { // Build the full table name from the trusted prefix (WP-managed) and a hard-coded suffix string. $adsd_rcc_table_name = $adsd_rcc_prefix . $adsd_rcc_suffix; $wpdb->query( "DROP TABLE IF EXISTS `{$adsd_rcc_table_name}`" ); } // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter // Remove all plugin WordPress options. $adsd_rcc_options = array( 'adsd_rcc_version', 'adsd_rcc_flush_rewrite_rules', ); foreach ( $adsd_rcc_options as $adsd_rcc_option ) { delete_option( $adsd_rcc_option ); } // Clear any scheduled cron events. wp_clear_scheduled_hook( 'adsd_rcc_expire_old_links' );