'Y', 'zetta' => 'Z', 'exa' => 'E', 'peta' => 'P', 'tera' => 'T', 'giga' => 'G', 'mega' => 'M', 'kilo' => 'k', 'hecto' => 'h', 'deca' => 'da', 'unit' => '', 'deci' => 'd', 'centi' => 'c', 'milli' => 'm', 'micro' => 'μ', 'nano' => 'n', 'pico' => 'p', 'femto' => 'f', 'atto' => 'a', 'zepto' => 'z', 'yocto' => 'y', ]; abstract protected function getSymbol(): string; abstract protected function getName(): string; public function __construct( private Unit $value ) { } final public static function fromValue(float $value): static { return new static(new Unit($value)); } final public function getValue(): Unit { return $this->value; } final public function format(string $prefix = 'auto'): string { if ($prefix === 'auto') { $prefix = $this->getAppropriatePrefix(); } else if (!method_exists($this->value, $prefix)) { throw new InvalidArgumentException('Invalid size: ' . $prefix); } $value = $this->value->{$prefix}(); $prefix = self::METRIC_PREFIXES[$prefix] ?? '?'; $symbol = $this->getSymbol(); return sprintf('%.3g %s%s', $value, $prefix, $symbol); } private function getAppropriatePrefix(): string { foreach (array_keys(self::METRIC_PREFIXES) as $prefix) { $value = $this->value->{$prefix}(); if ($value >= 1. && $value < 1000.) { return $prefix; } } return 'unit'; } public function __toString(): string { return $this->format(); } }