Make WordPress Core


Ignore:
Timestamp:
12/03/2020 08:37:43 PM (6 years ago)
Author:
iandunn
Message:

Multisite: Cache absolute dirsize paths to avoid PHP 8 fatal.

r49212 greatly improved the performance of get_dirsize(), but also changed the structure of the data stored in the dirsize_cache transient. It stored relative paths instead of absolute ones, and also removed the unnecessary size array.

That difference in data structures led to a fatal error in the following environment:

  • PHP 8
  • Multisite
  • A custom WP_CONTENT_DIR which is not a child of WP's ABSPATH folder (e.g., ​Bedrock)
  • The upload_space_check_disabled option set to 0

After upgrading to WP 5.6, the dirsize_cache transient still had data in the old format. When wp-admin.php/index.php was visited, get_space_used() received an array instead of an int, and tried to divide it by another int. PHP 7 would silently cast the arguments to match data types, but ​PHP 8 throws a fatal error:

Uncaught TypeError: Unsupported operand types: array / int

recurse_dirsize() was using ABSPATH to convert the absolute paths to relative ones, but some upload locations are not located under ABSPATH. In those cases, $directory and $cache_path were identical, and that triggered the early return of the old array, instead of the expected int.

In order to avoid that, this commit restores the absolute paths, but without the size array. It also adds a type check when returning cached values. Using absolute paths without size has the result of overwriting the old data, so that it matches the new format. The type check and upgrade routine are additional safety measures.

Props peterwilsoncc, janthiel, helen, hellofromtonya, francina, pbiron.
Fixes #51913. See #19879.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/functions.php

    r49693 r49744  
    76277627function recurse_dirsize( $directory, $exclude = null, $max_execution_time = null, &$directory_cache = null ) {
    76287628        $directory  = untrailingslashit( $directory );
    7629         $cache_path = untrailingslashit( str_replace( ABSPATH, '', $directory ) );
    7630 
    76317629        $save_cache = false;
    76327630
    … …  
    76367634        }
    76377635
    7638         if ( isset( $directory_cache[ $cache_path ] ) ) {
    7639                 return $directory_cache[ $cache_path ];
     7636        if ( isset( $directory_cache[ $directory ] ) && is_int( $directory_cache[ $directory ] ) ) {
     7637                return $directory_cache[ $directory ];
    76407638        }
    76417639
    … …  
    77067704        }
    77077705
    7708         $directory_cache[ $cache_path ] = $size;
     7706        $directory_cache[ $directory ] = $size;
    77097707
    77107708        // Only write the transient on the top level call and not on recursive calls.
    … …  
    77327730        }
    77337731
    7734         $cache_path = untrailingslashit( str_replace( ABSPATH, '', $path ) );
    7735         unset( $directory_cache[ $cache_path ] );
    7736 
    7737         while ( DIRECTORY_SEPARATOR !== $cache_path && '.' !== $cache_path && '..' !== $cache_path ) {
    7738                 $cache_path = dirname( $cache_path );
    7739                 unset( $directory_cache[ $cache_path ] );
     7732        $path = untrailingslashit( $path );
     7733        unset( $directory_cache[ $path ] );
     7734
     7735        while ( DIRECTORY_SEPARATOR !== $path && '.' !== $path && '..' !== $path ) {
     7736                $path = dirname( $path );
     7737                unset( $directory_cache[ $path ] );
    77407738        }
    77417739
Note: See TracChangeset for help on using the changeset viewer.