Skip to content

Commit

Permalink
Adds Number utility class (#440)
Browse files Browse the repository at this point in the history
Co-authored-by: Deeka Wong <8337659+huangdijia@users.noreply.github.com>
  • Loading branch information
huangdijia and huangdijia authored Nov 19, 2023
1 parent 1c83818 commit 98c117f
Showing 1 changed file with 197 additions and 0 deletions.
197 changes: 197 additions & 0 deletions src/Number.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
<?php

declare(strict_types=1);
/**
* This file is part of friendsofhyperf/components.
*
* @link https://github.com/friendsofhyperf/components
* @document https://github.com/friendsofhyperf/components/blob/main/README.md
* @contact huangdijia@gmail.com
*/

namespace FriendsOfHyperf\Support;

use Hyperf\Macroable\Macroable;
use NumberFormatter;
use RuntimeException;

use function Hyperf\Tappable\tap;

class Number
{
use Macroable;

/**
* The current default locale.
*
* @var string
*/
protected static $locale = 'en';

/**
* Format the given number according to the current locale.
*
* @param ?string $locale
* @return string|false
*/
public static function format(int|float $number, ?int $precision = null, ?int $maxPrecision = null, ?string $locale = null)
{
static::ensureIntlExtensionIsInstalled();

$formatter = new NumberFormatter($locale ?? static::$locale, NumberFormatter::DECIMAL);

if (! is_null($maxPrecision)) {
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $maxPrecision);
} elseif (! is_null($precision)) {
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, $precision);
}

return $formatter->format($number);
}

/**
* Spell out the given number in the given locale.
*
* @param ?string $locale
* @return string
*/
public static function spell(int|float $number, ?string $locale = null)
{
static::ensureIntlExtensionIsInstalled();

$formatter = new NumberFormatter($locale ?? static::$locale, NumberFormatter::SPELLOUT);

return $formatter->format($number);
}

/**
* Convert the given number to ordinal form.
*
* @param ?string $locale
* @return string
*/
public static function ordinal(int|float $number, ?string $locale = null)
{
static::ensureIntlExtensionIsInstalled();

$formatter = new NumberFormatter($locale ?? static::$locale, NumberFormatter::ORDINAL);

return $formatter->format($number);
}

/**
* Convert the given number to its percentage equivalent.
*
* @param ?string $locale
* @return string|false
*/
public static function percentage(int|float $number, int $precision = 0, ?int $maxPrecision = null, ?string $locale = null)
{
static::ensureIntlExtensionIsInstalled();

$formatter = new NumberFormatter($locale ?? static::$locale, NumberFormatter::PERCENT);

if (! is_null($maxPrecision)) {
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $maxPrecision);
} else {
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, $precision);
}

return $formatter->format($number / 100);
}

/**
* Convert the given number to its currency equivalent.
*
* @param ?string $locale
* @return string|false
*/
public static function currency(int|float $number, string $in = 'USD', ?string $locale = null)
{
static::ensureIntlExtensionIsInstalled();

$formatter = new NumberFormatter($locale ?? static::$locale, NumberFormatter::CURRENCY);

return $formatter->formatCurrency($number, $in);
}

/**
* Convert the given number to its file size equivalent.
*
* @return string
*/
public static function fileSize(int|float $bytes, int $precision = 0, ?int $maxPrecision = null)
{
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

for ($i = 0; ($bytes / 1024) > 0.9 && ($i < count($units) - 1); ++$i) {
$bytes /= 1024;
}

return sprintf('%s %s', static::format($bytes, $precision, $maxPrecision), $units[$i]);
}

/**
* Convert the number to its human readable equivalent.
*
* @param int $number
* @return string
*/
public static function forHumans(int|float $number, int $precision = 0, ?int $maxPrecision = null)
{
$units = [
3 => 'thousand',
6 => 'million',
9 => 'billion',
12 => 'trillion',
15 => 'quadrillion',
];

switch (true) {
case $number === 0:
return '0';
case $number < 0:
return sprintf('-%s', static::forHumans(abs($number), $precision, $maxPrecision));
case $number >= 1e15:
return sprintf('%s quadrillion', static::forHumans($number / 1e15, $precision, $maxPrecision));
}

$numberExponent = floor(log10($number));
$displayExponent = $numberExponent - ($numberExponent % 3);
$number /= pow(10, $displayExponent);

return trim(sprintf('%s %s', static::format($number, $precision, $maxPrecision), $units[$displayExponent] ?? ''));
}

/**
* Execute the given callback using the given locale.
*
* @return mixed
*/
public static function withLocale(string $locale, callable $callback)
{
$previousLocale = static::$locale;

static::useLocale($locale);

return tap($callback(), fn () => static::useLocale($previousLocale));
}

/**
* Set the default locale.
*/
public static function useLocale(string $locale)
{
static::$locale = $locale;
}

/**
* Ensure the "intl" PHP extension is installed.
*/
protected static function ensureIntlExtensionIsInstalled()
{
if (! extension_loaded('intl')) {
throw new RuntimeException('The "intl" PHP extension is required to use this method.');
}
}
}

0 comments on commit 98c117f

Please sign in to comment.