Skip to content

Instantly share code, notes, and snippets.

@timw4mail
Last active November 11, 2022 15:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timw4mail/9c4482849d445fd1e1da2d8727add2d8 to your computer and use it in GitHub Desktop.
Save timw4mail/9c4482849d445fd1e1da2d8727add2d8 to your computer and use it in GitHub Desktop.
PHP 8.1+ Unit Formatter
<?php declare(strict_types=1);
enum Base2Unit: int implements UnitInterface {
case B = 1024**0;
case KB = 1024**1;
case MB = 1024**3;
case GB = 1024**4;
case TB = 1024**5;
case PB = 1024**6;
public function getNextUnit(): self
{
return match ($this) {
self::B => self::KB,
self::KB => self::MB,
self::MB => self::GB,
self::GB => self::TB,
self::TB, self::PB => self::PB,
};
}
public function getPreviousUnit(): self
{
return match ($this) {
self::PB => self::TB,
self::TB => self::GB,
self::GB => self::MB,
self::MB => self::KB,
self::KB, self::B => self::B,
};
}
public function getDivisor(): int
{
return 1024;
}
public function getLabel(): string
{
return match ($this) {
self::B => '',
self::KB => 'K',
self::MB => 'M',
self::GB => 'G',
self::TB => 'T',
self::PB => 'P',
};
}
public static function getBaseUnit(): UnitInterface
{
return self::B;
}
}
<?php declare(strict_types=1);
$formattedGHz = UnitFormatter::friendlyHz(SI::Mega, 1234); // Returns '1.234 GHz'
$formattedMB = UnitFormatter::friendlyBytes(Base2Unit::KB, 4224); // Returns '4 MB, 128KB'
<?php declare(strict_types=1);
enum SI: int implements UnitInterface {
case Base = 10**0;
case Kilo = 10**3;
case Mega = 10**6;
case Giga = 10**9;
case Tera = 10**12;
case Peta = 10**15;
case Exa = 10**18;
public function getNextUnit(): self
{
return match ($this) {
self::Base => self::Kilo,
self::Kilo => self::Mega,
self::Mega => self::Giga,
self::Giga => self::Tera,
self::Tera => self::Peta,
self::Peta, self::Exa => self::Exa,
};
}
public function getPreviousUnit(): self
{
return match ($this) {
self::Exa => self::Peta,
self::Peta => self::Tera,
self::Tera => self::Giga,
self::Giga => self::Mega,
self::Mega => self::Kilo,
self::Kilo, self::Base => self::Base,
};
}
public function getDivisor(): int
{
return 1000;
}
public function getLabel(): string
{
return match ($this) {
self::Base => '',
self::Kilo => 'K',
self::Mega => 'M',
self::Giga => 'G',
self::Tera => 'T',
self::Peta => 'P',
self::Exa => 'E',
};
}
public static function getBaseUnit(): UnitInterface
{
return self::Base;
}
}
<?php declare(strict_types=1);
namespace App\Libraries;
class UnitFormatter {
public static function friendlyBytesFloat(UnitInterface $baseUnit, int $size): string
{
return (new UnitFormatterImpl($baseUnit, $size, 'B'))->formatFloat();
}
public static function friendlyBytes(UnitInterface $baseUnit, int $size): string
{
$parts = (new UnitFormatterImpl($baseUnit, $size, 'B'))->format();
return join(', ', $parts);
}
public static function friendlyHz(UnitInterface $baseUnit, int $size): string
{
return (new UnitFormatterImpl($baseUnit, $size, 'Hz'))->formatFloat();
}
}
class UnitFormatterImpl {
public function __construct(
private readonly UnitInterface $baseUnit,
private readonly int $input,
private readonly string $unitType,
)
{
}
public function formatFloat(): string
{
$unit = $this->getLargestUnit($this->input);
return ($this->input < $unit->getDivisor())
? $this->formatLabel($this->input, $unit)
: $this->formatLabel($this->input / $unit->getDivisor(), $unit);
}
public function format(): array
{
$output = [];
$nextValue = $this->input;
while ($nextValue > 0)
{
[$unit, $value, $nextValue] = $this->getUnitValue($nextValue);
$output[] = $this->formatLabel($value, $unit);
}
return $output;
}
public function getUnitValue(int $initial): array
{
$unit = $this->getLargestUnit($initial);
$divisor = $unit->getDivisor();
$value = (int)($initial / $divisor);
$rem = $initial % $divisor;
if ($value < 1 && $rem > 0)
{
return [$unit, $rem, 0];
}
return [$unit, $value, $rem];
}
public function formatLabel(mixed $value, UnitInterface $unit): string
{
return $value . ' ' . $unit->getLabel() . $this->unitType;
}
public function getLargestUnit(int $input): UnitInterface
{
$unit = $this->baseUnit;
$divisor = $this->baseUnit->getDivisor();
$value = $input;
while ($value >= $divisor)
{
$unit = $unit->getNextUnit();
$value /= $divisor;
}
return $unit;
}
}
<?php declare(strict_types=1);
use BackedEnum;
interface UnitInterface extends BackedEnum {
public function getNextUnit(): self;
public function getPreviousUnit(): self;
public function getDivisor(): int;
public function getLabel(): string;
public static function getBaseUnit(): self;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment