From ac516cc51b52010bfd64c86a30f19b8b4c567847 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 3 Feb 2026 10:20:55 -0500 Subject: [PATCH 1/4] feat(util): add binary unit label support to humanFileSize() Signed-off-by: Josh --- lib/public/Util.php | 62 ++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/lib/public/Util.php b/lib/public/Util.php index 70a862880f19d..ceb7287c54592 100644 --- a/lib/public/Util.php +++ b/lib/public/Util.php @@ -25,6 +25,10 @@ * @since 4.0.0 */ class Util { + private const DECIMAL_LABELS = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB']; + private const BINARY_LABELS = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB']; + private const MAX_LANE:_INDEX = 5; + private static ?IManager $shareManager = null; private static array $scriptsInit = []; @@ -326,41 +330,57 @@ public static function numericToNumber(string|float|int $number): int|float { } /** - * Make a human file size (2048 to 2 kB) - * @param int|float $bytes file size in bytes - * @return string a human readable file size + * Converts byte values into human-friendly strings using base-1024 logic. + * + * Always divides by 1024, but uses decimal (SI) unit labels (KB, MB) by default (which is + * common in many OSes). Can optionally use more technically accurate IEC binary unit labels + * (KiB, MiB). + * + * - **Rounding:** KB/KiB uses 0 decimals; MB+ uses 1 decimal place. + * - **Transition:** Prevents "1024 KB" by rounding up to "1.0 MB" early. + * + * @example humanFileSize(2048) // "2 KB" (whole) + * @example humanFileSize(1500000) // "1.4 MB" (decimal) + * @example humanFileSize(1500000, true) // "1.4 MiB" (more accurate IEC label) + * @example humanFileSize(1048575) // "1.0 MB" (edge case: rounds up from 1023.999... KB) + * + * @param int|float $bytes File size in bytes + * @param bool $useBinaryLabel If true, use IEC binary labels (KiB, MiB). If false (default), + * use SI decimal labels (KB, MB). Note: Calculation is always + * binary (รท1024) regardless of this setting. + * @return string Human-readable file size string, or "?" for negative values * @since 4.0.0 */ - public static function humanFileSize(int|float $bytes): string { + public static function humanFileSize(int|float $bytes, bool $useBinaryLabel = false): string { if ($bytes < 0) { return '?'; } if ($bytes < 1024) { return "$bytes B"; } - $bytes = round($bytes / 1024, 0); - if ($bytes < 1024) { - return "$bytes KB"; - } - $bytes = round($bytes / 1024, 1); - if ($bytes < 1024) { - return "$bytes MB"; - } - $bytes = round($bytes / 1024, 1); - if ($bytes < 1024) { - return "$bytes GB"; + + $units = $useBinaryLabel ? self::BINARY_LABELS : self::DECIMAL_LABELS; + + // First step: KB/KiB (0 decimals, round immediately) + $value = round($bytes / 1024, 0); + if ($value < 1024) { + return "$value {$units[0]}"; } - $bytes = round($bytes / 1024, 1); - if ($bytes < 1024) { - return "$bytes TB"; + + // Subsequent steps: larger units (1 decimal, round at each step) + for ($unitIndex = 1; $unitIndex <= self::MAX_LABEL_INDEX; $unitIndex++) { + $value = round($value / 1024, 1); + if ($value < 1024 || $unitIndex === self::MAX_LABEL_INDEX) { + return "$value {$units[$unitIndex]}"; + } } - $bytes = round($bytes / 1024, 1); - return "$bytes PB"; + // Unreachable, but keeps static analyzer happy + return "$value {$units[self::MAX_LABEL_INDEX]}"; } /** - * Make a computer file size (2 kB to 2048) + * Converts human-readable file size strings into bytes using base-1024 logic. * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418 * * @param string $str file size in a fancy format From 717338977e2454313a5822af3f827a986b6eab68 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 3 Feb 2026 10:30:16 -0500 Subject: [PATCH 2/4] fix(base): use correct unit prefix for excessive memory usage logs Signed-off-by: Josh --- lib/base.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/base.php b/lib/base.php index 8676b17e19b48..ee4d963d0bb55 100644 --- a/lib/base.php +++ b/lib/base.php @@ -940,7 +940,7 @@ public static function init(): void { }; $memoryLimitIni = @ini_get('memory_limit'); - $message = 'Request used ' . Util::humanFileSize($memoryPeak) . ' of memory. Memory limit: ' . ($memoryLimitIni ?: 'unknown'); + $message = 'Request used ' . Util::humanFileSize($memoryPeak, true) . ' of memory. Memory limit: ' . ($memoryLimitIni ?: 'unknown'); } else { // Fall back to hardcoded thresholds if memory_limit cannot be determined or if debug mode is enabled $logLevel = match (true) { @@ -950,7 +950,7 @@ public static function init(): void { default => null, }; - $message = 'Request used more than 300 MB of RAM: ' . Util::humanFileSize($memoryPeak); + $message = 'Request used more than 300 MB of RAM: ' . Util::humanFileSize($memoryPeak, true); } // Log the message From d6bb6b8122efa62ef3c8424b192582671c55f900 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 3 Feb 2026 11:03:02 -0500 Subject: [PATCH 3/4] chore: fixup typo Signed-off-by: Josh --- lib/public/Util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/Util.php b/lib/public/Util.php index ceb7287c54592..3454363baffc6 100644 --- a/lib/public/Util.php +++ b/lib/public/Util.php @@ -27,7 +27,7 @@ class Util { private const DECIMAL_LABELS = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB']; private const BINARY_LABELS = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB']; - private const MAX_LANE:_INDEX = 5; + private const MAX_LABEL_INDEX = 5; private static ?IManager $shareManager = null; From 6c3823ddd5d52e0786d5102d812c72a62fd9aea6 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 3 Feb 2026 11:14:06 -0500 Subject: [PATCH 4/4] chore: make psalm happy Signed-off-by: Josh --- lib/public/Util.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/public/Util.php b/lib/public/Util.php index 3454363baffc6..c8e929ef42cb3 100644 --- a/lib/public/Util.php +++ b/lib/public/Util.php @@ -371,6 +371,7 @@ public static function humanFileSize(int|float $bytes, bool $useBinaryLabel = fa for ($unitIndex = 1; $unitIndex <= self::MAX_LABEL_INDEX; $unitIndex++) { $value = round($value / 1024, 1); if ($value < 1024 || $unitIndex === self::MAX_LABEL_INDEX) { + /** @var int<0, 5> $unitIndex */ return "$value {$units[$unitIndex]}"; } }