'ids', 'number' => self::SITES_PER_BATCH, 'offset' => $offset, ) ); foreach ( $site_ids as $site_id ) { switch_to_blog( (int) $site_id ); self::clean_site(); restore_current_blog(); } $offset += self::SITES_PER_BATCH; $batch_size = count( $site_ids ); } while ( self::SITES_PER_BATCH === $batch_size ); } /** * Cleans the current site. */ public static function clean_site(): void { global $wpdb; foreach ( Plugin::CRON_HOOKS as $hook ) { wp_unschedule_hook( $hook ); } // Options and transients carry the `showfm_` prefix. Direct queries are the only way // to find options-table transients whose names are hashed. Deleting the namespace // option also makes retained external-cache entries unreachable after reinstall. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Uninstall cleanup. $options = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s OR option_name LIKE %s", $wpdb->esc_like( 'showfm_' ) . '%', $wpdb->esc_like( '_transient_showfm_' ) . '%', $wpdb->esc_like( '_transient_timeout_showfm_' ) . '%' ) ); foreach ( $options as $option ) { delete_option( $option ); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Uninstall cleanup. $meta_keys = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT meta_key FROM {$wpdb->postmeta} WHERE meta_key LIKE %s", $wpdb->esc_like( '_showfm_' ) . '%' ) ); foreach ( $meta_keys as $meta_key ) { delete_post_meta_by_key( $meta_key ); } } }