Make WordPress Core


Ignore:
Timestamp:
12/03/2020 08:37:43 PM (3 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.