diff --git a/src/wp-includes/ms-site.php b/src/wp-includes/ms-site.php index 342058156be01..4a35f82089e72 100644 --- a/src/wp-includes/ms-site.php +++ b/src/wp-includes/ms-site.php @@ -841,6 +841,17 @@ function wp_uninitialize_site( $site_id ) { $wpdb->query( "DROP TABLE IF EXISTS `$table`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared } + /* + * The site's options tables have just been dropped, so clear the caches that would + * otherwise keep serving the deleted site's data. `clean_blog_cache()` is deliberately + * network level only, and `switch_to_blog()` depends on these caches being cleared to + * surface the failure instead of silently continuing. + * + * @since 7.2.0 + */ + wp_cache_delete_multiple( array( 'alloptions', 'notoptions' ), 'options' ); + wp_cache_delete( 'is_blog_installed' ); + /** * Filters the upload base directory to delete when the site is deleted. * diff --git a/tests/phpunit/tests/multisite/site.php b/tests/phpunit/tests/multisite/site.php index 0fa6e4581669a..1a537e7cc6f2d 100644 --- a/tests/phpunit/tests/multisite/site.php +++ b/tests/phpunit/tests/multisite/site.php @@ -1619,6 +1619,60 @@ public function test_wp_delete_site_cleans_cache() { $this->assertNull( get_site( $site_id ) ); } + /** + * @ticket 29247 + */ + public function test_wp_delete_site_clears_site_option_caches() { + $site_id = self::factory()->blog->create(); + + switch_to_blog( $site_id ); + update_option( 'test_option', 'value' ); + wp_load_alloptions( true ); + is_blog_installed(); + + // The site's caches are now populated. + $this->assertIsArray( wp_cache_get( 'alloptions', 'options' ) ); + $this->assertNotEmpty( wp_cache_get( 'is_blog_installed' ) ); + restore_current_blog(); + + wp_delete_site( $site_id ); + + /* + * The site has no tables anymore, so suppress the roles and user switch the same way + * `wp_is_site_initialized()` does. + */ + remove_action( 'switch_blog', 'wp_switch_roles_and_user', 1 ); + switch_to_blog( $site_id ); + + $this->assertFalse( wp_cache_get( 'alloptions', 'options' ) ); + $this->assertFalse( wp_cache_get( 'notoptions', 'options' ) ); + $this->assertFalse( wp_cache_get( 'is_blog_installed' ) ); + + restore_current_blog(); + add_action( 'switch_blog', 'wp_switch_roles_and_user', 1, 2 ); + } + + /** + * @ticket 29247 + */ + public function test_clean_blog_cache_does_not_clear_option_caches() { + $site_id = self::factory()->blog->create(); + + switch_to_blog( $site_id ); + update_option( 'test_option', 'value' ); + wp_load_alloptions( true ); + + $this->assertIsArray( wp_cache_get( 'alloptions', 'options' ) ); + restore_current_blog(); + + clean_blog_cache( $site_id ); + + // `clean_blog_cache()` is network level only, per the discussion on the ticket. + switch_to_blog( $site_id ); + $this->assertIsArray( wp_cache_get( 'alloptions', 'options' ) ); + restore_current_blog(); + } + /** * @ticket 40364 */