diff --git a/src/wp-admin/includes/upgrade.php b/src/wp-admin/includes/upgrade.php index 9014d9710018..72f3536578a8 100644 --- a/src/wp-admin/includes/upgrade.php +++ b/src/wp-admin/includes/upgrade.php @@ -874,7 +874,7 @@ function upgrade_all() { upgrade_550(); } - if ( $wp_current_db_version < 49632 ) { + if ( $wp_current_db_version < 49735 ) { upgrade_560(); } @@ -2274,6 +2274,10 @@ function upgrade_560() { */ save_mod_rewrite_rules(); } + + if ( $wp_current_db_version < 49735 ) { + delete_transient( 'dirsize_cache' ); + } } /** diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 3b0dfda4bf23..7219d768a9f3 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -7626,8 +7626,6 @@ function get_dirsize( $directory, $max_execution_time = null ) { */ function recurse_dirsize( $directory, $exclude = null, $max_execution_time = null, &$directory_cache = null ) { $directory = untrailingslashit( $directory ); - $cache_path = untrailingslashit( str_replace( ABSPATH, '', $directory ) ); - $save_cache = false; if ( ! isset( $directory_cache ) ) { @@ -7635,8 +7633,8 @@ function recurse_dirsize( $directory, $exclude = null, $max_execution_time = nul $save_cache = true; } - if ( isset( $directory_cache[ $cache_path ] ) ) { - return $directory_cache[ $cache_path ]; + if ( isset( $directory_cache[ $directory ] ) && is_int( $directory_cache[ $directory ] ) ) { + return $directory_cache[ $directory ]; } if ( ! file_exists( $directory ) || ! is_dir( $directory ) || ! is_readable( $directory ) ) { @@ -7705,7 +7703,7 @@ function recurse_dirsize( $directory, $exclude = null, $max_execution_time = nul } } - $directory_cache[ $cache_path ] = $size; + $directory_cache[ $directory ] = $size; // Only write the transient on the top level call and not on recursive calls. if ( $save_cache ) { @@ -7731,12 +7729,12 @@ function clean_dirsize_cache( $path ) { return; } - $cache_path = untrailingslashit( str_replace( ABSPATH, '', $path ) ); - unset( $directory_cache[ $cache_path ] ); + $path = untrailingslashit( $path ); + unset( $directory_cache[ $path ] ); - while ( DIRECTORY_SEPARATOR !== $cache_path && '.' !== $cache_path && '..' !== $cache_path ) { - $cache_path = dirname( $cache_path ); - unset( $directory_cache[ $cache_path ] ); + while ( DIRECTORY_SEPARATOR !== $path && '.' !== $path && '..' !== $path ) { + $path = dirname( $path ); + unset( $directory_cache[ $path ] ); } set_transient( 'dirsize_cache', $directory_cache ); diff --git a/src/wp-includes/version.php b/src/wp-includes/version.php index 624d93fca9af..d756d5b7db08 100644 --- a/src/wp-includes/version.php +++ b/src/wp-includes/version.php @@ -20,7 +20,7 @@ * * @global int $wp_db_version */ -$wp_db_version = 49632; +$wp_db_version = 49735; /** * Holds the TinyMCE version. diff --git a/tests/phpunit/tests/multisite/cleanDirsizeCache.php b/tests/phpunit/tests/multisite/cleanDirsizeCache.php index 96598198df1f..cedafb1090d7 100644 --- a/tests/phpunit/tests/multisite/cleanDirsizeCache.php +++ b/tests/phpunit/tests/multisite/cleanDirsizeCache.php @@ -93,7 +93,7 @@ function test_clean_dirsize_cache_file_input_mock() { } $upload_dir = wp_upload_dir(); - $cache_key_prefix = untrailingslashit( str_replace( ABSPATH, '', $upload_dir['basedir'] ) ); + $cache_key_prefix = untrailingslashit( $upload_dir['basedir'] ); // Clear the dirsize cache. delete_transient( 'dirsize_cache' ); @@ -141,7 +141,7 @@ function test_clean_dirsize_cache_folder_input_mock() { } $upload_dir = wp_upload_dir(); - $cache_key_prefix = untrailingslashit( str_replace( ABSPATH, '', $upload_dir['basedir'] ) ); + $cache_key_prefix = untrailingslashit( $upload_dir['basedir'] ); // Clear the dirsize cache. delete_transient( 'dirsize_cache' ); @@ -205,7 +205,7 @@ function test_get_dirsize_cache_in_recurse_dirsize_upload() { $this->assertSame( $size, $calc_size ); // `dirsize_cache` should now be filled after upload and recurse_dirsize() call. - $cache_path = untrailingslashit( str_replace( ABSPATH, '', $upload_dir['path'] ) ); + $cache_path = untrailingslashit( $upload_dir['path'] ); $this->assertSame( true, is_array( get_transient( 'dirsize_cache' ) ) ); $this->assertSame( $size, get_transient( 'dirsize_cache' )[ $cache_path ] ); @@ -233,15 +233,98 @@ function _filter_pre_recurse_dirsize() { } function _get_mock_dirsize_cache_for_site( $site_id ) { + $prefix = wp_upload_dir()['basedir']; + + return array( + "$prefix/2/2" => 22, + "$prefix/2/1" => 21, + "$prefix/2" => 2, + "$prefix/1/3" => 13, + "$prefix/1/2" => 12, + "$prefix/1/1" => 11, + "$prefix/1" => 1, + "$prefix/custom_directory" => 42, + ); + } + + /* + * Test that 5.6+ gracefully handles the old 5.5 transient structure. + * + * @ticket 51913 + */ + function test_5_5_transient_structure_compat() { + $blog_id = self::factory()->blog->create(); + switch_to_blog( $blog_id ); + + /* + * Our comparison of space relies on an initial value of 0. If a previous test has failed + * or if the `src` directory already contains a directory with site content, then the initial + * expectation will be polluted. We create sites until an empty one is available. + */ + while ( 0 !== get_space_used() ) { + restore_current_blog(); + $blog_id = self::factory()->blog->create(); + switch_to_blog( $blog_id ); + } + + // Clear the dirsize cache. + delete_transient( 'dirsize_cache' ); + + // Set the dirsize cache to our mock. + set_transient( 'dirsize_cache', $this->_get_mock_5_5_dirsize_cache( $blog_id ) ); + + $upload_dir = wp_upload_dir(); + + /* + * The cached size should be ignored, because it's in the old format. The function + * will try to fetch a live value, but in this case the folder doesn't actually + * exist on disk, so the function should fail. + */ + $this->assertSame( false, recurse_dirsize( $upload_dir['basedir'] . '/2/1' ) ); + + /* + * Now that it's confirmed that old cached values aren't being returned, create the + * folder on disk, so that the the rest of the function can be tested. + */ + wp_mkdir_p( $upload_dir['basedir'] . '/2/1' ); + $filename = $upload_dir['basedir'] . '/2/1/this-needs-to-exist.txt'; + file_put_contents( $filename, 'this file is 21 bytes' ); + + // Clear the dirsize cache. + delete_transient( 'dirsize_cache' ); + + // Set the dirsize cache to our mock. + set_transient( 'dirsize_cache', $this->_get_mock_5_5_dirsize_cache( $blog_id ) ); + + /* + * Now that the folder exists, the old cached value should be overwritten + * with the size, using the current format. + */ + $this->assertSame( 21, recurse_dirsize( $upload_dir['basedir'] . '/2/1' ) ); + $this->assertSame( 21, get_transient( 'dirsize_cache' )[ $upload_dir['basedir'] . '/2/1' ] ); + + // No cache match on non existing directory should return false. + $this->assertSame( false, recurse_dirsize( $upload_dir['basedir'] . '/does_not_exist' ) ); + + // Cleanup. + $this->remove_added_uploads(); + rmdir( $upload_dir['basedir'] . '/2/1' ); + + restore_current_blog(); + } + + function _get_mock_5_5_dirsize_cache( $site_id ) { + $prefix = untrailingslashit( wp_upload_dir()['basedir'] ); + return array( - "wp-content/uploads/sites/$site_id/2/2" => 22, - "wp-content/uploads/sites/$site_id/2/1" => 21, - "wp-content/uploads/sites/$site_id/2" => 2, - "wp-content/uploads/sites/$site_id/1/3" => 13, - "wp-content/uploads/sites/$site_id/1/2" => 12, - "wp-content/uploads/sites/$site_id/1/1" => 11, - "wp-content/uploads/sites/$site_id/1" => 1, - "wp-content/uploads/sites/$site_id/custom_directory" => 42, + "$prefix/2/2" => array( 'size' => 22 ), + "$prefix/2/1" => array( 'size' => 21 ), + "$prefix/2" => array( 'size' => 2 ), + "$prefix/1/3" => array( 'size' => 13 ), + "$prefix/1/2" => array( 'size' => 12 ), + "$prefix/1/1" => array( 'size' => 11 ), + "$prefix/1" => array( 'size' => 1 ), + "$prefix/custom_directory" => array( 'size' => 42 ), ); } }