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. Built from https://develop.svn.wordpress.org/trunk@45104 git-svn-id: http://core.svn.wordpress.org/trunk@44913 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
1909df67a7
commit
2cdc22aded
|
@ -32,7 +32,6 @@ class WP_Debug_Data {
|
||||||
*/
|
*/
|
||||||
static function debug_data( $locale = null ) {
|
static function debug_data( $locale = null ) {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
if ( ! empty( $locale ) ) {
|
if ( ! empty( $locale ) ) {
|
||||||
// Change the language used for translations
|
// Change the language used for translations
|
||||||
if ( function_exists( 'switch_to_locale' ) ) {
|
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.
|
// Go through the various installation directories and calculate their sizes.
|
||||||
$uploads_dir = wp_upload_dir();
|
$uploads_dir = wp_upload_dir();
|
||||||
$inaccurate = false;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We will be using the PHP max execution time to prevent the size calculations
|
* 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.
|
* hosts do not allow you to read configuration values.
|
||||||
*/
|
*/
|
||||||
$max_execution_time = 30;
|
$max_execution_time = 30;
|
||||||
$start_execution_time = microtime( true );
|
|
||||||
if ( function_exists( 'ini_get' ) ) {
|
if ( function_exists( 'ini_get' ) ) {
|
||||||
$max_execution_time = ini_get( 'max_execution_time' );
|
$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(
|
$size_directories = array(
|
||||||
'wordpress' => array(
|
'wordpress' => array(
|
||||||
'path' => ABSPATH,
|
'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.
|
// Loop over all the directories we want to gather the sizes for.
|
||||||
foreach ( $size_directories as $size => $attributes ) {
|
foreach ( $size_directories as $size => $attributes ) {
|
||||||
/*
|
$dir_size = null; // Default to timeout.
|
||||||
* 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 );
|
|
||||||
|
|
||||||
$size_directories[ $size ]['size'] = $calculated_size;
|
if ( microtime( true ) - WP_START_TIMESTAMP < $max_execution_time ) {
|
||||||
|
$dir_size = get_dirsize( $attributes['path'], $max_execution_time );
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$size_db = WP_Debug_Data::get_database_size();
|
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 );
|
||||||
|
|
||||||
$size_total = $size_directories['wordpress']['size'] + $size_db;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
$info['wp-paths-sizes']['fields'] = array(
|
||||||
array(
|
array(
|
||||||
|
@ -383,7 +403,7 @@ class WP_Debug_Data {
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'label' => __( 'Uploads Directory Size' ),
|
'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(
|
array(
|
||||||
'label' => __( 'Themes Directory Location' ),
|
'label' => __( 'Themes Directory Location' ),
|
||||||
|
@ -395,7 +415,7 @@ class WP_Debug_Data {
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'label' => __( 'Themes Directory Size' ),
|
'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(
|
array(
|
||||||
'label' => __( 'Plugins Directory Location' ),
|
'label' => __( 'Plugins Directory Location' ),
|
||||||
|
@ -403,7 +423,7 @@ class WP_Debug_Data {
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'label' => __( 'Plugins Directory Size' ),
|
'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(
|
array(
|
||||||
'label' => __( 'WordPress Directory Location' ),
|
'label' => __( 'WordPress Directory Location' ),
|
||||||
|
@ -411,7 +431,7 @@ class WP_Debug_Data {
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'label' => __( 'WordPress Directory Size' ),
|
'label' => __( 'WordPress Directory Size' ),
|
||||||
'value' => size_format( $size_directories['wordpress']['size'], 2 ),
|
'value' => $size_directories['wordpress']['size'],
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'label' => __( 'Database size' ),
|
'label' => __( 'Database size' ),
|
||||||
|
@ -419,11 +439,7 @@ class WP_Debug_Data {
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'label' => __( 'Total installation size' ),
|
'label' => __( 'Total installation size' ),
|
||||||
'value' => sprintf(
|
'value' => $size_total,
|
||||||
'%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.' ) )
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -934,36 +950,6 @@ class WP_Debug_Data {
|
||||||
return $return;
|
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.
|
* 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 );
|
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 = @ini_get( 'memory_limit' );
|
||||||
$current_limit_int = wp_convert_hr_to_bytes( $current_limit );
|
$current_limit_int = wp_convert_hr_to_bytes( $current_limit );
|
||||||
|
|
||||||
|
|
|
@ -7006,3 +7006,103 @@ function wp_direct_php_update_button() {
|
||||||
);
|
);
|
||||||
echo '</p>';
|
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
|
// 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.
|
* Check an array of MIME types against a whitelist.
|
||||||
*
|
*
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
*
|
*
|
||||||
* @global string $wp_version
|
* @global string $wp_version
|
||||||
*/
|
*/
|
||||||
$wp_version = '5.2-beta1-45103';
|
$wp_version = '5.2-beta1-45104';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
||||||
|
|
Loading…
Reference in New Issue