Site health:
- Prevent fatal errors from timeouts on the Tools => Site Health => Info tab. - Use the `get_dirsize()` and `recurse_dirsize()` functions to calculate directory sizes. The results are cached. - Introduce "timeout protection" in `recurse_dirsize()`. Props pento, Clorith, xkon, afercia, jeremyfelt, azaozz. Fixes #46645. git-svn-id: https://develop.svn.wordpress.org/trunk@45104 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
f6f02a327f
commit
d5d4979921
@ -32,7 +32,6 @@ class WP_Debug_Data {
|
||||
*/
|
||||
static function debug_data( $locale = null ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( ! empty( $locale ) ) {
|
||||
// Change the language used for translations
|
||||
if ( function_exists( 'switch_to_locale' ) ) {
|
||||
@ -312,21 +311,33 @@ class WP_Debug_Data {
|
||||
);
|
||||
}
|
||||
|
||||
$size_db = WP_Debug_Data::get_database_size();
|
||||
|
||||
// Go through the various installation directories and calculate their sizes.
|
||||
$uploads_dir = wp_upload_dir();
|
||||
$inaccurate = false;
|
||||
|
||||
/*
|
||||
* We will be using the PHP max execution time to prevent the size calculations
|
||||
* from causing a timeout. We provide a default value of 30 seconds, as some
|
||||
* from causing a timeout. The default value is 30 seconds, and some
|
||||
* hosts do not allow you to read configuration values.
|
||||
*/
|
||||
$max_execution_time = 30;
|
||||
$start_execution_time = microtime( true );
|
||||
$max_execution_time = 30;
|
||||
|
||||
if ( function_exists( 'ini_get' ) ) {
|
||||
$max_execution_time = ini_get( 'max_execution_time' );
|
||||
}
|
||||
|
||||
// Here 20 seconds is a "sensible default" for how long to make the user wait for the directory size calculation.
|
||||
// When testing 20 seconds seem enough in nearly all cases. The remaining edge cases are likely testing or development sites
|
||||
// that have very large number of files, for example `node_modules` in plugins or themes, etc.
|
||||
if ( $max_execution_time > 20 ) {
|
||||
$max_execution_time = 20;
|
||||
} elseif ( $max_execution_time > 2 ) {
|
||||
// If the max_execution_time is set to lower than 20 seconds, reduce it a bit to prevent
|
||||
// edge-case timeouts that may happen after the size loop has finished running.
|
||||
$max_execution_time -= 1;
|
||||
}
|
||||
|
||||
$size_directories = array(
|
||||
'wordpress' => array(
|
||||
'path' => ABSPATH,
|
||||
@ -346,35 +357,44 @@ class WP_Debug_Data {
|
||||
),
|
||||
);
|
||||
|
||||
$timeout = __( 'The directory size calculation has timed out. Usually caused by a very large number of sub-directories and files.' );
|
||||
$inaccessible = __( 'The size cannot be calculated. The directory is not accessible. Usually caused by invalid permissions.' );
|
||||
$size_total = 0;
|
||||
|
||||
// Loop over all the directories we want to gather the sizes for.
|
||||
foreach ( $size_directories as $size => $attributes ) {
|
||||
/*
|
||||
* We run a helper function with a RecursiveIterator, which
|
||||
* may throw an exception if it can't access directories.
|
||||
*
|
||||
* If a failure is detected we mark the result as inaccurate.
|
||||
*/
|
||||
try {
|
||||
$calculated_size = WP_Debug_data::get_directory_size( $attributes['path'], $max_execution_time, $start_execution_time );
|
||||
$dir_size = null; // Default to timeout.
|
||||
|
||||
$size_directories[ $size ]['size'] = $calculated_size;
|
||||
|
||||
/*
|
||||
* If the size returned is -1, this means execution has
|
||||
* exceeded the maximum execution time, also denoting an
|
||||
* inaccurate value in the end.
|
||||
*/
|
||||
if ( -1 === $calculated_size ) {
|
||||
$inaccurate = true;
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$inaccurate = true;
|
||||
if ( microtime( true ) - WP_START_TIMESTAMP < $max_execution_time ) {
|
||||
$dir_size = get_dirsize( $attributes['path'], $max_execution_time );
|
||||
}
|
||||
|
||||
if ( $dir_size === false ) {
|
||||
// Error reading
|
||||
$dir_size = $inaccessible;
|
||||
$size_total = null;
|
||||
} elseif ( $dir_size === null ) {
|
||||
// Timeout
|
||||
$dir_size = $timeout;
|
||||
$size_total = null;
|
||||
} else {
|
||||
$is_subdir = ( strpos( $size_directories[ $size ]['path'], ABSPATH ) === 0 );
|
||||
|
||||
if ( $size_total !== null && ( $size === 'wordpress' || ! $is_subdir ) ) {
|
||||
$size_total += $dir_size;
|
||||
}
|
||||
|
||||
$dir_size = size_format( $dir_size, 2 );
|
||||
}
|
||||
|
||||
$size_directories[ $size ]['size'] = $dir_size;
|
||||
}
|
||||
|
||||
$size_db = WP_Debug_Data::get_database_size();
|
||||
|
||||
$size_total = $size_directories['wordpress']['size'] + $size_db;
|
||||
if ( $size_total !== null && $size_db > 0 ) {
|
||||
$size_total = size_format( $size_total + $size_db, 2 );
|
||||
} else {
|
||||
$size_total = __( 'Total size is not available. Some errors were encountered when determining the size of your installation.' );
|
||||
}
|
||||
|
||||
$info['wp-paths-sizes']['fields'] = array(
|
||||
array(
|
||||
@ -383,7 +403,7 @@ class WP_Debug_Data {
|
||||
),
|
||||
array(
|
||||
'label' => __( 'Uploads Directory Size' ),
|
||||
'value' => ( -1 === $size_directories['uploads']['size'] ? __( 'Unable to determine the size of this directory' ) : size_format( $size_directories['uploads']['size'], 2 ) ),
|
||||
'value' => $size_directories['uploads']['size'],
|
||||
),
|
||||
array(
|
||||
'label' => __( 'Themes Directory Location' ),
|
||||
@ -395,7 +415,7 @@ class WP_Debug_Data {
|
||||
),
|
||||
array(
|
||||
'label' => __( 'Themes Directory Size' ),
|
||||
'value' => ( -1 === $size_directories['themes']['size'] ? __( 'Unable to determine the size of this directory' ) : size_format( $size_directories['themes']['size'], 2 ) ),
|
||||
'value' => $size_directories['themes']['size'],
|
||||
),
|
||||
array(
|
||||
'label' => __( 'Plugins Directory Location' ),
|
||||
@ -403,7 +423,7 @@ class WP_Debug_Data {
|
||||
),
|
||||
array(
|
||||
'label' => __( 'Plugins Directory Size' ),
|
||||
'value' => ( -1 === $size_directories['plugins']['size'] ? __( 'Unable to determine the size of this directory' ) : size_format( $size_directories['plugins']['size'], 2 ) ),
|
||||
'value' => $size_directories['plugins']['size'],
|
||||
),
|
||||
array(
|
||||
'label' => __( 'WordPress Directory Location' ),
|
||||
@ -411,7 +431,7 @@ class WP_Debug_Data {
|
||||
),
|
||||
array(
|
||||
'label' => __( 'WordPress Directory Size' ),
|
||||
'value' => size_format( $size_directories['wordpress']['size'], 2 ),
|
||||
'value' => $size_directories['wordpress']['size'],
|
||||
),
|
||||
array(
|
||||
'label' => __( 'Database size' ),
|
||||
@ -419,11 +439,7 @@ class WP_Debug_Data {
|
||||
),
|
||||
array(
|
||||
'label' => __( 'Total installation size' ),
|
||||
'value' => sprintf(
|
||||
'%s%s',
|
||||
size_format( $size_total, 2 ),
|
||||
( false === $inaccurate ? '' : __( '- Some errors, likely caused by invalid permissions, were encountered when determining the size of your installation. This means the values represented may be inaccurate.' ) )
|
||||
),
|
||||
'value' => $size_total,
|
||||
),
|
||||
);
|
||||
|
||||
@ -934,36 +950,6 @@ class WP_Debug_Data {
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the size of a directory, including all subdirectories.
|
||||
*
|
||||
* @since 5.2.0
|
||||
*
|
||||
* @param string $path The directory to check.
|
||||
* @param string|int $max_execution_time How long a PHP script can run on this host.
|
||||
* @param float $start_execution_time When we started executing this section of the script.
|
||||
*
|
||||
* @return int The directory size, in bytes.
|
||||
*/
|
||||
public static function get_directory_size( $path, $max_execution_time, $start_execution_time ) {
|
||||
$size = 0;
|
||||
|
||||
foreach ( new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $path ) ) as $file ) {
|
||||
// Check if the maximum execution time is a value considered "infinite".
|
||||
if ( 0 !== $max_execution_time && -1 !== $max_execution_time ) {
|
||||
$runtime = ( microtime( true ) - $start_execution_time );
|
||||
|
||||
// If the script has been running as long, or longer, as it is allowed, return a failure message.
|
||||
if ( $runtime >= $max_execution_time ) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
$size += $file->getSize();
|
||||
}
|
||||
|
||||
return $size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the total size of all the database tables for the active database user.
|
||||
*
|
||||
@ -982,6 +968,6 @@ class WP_Debug_Data {
|
||||
}
|
||||
}
|
||||
|
||||
return $size;
|
||||
return (int) $size;
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,11 @@ function wp_initial_constants() {
|
||||
define( 'TB_IN_BYTES', 1024 * GB_IN_BYTES );
|
||||
/**#@-*/
|
||||
|
||||
// Start of run timestamp.
|
||||
if ( ! defined( 'WP_START_TIMESTAMP' ) ) {
|
||||
define( 'WP_START_TIMESTAMP', microtime( true ) );
|
||||
}
|
||||
|
||||
$current_limit = @ini_get( 'memory_limit' );
|
||||
$current_limit_int = wp_convert_hr_to_bytes( $current_limit );
|
||||
|
||||
|
@ -7006,3 +7006,103 @@ function wp_direct_php_update_button() {
|
||||
);
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of a directory.
|
||||
*
|
||||
* A helper function that is used primarily to check whether
|
||||
* a blog has exceeded its allowed upload space.
|
||||
*
|
||||
* @since MU (3.0.0)
|
||||
*
|
||||
* @param string $directory Full path of a directory.
|
||||
* @param int $max_execution_time Maximum time to run before giving up. In seconds.
|
||||
* The timeout is global and is measured from the moment WordPress started to load.
|
||||
* @return int|false|null Size in MB if a valid directory. False if not. Null if timeout.
|
||||
*/
|
||||
function get_dirsize( $directory, $max_execution_time = null ) {
|
||||
$dirsize = get_transient( 'dirsize_cache' );
|
||||
|
||||
if ( is_array( $dirsize ) && isset( $dirsize[ $directory ]['size'] ) ) {
|
||||
return $dirsize[ $directory ]['size'];
|
||||
}
|
||||
|
||||
if ( ! is_array( $dirsize ) ) {
|
||||
$dirsize = array();
|
||||
}
|
||||
|
||||
// Exclude individual site directories from the total when checking the main site of a network
|
||||
// as they are subdirectories and should not be counted.
|
||||
if ( is_multisite() && is_main_site() ) {
|
||||
$dirsize[ $directory ]['size'] = recurse_dirsize( $directory, $directory . '/sites', $max_execution_time );
|
||||
} else {
|
||||
$dirsize[ $directory ]['size'] = recurse_dirsize( $directory, null, $max_execution_time );
|
||||
}
|
||||
|
||||
set_transient( 'dirsize_cache', $dirsize, HOUR_IN_SECONDS );
|
||||
return $dirsize[ $directory ]['size'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of a directory recursively.
|
||||
*
|
||||
* Used by get_dirsize() to get a directory's size when it contains
|
||||
* other directories.
|
||||
*
|
||||
* @since MU (3.0.0)
|
||||
* @since 4.3.0 $exclude parameter added.
|
||||
*
|
||||
* @param string $directory Full path of a directory.
|
||||
* @param string $exclude Optional. Full path of a subdirectory to exclude from the total.
|
||||
* @param int $max_execution_time Maximum time to run before giving up. In seconds.
|
||||
* The timeout is global and is measured from the moment WordPress started to load.
|
||||
* @return int|false|null Size in MB if a valid directory. False if not. Null if timeout.
|
||||
*/
|
||||
function recurse_dirsize( $directory, $exclude = null, $max_execution_time = null ) {
|
||||
$size = 0;
|
||||
|
||||
$directory = untrailingslashit( $directory );
|
||||
|
||||
if ( ! file_exists( $directory ) || ! is_dir( $directory ) || ! is_readable( $directory ) || $directory === $exclude ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $max_execution_time ) {
|
||||
// Keep the previous behavior but attempt to prevent fatal errors from timeout.
|
||||
if ( function_exists( 'ini_get' ) ) {
|
||||
$max_execution_time = ini_get( 'max_execution_time' );
|
||||
} else {
|
||||
// Use PHP default.
|
||||
$max_execution_time = 30;
|
||||
}
|
||||
|
||||
// Leave 1 second "buffer" for other operations if $max_execution_time has reasonable value.
|
||||
if ( $max_execution_time > 10 ) {
|
||||
$max_execution_time -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $handle = opendir( $directory ) ) {
|
||||
while ( ( $file = readdir( $handle ) ) !== false ) {
|
||||
$path = $directory . '/' . $file;
|
||||
if ( $file != '.' && $file != '..' ) {
|
||||
if ( is_file( $path ) ) {
|
||||
$size += filesize( $path );
|
||||
} elseif ( is_dir( $path ) ) {
|
||||
$handlesize = recurse_dirsize( $path, $exclude, $max_execution_time );
|
||||
if ( $handlesize > 0 ) {
|
||||
$size += $handlesize;
|
||||
}
|
||||
}
|
||||
|
||||
if ( microtime( true ) - WP_START_TIMESTAMP > $max_execution_time ) {
|
||||
// Time exceeded. Give up instead of risking a fatal timeout.
|
||||
$size = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir( $handle );
|
||||
}
|
||||
return $size;
|
||||
}
|
||||
|
@ -1777,80 +1777,6 @@ function get_most_recent_post_of_user( $user_id ) {
|
||||
|
||||
// Misc functions
|
||||
|
||||
/**
|
||||
* Get the size of a directory.
|
||||
*
|
||||
* A helper function that is used primarily to check whether
|
||||
* a blog has exceeded its allowed upload space.
|
||||
*
|
||||
* @since MU (3.0.0)
|
||||
*
|
||||
* @param string $directory Full path of a directory.
|
||||
* @return int Size of the directory in MB.
|
||||
*/
|
||||
function get_dirsize( $directory ) {
|
||||
$dirsize = get_transient( 'dirsize_cache' );
|
||||
if ( is_array( $dirsize ) && isset( $dirsize[ $directory ]['size'] ) ) {
|
||||
return $dirsize[ $directory ]['size'];
|
||||
}
|
||||
|
||||
if ( ! is_array( $dirsize ) ) {
|
||||
$dirsize = array();
|
||||
}
|
||||
|
||||
// Exclude individual site directories from the total when checking the main site,
|
||||
// as they are subdirectories and should not be counted.
|
||||
if ( is_main_site() ) {
|
||||
$dirsize[ $directory ]['size'] = recurse_dirsize( $directory, $directory . '/sites' );
|
||||
} else {
|
||||
$dirsize[ $directory ]['size'] = recurse_dirsize( $directory );
|
||||
}
|
||||
|
||||
set_transient( 'dirsize_cache', $dirsize, HOUR_IN_SECONDS );
|
||||
return $dirsize[ $directory ]['size'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of a directory recursively.
|
||||
*
|
||||
* Used by get_dirsize() to get a directory's size when it contains
|
||||
* other directories.
|
||||
*
|
||||
* @since MU (3.0.0)
|
||||
* @since 4.3.0 $exclude parameter added.
|
||||
*
|
||||
* @param string $directory Full path of a directory.
|
||||
* @param string $exclude Optional. Full path of a subdirectory to exclude from the total.
|
||||
* @return int|false Size in MB if a valid directory. False if not.
|
||||
*/
|
||||
function recurse_dirsize( $directory, $exclude = null ) {
|
||||
$size = 0;
|
||||
|
||||
$directory = untrailingslashit( $directory );
|
||||
|
||||
if ( ! file_exists( $directory ) || ! is_dir( $directory ) || ! is_readable( $directory ) || $directory === $exclude ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $handle = opendir( $directory ) ) {
|
||||
while ( ( $file = readdir( $handle ) ) !== false ) {
|
||||
$path = $directory . '/' . $file;
|
||||
if ( $file != '.' && $file != '..' ) {
|
||||
if ( is_file( $path ) ) {
|
||||
$size += filesize( $path );
|
||||
} elseif ( is_dir( $path ) ) {
|
||||
$handlesize = recurse_dirsize( $path, $exclude );
|
||||
if ( $handlesize > 0 ) {
|
||||
$size += $handlesize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir( $handle );
|
||||
}
|
||||
return $size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check an array of MIME types against a whitelist.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user