diff --git a/features/site.feature b/features/site.feature index 8e626db35..aea85e9a5 100644 --- a/features/site.feature +++ b/features/site.feature @@ -78,6 +78,42 @@ Feature: Manage sites in a multisite installation When I try the previous command again Then the return code should be 1 + @skip-windows + Scenario: Delete a site by id and remove all prefixed tables + Given a WP multisite subdirectory install + + When I run `wp site create --slug=first --porcelain` + Then STDOUT should be a number + And save STDOUT as {SITE_ID} + And I try `wp db query "CREATE TABLE wp_{SITE_ID}_custom_data (id INTEGER PRIMARY KEY);"` + And I run `wp site delete {SITE_ID} --yes --delete-tables-with-prefix` + And STDOUT should contain: + """ + Success: The site at ' + """ + + When I try `wp db query "CREATE TABLE wp_{SITE_ID}_custom_data (id INTEGER PRIMARY KEY);"` + Then STDOUT should be empty + And the return code should be 0 + And I try `wp db query "DROP TABLE wp_{SITE_ID}_custom_data;"` + And the return code should be 0 + + @skip-windows + Scenario: Deleting a site cannot combine keep-tables with delete-tables-with-prefix + Given a WP multisite subdirectory install + + When I run `wp site create --slug=first --porcelain` + Then STDOUT should be a number + And save STDOUT as {SITE_ID} + + When I try `wp site delete {SITE_ID} --yes --keep-tables --delete-tables-with-prefix` + Then STDERR should be: + """ + Error: The '--keep-tables' and '--delete-tables-with-prefix' flags cannot be used together. + """ + And STDOUT should be empty + And the return code should be 1 + @skip-windows Scenario: Filter site list Given a WP multisite install diff --git a/src/Site_Command.php b/src/Site_Command.php index 5a231d7a9..472000c9e 100644 --- a/src/Site_Command.php +++ b/src/Site_Command.php @@ -288,6 +288,9 @@ public function empty_( $args, $assoc_args ) { * [--keep-tables] * : Delete the blog from the list, but don't drop its tables. * + * [--delete-tables-with-prefix] + * : Delete all tables with the site's database table prefix after deleting the site. + * * ## EXAMPLES * * $ wp site delete 123 @@ -299,11 +302,18 @@ public function delete( $args, $assoc_args ) { WP_CLI::error( 'This is not a multisite installation.' ); } + if ( Utils\get_flag_value( $assoc_args, 'keep-tables' ) && Utils\get_flag_value( $assoc_args, 'delete-tables-with-prefix' ) ) { + WP_CLI::error( "The '--keep-tables' and '--delete-tables-with-prefix' flags cannot be used together." ); + } + if ( isset( $assoc_args['slug'] ) ) { $blog_id = get_id_from_blogname( $assoc_args['slug'] ); if ( null === $blog_id ) { WP_CLI::error( sprintf( 'Could not find site with slug \'%s\'.', $assoc_args['slug'] ) ); } + if ( is_main_site( $blog_id ) ) { + WP_CLI::error( 'You cannot delete the root site.' ); + } $blog = get_blog_details( $blog_id ); } else { if ( empty( $args ) ) { @@ -327,11 +337,49 @@ public function delete( $args, $assoc_args ) { WP_CLI::confirm( "Are you sure you want to delete the '{$site_url}' site?", $assoc_args ); - wpmu_delete_blog( (int) $blog->blog_id, ! Utils\get_flag_value( $assoc_args, 'keep-tables' ) ); + $did_delete = wpmu_delete_blog( (int) $blog->blog_id, ! Utils\get_flag_value( $assoc_args, 'keep-tables' ) ); + if ( false === $did_delete ) { + WP_CLI::error( "The site at '{$site_url}' could not be deleted." ); + } + + if ( Utils\get_flag_value( $assoc_args, 'delete-tables-with-prefix' ) ) { + $this->drop_tables_with_prefix( (int) $blog->blog_id ); + } WP_CLI::success( "The site at '{$site_url}' was deleted." ); } + /** + * Drops all database tables for a site prefix. + * + * @param int $blog_id Site ID. + */ + private function drop_tables_with_prefix( $blog_id ) { + global $wpdb; + + $blog_prefix = $wpdb->get_blog_prefix( $blog_id ); + if ( is_main_site( $blog_id ) || $blog_prefix === $wpdb->base_prefix ) { + WP_CLI::error( 'You cannot drop tables for the root site.' ); + } + + $prefix_like = $wpdb->esc_like( $blog_prefix ) . '%'; + $tables = $wpdb->get_col( $wpdb->prepare( 'SHOW TABLES LIKE %s', $prefix_like ) ); + + if ( empty( $tables ) ) { + return; + } + + $tables = array_map( + static function ( $table ) { + return '`' . str_replace( '`', '``', $table ) . '`'; + }, + $tables + ); + + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table identifiers are escaped and cannot be passed as placeholders. + $wpdb->query( 'DROP TABLE IF EXISTS ' . implode( ', ', $tables ) ); + } + /** * Gets details about a site in a multisite installation. *