Created
January 5, 2023 23:57
-
-
Save inxilpro/35f511eab511d0abe9796bcc3c5dea1f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Support; | |
use InvalidArgumentException; | |
class Bytes | |
{ | |
// Most modern systems use base-1000 rather than base-1024 for file size now | |
protected const BASE = 1000; | |
protected const UNITS = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; | |
protected const PRECISIONS = [0, 0, 2, 2, 3, 3, 4, 4, 5]; | |
// Technically 'KiB'-style suffixes are base-1024, so we could potentially configure that | |
protected const ALIASES = ['k' => 1, 'kib' => 1, 'm' => 2, 'mib' => 2, 'g' => 3, 'gib' => 3, 't' => 4, 'tib' => 4, 'pib' => 5, 'eib' => 6, 'zib' => 7, 'yib' => 8]; | |
public static function from(int|string|self $value): static | |
{ | |
return match (get_debug_type($value)) { | |
'int' => new static($value), | |
'string' => static::parse($value), | |
static::class => new static($value->bytes), | |
default => throw new InvalidArgumentException('Invalid value passed to Bytes::from'), | |
}; | |
} | |
public static function parse(string $value): static | |
{ | |
if (! preg_match('/([0-9,.]+)\s*([a-z]+)/i', $value, $matches)) { | |
throw new InvalidArgumentException("Unable to parse '{$value}' as a file size."); | |
} | |
$size = (float) str_replace(',', '', $matches[1]); | |
$exponent = static::suffixToExponent($matches[2]); | |
$multiplier = static::BASE ** $exponent; | |
return static::from($size * $multiplier); | |
} | |
protected static function suffixToExponent(string $suffix): int | |
{ | |
$lookup = collect(static::UNITS) | |
->combine(array_keys(static::UNITS)) | |
->union(static::ALIASES); | |
return $lookup->first(fn($exponent, $key) => 0 === strcasecmp($key, $suffix), function() use ($suffix) { | |
throw new InvalidArgumentException("Unknown suffix: {$suffix}"); | |
}); | |
} | |
public function __construct( | |
public int $bytes | |
) { | |
} | |
public function toKb(): float | |
{ | |
return $this->bytes / static::BASE; | |
} | |
public function toMb(): float | |
{ | |
return $this->bytes / (static::BASE ** 2); | |
} | |
public function toGb(): float | |
{ | |
return $this->bytes / (static::BASE ** 3); | |
} | |
public function toTb(): float | |
{ | |
return $this->bytes / (static::BASE ** 4); | |
} | |
public function toPb(): float | |
{ | |
return $this->bytes / (static::BASE ** 5); | |
} | |
public function __toString(): string | |
{ | |
// Don't go over the largest configured unit | |
$highest_allowed_exponent = count(static::UNITS) - 1; | |
// Ensure we have bytes | |
$bytes = max($this->bytes, 1); | |
// Shoehorn bytes into one of our groups based on 1000's of bytes | |
// ie. 100 bytes is 0, 1000 bytes is 1, 1000000 bytes is 2, etc | |
$exponent = min(floor(log($bytes) / log(static::BASE)), $highest_allowed_exponent); | |
// Now convert bytes to our chosen size, and display | |
$size = $bytes / (static::BASE ** $exponent); | |
$unit = static::UNITS[$exponent]; | |
$precision = static::PRECISIONS[$exponent] ?? 3; | |
return round($size, $precision).' '.$unit; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment