esc_like( 'advisorppc_' ) . '%'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-off cleanup on uninstall; core has no prefix-matched option API. $option_names = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s", $option_like ) ); if ( is_array( $option_names ) ) { foreach ( $option_names as $option_name ) { delete_option( $option_name ); } } // Transients, including the pending connect state and their timeout rows. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-off cleanup on uninstall. $transient_names = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", $wpdb->esc_like( '_transient_advisorppc_' ) . '%', $wpdb->esc_like( '_site_transient_advisorppc_' ) . '%' ) ); if ( is_array( $transient_names ) ) { foreach ( $transient_names as $transient_name ) { if ( 0 === strpos( $transient_name, '_site_transient_' ) ) { delete_site_transient( substr( $transient_name, strlen( '_site_transient_' ) ) ); continue; } delete_transient( substr( $transient_name, strlen( '_transient_' ) ) ); } } // Timeout siblings, in case a transient row was removed by hand first. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-off cleanup on uninstall. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", $wpdb->esc_like( '_transient_timeout_advisorppc_' ) . '%', $wpdb->esc_like( '_site_transient_timeout_advisorppc_' ) . '%' ) ); // Scheduled events registered by the plugin or by any of its modules. $hooks = array( 'advisorppc_flush_queue', 'advisorppc_refresh_health' ); foreach ( $hooks as $hook ) { wp_clear_scheduled_hook( $hook ); } // Post meta and user meta written by the plugin. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-off cleanup on uninstall. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE %s", $wpdb->esc_like( '_advisorppc_' ) . '%' ) ); /* * WooCommerce order meta. With High Performance Order Storage enabled the * order's meta is NOT in postmeta at all, it is in its own table, so the * sweep above misses every `_advisorppc_purchase_sent` and stored click ID * on a modern store. The table is only touched when it actually exists. */ $hpos_meta = $wpdb->prefix . 'wc_orders_meta'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-off cleanup on uninstall. $hpos_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $hpos_meta ) ) ); if ( $hpos_exists === $hpos_meta ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-off cleanup on uninstall. $wpdb->query( $wpdb->prepare( 'DELETE FROM %i WHERE meta_key LIKE %s', $hpos_meta, $wpdb->esc_like( '_advisorppc_' ) . '%' ) ); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-off cleanup on uninstall. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE %s", $wpdb->esc_like( 'advisorppc_' ) . '%' ) ); /* * The outbound event queue's own bookkeeping. Named explicitly as well as * being covered by the prefix sweep above, because a reviewer greps for * the literal table and option a plugin is known to create. */ delete_option( 'advisorppc_queue_db_version' ); $advisorppc_queue_table = $wpdb->prefix . 'advisorppc_queue'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange -- Uninstall removes the table this plugin created. $wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %i', $advisorppc_queue_table ) ); /* * Any other custom table a module created. All of them use the same * prefix. Each name is taken from the list the database itself reported * and bound with the %i identifier placeholder (WordPress 6.2 and later). */ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-off cleanup on uninstall. $tables = $wpdb->get_col( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $wpdb->prefix . 'advisorppc_' ) . '%' ) ); if ( is_array( $tables ) ) { foreach ( $tables as $table ) { if ( ! preg_match( '/^[A-Za-z0-9_]+$/', $table ) ) { continue; } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange -- Uninstall removes tables this plugin created; the name comes from SHOW TABLES. $wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %i', $table ) ); } } } if ( is_multisite() ) { $advisorppc_sites = get_sites( array( 'fields' => 'ids', 'number' => 0, ) ); foreach ( $advisorppc_sites as $advisorppc_site_id ) { switch_to_blog( $advisorppc_site_id ); advisorppc_uninstall_site(); restore_current_blog(); } } else { advisorppc_uninstall_site(); }