-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Deeka Wong <[email protected]>
- Loading branch information
1 parent
1c83818
commit 98c117f
Showing
1 changed file
with
197 additions
and
0 deletions.
There are no files selected for viewing
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
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 [email protected] | ||
*/ | ||
|
||
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.'); | ||
} | ||
} | ||
} |