diff --git a/src/wp-admin/includes/class-wp-upgrader.php b/src/wp-admin/includes/class-wp-upgrader.php index ba27113ff73de..d5eeae81afd98 100644 --- a/src/wp-admin/includes/class-wp-upgrader.php +++ b/src/wp-admin/includes/class-wp-upgrader.php @@ -746,6 +746,8 @@ public function install_package( $args = array() ) { * * @since 2.8.0 * + * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. + * * @param array $options { * Array or string of arguments for upgrading/installing a package. * @@ -772,6 +774,7 @@ public function install_package( $args = array() ) { * or false if unable to connect to the filesystem. */ public function run( $options ) { + global $wp_filesystem; $defaults = array( 'package' => '', // Please always pass this. @@ -906,6 +909,13 @@ public function run( $options ) { ) ); + if ( is_wp_error( $result ) && $options['clear_working'] + && $wp_filesystem instanceof WP_Filesystem_Base + && $wp_filesystem->exists( $working_dir ) + ) { + $wp_filesystem->delete( $working_dir, true ); + } + /** * Filters the result of WP_Upgrader::install_package(). * diff --git a/tests/phpunit/tests/admin/wpUpgrader.php b/tests/phpunit/tests/admin/wpUpgrader.php index ffea594ae87bc..bce2f916910d2 100644 --- a/tests/phpunit/tests/admin/wpUpgrader.php +++ b/tests/phpunit/tests/admin/wpUpgrader.php @@ -1464,6 +1464,62 @@ public function test_run_should_return_false_when_requesting_filesystem_credenti $this->assertFalse( self::$instance->run( array() ) ); } + /** + * Tests that `WP_Upgrader::run()` removes the working directory after an + * installation failure. + * + * @ticket 44710 + * + * @covers WP_Upgrader::run + */ + public function test_run_should_clear_working_directory_after_installation_failure() { + $working_directory = '/wp-content/upgrade/test-package'; + $expected = new WP_Error( 'incompatible_archive' ); + + self::$wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'exists' ) + ->with( $working_directory ) + ->willReturn( true ); + + self::$wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'delete' ) + ->with( $working_directory, true ); + // Note: setMethods() is deprecated in PHPUnit 9, but still supported. + $instance = $this->getMockBuilder( 'WP_Upgrader' ) + ->setConstructorArgs( array( self::$upgrader_skin_mock ) ) + ->setMethods( array( 'fs_connect', 'download_package', 'unpack_package', 'install_package' ) ) + ->getMock(); + + $instance + ->method( 'fs_connect' ) + ->willReturn( true ); + + $instance + ->method( 'download_package' ) + ->willReturn( '/tmp/test-package.zip' ); + + $instance + ->method( 'unpack_package' ) + ->willReturn( $working_directory ); + + $instance + ->method( 'install_package' ) + ->willReturn( $expected ); + + $actual = $instance->run( + array( + 'package' => '/tmp/test-package.zip', + 'destination' => WP_PLUGIN_DIR, + 'clear_working' => true, + 'is_multi' => true, + ) + ); + + $this->assertSame( $expected, $actual ); + } + /** * Tests that `WP_Upgrader::maintenance_mode()` removes the `.maintenance` file. *