Files
wordpress-theme-aksrt/src/Ledger/AssetAmount.php
T

91 lines
2.6 KiB
PHP

<?php
/**
* @package AKSRT
*/
namespace AKSRT\Ledger;
defined('ABSPATH') || exit;
final class AssetAmount {
private function __construct(
private readonly AssetType $asset,
private readonly int $minor
) {}
public static function points(int $points): self {
if ($points < 0) {
throw new \InvalidArgumentException('points_must_be_non_negative');
}
return new self(AssetType::POINTS, $points);
}
public static function balance(string $decimal): self {
$decimal = trim($decimal);
if (!preg_match('/^(0|[1-9]\d*)(?:\.(\d{1,2}))?$/', $decimal, $matches)) {
throw new \InvalidArgumentException('balance_decimal_invalid');
}
$whole = (int) $matches[1];
$fraction = str_pad((string) ($matches[2] ?? ''), 2, '0');
if ($whole > intdiv(PHP_INT_MAX - (int) $fraction, 100)) {
throw new \InvalidArgumentException('balance_decimal_overflow');
}
return new self(AssetType::BALANCE, ($whole * 100) + (int) $fraction);
}
public static function balance_minor(string $decimal): int {
return self::balance($decimal)->minor();
}
public static function decimal_from_minor(int $minor): string {
return self::from_minor(AssetType::BALANCE, $minor)->decimal();
}
/**
* Apply an integer percentage using half-up rounding without floats.
*/
public static function percentage(int $minor, int $percent): int {
if ($minor < 0) {
throw new \InvalidArgumentException('asset_minor_must_be_non_negative');
}
if ($percent < 0 || $percent > 100) {
throw new \InvalidArgumentException('asset_percentage_invalid');
}
$whole = intdiv($minor, 100) * $percent;
$remainder = ($minor % 100) * $percent;
return $whole + intdiv($remainder + 50, 100);
}
public static function from_minor(AssetType|string $asset, int $minor): self {
if ($minor < 0) {
throw new \InvalidArgumentException('asset_minor_must_be_non_negative');
}
return new self(AssetType::from_value($asset), $minor);
}
public function asset(): AssetType {
return $this->asset;
}
public function minor(): int {
return $this->minor;
}
public function decimal(): string {
if ($this->asset === AssetType::POINTS) {
return (string) $this->minor;
}
return sprintf('%d.%02d', intdiv($this->minor, 100), $this->minor % 100);
}
public function formatted(): string {
if ($this->asset === AssetType::POINTS) {
return number_format_i18n($this->minor);
}
global $wp_locale;
$decimal_point = isset($wp_locale->number_format['decimal_point']) ? $wp_locale->number_format['decimal_point'] : '.';
return number_format_i18n(intdiv($this->minor, 100)) . $decimal_point . sprintf('%02d', $this->minor % 100);
}
}