WordPress Version: 6.4
/**
* Gets the size of a directory recursively.
*
* Used by get_dirsize() to get a directory size when it contains other directories.
*
* @since MU (3.0.0)
* @since 4.3.0 The `$exclude` parameter was added.
* @since 5.2.0 The `$max_execution_time` parameter was added.
* @since 5.6.0 The `$directory_cache` parameter was added.
*
* @param string $directory Full path of a directory.
* @param string|string[] $exclude Optional. Full path of a subdirectory to exclude from the total,
* or array of paths. Expected without trailing slash(es).
* Default null.
* @param int $max_execution_time Optional. Maximum time to run before giving up. In seconds.
* The timeout is global and is measured from the moment
* WordPress started to load. Defaults to the value of
* `max_execution_time` PHP setting.
* @param array $directory_cache Optional. Array of cached directory paths.
* Defaults to the value of `dirsize_cache` transient.
* @return int|false|null Size in bytes if a valid directory. False if not. Null if timeout.
*/
function recurse_dirsize($directory, $exclude = null, $max_execution_time = null, &$directory_cache = null)
{
$directory = untrailingslashit($directory);
$save_cache = false;
if (!isset($directory_cache)) {
$directory_cache = get_transient('dirsize_cache');
$save_cache = true;
}
if (isset($directory_cache[$directory]) && is_int($directory_cache[$directory])) {
return $directory_cache[$directory];
}
if (!file_exists($directory) || !is_dir($directory) || !is_readable($directory)) {
return false;
}
if (is_string($exclude) && $directory === $exclude || is_array($exclude) && in_array($directory, $exclude, true)) {
return false;
}
if (null === $max_execution_time) {
// Keep the previous behavior but attempt to prevent fatal errors from timeout if possible.
if (function_exists('ini_get')) {
$max_execution_time = ini_get('max_execution_time');
} else {
// Disable...
$max_execution_time = 0;
}
// Leave 1 second "buffer" for other operations if $max_execution_time has reasonable value.
if ($max_execution_time > 10) {
$max_execution_time -= 1;
}
}
/**
* Filters the amount of storage space used by one directory and all its children, in megabytes.
*
* Return the actual used space to short-circuit the recursive PHP file size calculation
* and use something else, like a CDN API or native operating system tools for better performance.
*
* @since 5.6.0
*
* @param int|false $space_used The amount of used space, in bytes. Default false.
* @param string $directory Full path of a directory.
* @param string|string[]|null $exclude Full path of a subdirectory to exclude from the total,
* or array of paths.
* @param int $max_execution_time Maximum time to run before giving up. In seconds.
* @param array $directory_cache Array of cached directory paths.
*/
$size = apply_filters('pre_recurse_dirsize', false, $directory, $exclude, $max_execution_time, $directory_cache);
if (false === $size) {
$size = 0;
$handle = opendir($directory);
if ($handle) {
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, $directory_cache);
if ($handlesize > 0) {
$size += $handlesize;
}
}
if ($max_execution_time > 0 && microtime(true) - WP_START_TIMESTAMP > $max_execution_time) {
// Time exceeded. Give up instead of risking a fatal timeout.
$size = null;
break;
}
}
}
closedir($handle);
}
}
if (!is_array($directory_cache)) {
$directory_cache = array();
}
$directory_cache[$directory] = $size;
// Only write the transient on the top level call and not on recursive calls.
if ($save_cache) {
$expiration = wp_using_ext_object_cache() ? 0 : (10 * YEAR_IN_SECONDS);
set_transient('dirsize_cache', $directory_cache, $expiration);
}
return $size;
}