Skip to content

Commit

Permalink
formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
taylorotwell committed Nov 15, 2023
1 parent ec8ba4b commit 6d3a6c2
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 138 deletions.
100 changes: 58 additions & 42 deletions src/Illuminate/Support/Number.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,39 @@ class Number
use Macroable;

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

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

return $formatter->format($number);
}
protected static $locale = 'en';

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

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

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

/**
* Format the number to a percent format.
* Convert the given number to its percentage equivalent.
*
* @param float|int $number
* @param int|float $number
* @param int $precision
* @param string $locale
* @return false|string
* @return string|false
*/
public static function toPercent($number, $precision = 2, $locale = 'en')
public static function toPercentage(int|float $number, int $precision = 2, string $locale = 'en')
{
static::needsIntlExtension();
static::ensureIntlExtensionIsInstalled();

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

Expand All @@ -63,32 +54,32 @@ public static function toPercent($number, $precision = 2, $locale = 'en')
}

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

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

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

/**
* Format the number of bytes to a human-readable string.
* Convert the given number to its file size equivalent.
*
* @param int $bytes
* @param int|float $bytes
* @param int $precision
* @return string
*/
public static function bytesToHuman($bytes, $precision = 2)
public static function toFileSize(int|float $bytes, int $precision = 2)
{
$units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

for ($i = 0; ($bytes / 1024) > 0.9 && ($i < count($units) - 1); $i++) {
$bytes /= 1024;
Expand All @@ -98,13 +89,13 @@ public static function bytesToHuman($bytes, $precision = 2)
}

/**
* Format the number to a fluent human-readable string.
* Convert the number to its human readable equivalent.
*
* @param int $number
* @param int $precision
* @return string
*/
public static function toHuman($number, $precision = 2)
public static function forHumans(int|float $number, int $precision = 2)
{
$units = [
3 => 'thousand',
Expand All @@ -118,9 +109,9 @@ public static function toHuman($number, $precision = 2)
case $number === 0:
return '0';
case $number < 0:
return sprintf('-%s', static::toHuman(abs($number), $precision));
return sprintf('-%s', static::forHumans(abs($number), $precision));
case $number >= 1e15:
return sprintf('%s quadrillion', static::toHuman($number / 1e15, $precision));
return sprintf('%s quadrillion', static::forHumans($number / 1e15, $precision));
}

$numberExponent = floor(log10($number));
Expand All @@ -131,16 +122,41 @@ public static function toHuman($number, $precision = 2)
}

/**
* Some of the helper methods are wrappers for the PHP intl extension,
* and thus require it to be installed on the server. If it's not
* installed, we instead throw an exception from this method.
* Execute the given callback using the given locale.
*
* @param string $locale
* @param callable $callback
* @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.
*
* @param string $locale
* @return void
*/
public static function useLocale(string $locale)
{
static::$locale = $locale;
}

/**
* Ensure the "intl" PHP exntension is installed.
*
* @return void
*/
protected static function needsIntlExtension()
protected static function ensureIntlExtensionIsInstalled()
{
if (! extension_loaded('intl')) {
throw new RuntimeException('The intl PHP extension is required to use this helper.');
throw new RuntimeException('The "intl" PHP extension is required to use this method.');
}
}
}
158 changes: 62 additions & 96 deletions tests/Support/SupportNumberTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,64 +78,30 @@ public function testFormatWithAppLocale()

$this->assertSame('123,456,789', Number::format(123456789));

Number::useLocale('de');

$this->app->locale = 'de';

$this->assertSame('123.456.789', Number::format(123456789));
}

public function testSpellout()
{
$this->needsIntlExtension();

$this->assertSame('zero', Number::spellout(0));
$this->assertSame('one', Number::spellout(1));
$this->assertSame('ten', Number::spellout(10));
$this->assertSame('twenty-five', Number::spellout(25));
$this->assertSame('one hundred', Number::spellout(100));
$this->assertSame('one hundred thousand', Number::spellout(100000));
$this->assertSame('one hundred twenty-three million four hundred fifty-six thousand seven hundred eighty-nine', Number::spellout(123456789));

$this->assertSame('one billion', Number::spellout(1000000000));
$this->assertSame('one trillion', Number::spellout(1000000000000));
$this->assertSame('one quadrillion', Number::spellout(1000000000000000));
$this->assertSame('1,000,000,000,000,000,000', Number::spellout(1000000000000000000));

$this->assertSame('minus one', Number::spellout(-1));
$this->assertSame('minus ten', Number::spellout(-10));
$this->assertSame('minus twenty-five', Number::spellout(-25));

$this->assertSame('zero point two', Number::spellout(0.2));
$this->assertSame('one point two three', Number::spellout(1.23));
$this->assertSame('minus one point two three', Number::spellout(-1.23));
$this->assertSame('one hundred twenty-three point four five six', Number::spellout(123.456));
}

public function testSpelloutWithDifferentLocale()
{
$this->needsIntlExtension();

$this->assertSame('cent vingt-trois', Number::spellout(123, 'fr'));

$this->assertSame('ein­hundert­drei­und­zwanzig', Number::spellout(123, 'de'));

$this->assertSame('ett­hundra­tjugo­tre', Number::spellout(123, 'sv'));
Number::useLocale('en');
}

public function testToPercent()
{
$this->needsIntlExtension();

$this->assertSame('0%', Number::toPercent(0));
$this->assertSame('1%', Number::toPercent(1));
$this->assertSame('10%', Number::toPercent(10));
$this->assertSame('100%', Number::toPercent(100));
$this->assertSame('0%', Number::toPercentage(0));
$this->assertSame('1%', Number::toPercentage(1));
$this->assertSame('10%', Number::toPercentage(10));
$this->assertSame('100%', Number::toPercentage(100));

$this->assertSame('300%', Number::toPercent(300));
$this->assertSame('1,000%', Number::toPercent(1000));
$this->assertSame('300%', Number::toPercentage(300));
$this->assertSame('1,000%', Number::toPercentage(1000));

$this->assertSame('1.75%', Number::toPercent(1.75));
$this->assertSame('0.12%', Number::toPercent(0.12345));
$this->assertSame('0.1235%', Number::toPercent(0.12345, 4));
$this->assertSame('1.75%', Number::toPercentage(1.75));
$this->assertSame('0.12%', Number::toPercentage(0.12345));
$this->assertSame('0.1235%', Number::toPercentage(0.12345, 4));
}

public function testToCurrency()
Expand Down Expand Up @@ -170,60 +136,60 @@ public function testToCurrencyWithDifferentLocale()

public function testBytesToHuman()
{
$this->assertSame('0 B', Number::bytesToHuman(0));
$this->assertSame('1 B', Number::bytesToHuman(1));
$this->assertSame('1 kB', Number::bytesToHuman(1024));
$this->assertSame('2 kB', Number::bytesToHuman(2048));
$this->assertSame('1.23 kB', Number::bytesToHuman(1264));
$this->assertSame('1.234 kB', Number::bytesToHuman(1264, 3));
$this->assertSame('5 GB', Number::bytesToHuman(1024 * 1024 * 1024 * 5));
$this->assertSame('10 TB', Number::bytesToHuman((1024 ** 4) * 10));
$this->assertSame('10 PB', Number::bytesToHuman((1024 ** 5) * 10));
$this->assertSame('1 ZB', Number::bytesToHuman(1024 ** 7));
$this->assertSame('1 YB', Number::bytesToHuman(1024 ** 8));
$this->assertSame('1024 YB', Number::bytesToHuman(1024 ** 9));
$this->assertSame('0 B', Number::toFileSize(0));
$this->assertSame('1 B', Number::toFileSize(1));
$this->assertSame('1 KB', Number::toFileSize(1024));
$this->assertSame('2 KB', Number::toFileSize(2048));
$this->assertSame('1.23 KB', Number::toFileSize(1264));
$this->assertSame('1.234 KB', Number::toFileSize(1264, 3));
$this->assertSame('5 GB', Number::toFileSize(1024 * 1024 * 1024 * 5));
$this->assertSame('10 TB', Number::toFileSize((1024 ** 4) * 10));
$this->assertSame('10 PB', Number::toFileSize((1024 ** 5) * 10));
$this->assertSame('1 ZB', Number::toFileSize(1024 ** 7));
$this->assertSame('1 YB', Number::toFileSize(1024 ** 8));
$this->assertSame('1024 YB', Number::toFileSize(1024 ** 9));
}

public function testToHuman()
{
$this->assertSame('1', Number::toHuman(1));
$this->assertSame('10', Number::toHuman(10));
$this->assertSame('100', Number::toHuman(100));
$this->assertSame('1 thousand', Number::toHuman(1000));
$this->assertSame('1 million', Number::toHuman(1000000));
$this->assertSame('1 billion', Number::toHuman(1000000000));
$this->assertSame('1 trillion', Number::toHuman(1000000000000));
$this->assertSame('1 quadrillion', Number::toHuman(1000000000000000));
$this->assertSame('1 thousand quadrillion', Number::toHuman(1000000000000000000));

$this->assertSame('123', Number::toHuman(123));
$this->assertSame('1.23 thousand', Number::toHuman(1234));
$this->assertSame('12.35 thousand', Number::toHuman(12345));
$this->assertSame('1.23 million', Number::toHuman(1234567));
$this->assertSame('1.23 billion', Number::toHuman(1234567890));
$this->assertSame('1.23 trillion', Number::toHuman(1234567890123));
$this->assertSame('1.23 quadrillion', Number::toHuman(1234567890123456));
$this->assertSame('1.23 thousand quadrillion', Number::toHuman(1234567890123456789));
$this->assertSame('490 thousand', Number::toHuman(489939, precision: 0));
$this->assertSame('489.939 thousand', Number::toHuman(489939, precision: 4));
$this->assertSame('500 million', Number::toHuman(500000000, precision: 5));

$this->assertSame('1 million quadrillion', Number::toHuman(1000000000000000000000));
$this->assertSame('1 billion quadrillion', Number::toHuman(1000000000000000000000000));
$this->assertSame('1 trillion quadrillion', Number::toHuman(1000000000000000000000000000));
$this->assertSame('1 quadrillion quadrillion', Number::toHuman(1000000000000000000000000000000));
$this->assertSame('1 thousand quadrillion quadrillion', Number::toHuman(1000000000000000000000000000000000));

$this->assertSame('0', Number::toHuman(0));
$this->assertSame('-1', Number::toHuman(-1));
$this->assertSame('-10', Number::toHuman(-10));
$this->assertSame('-100', Number::toHuman(-100));
$this->assertSame('-1 thousand', Number::toHuman(-1000));
$this->assertSame('-1 million', Number::toHuman(-1000000));
$this->assertSame('-1 billion', Number::toHuman(-1000000000));
$this->assertSame('-1 trillion', Number::toHuman(-1000000000000));
$this->assertSame('-1 quadrillion', Number::toHuman(-1000000000000000));
$this->assertSame('-1 thousand quadrillion', Number::toHuman(-1000000000000000000));
$this->assertSame('1', Number::forHumans(1));
$this->assertSame('10', Number::forHumans(10));
$this->assertSame('100', Number::forHumans(100));
$this->assertSame('1 thousand', Number::forHumans(1000));
$this->assertSame('1 million', Number::forHumans(1000000));
$this->assertSame('1 billion', Number::forHumans(1000000000));
$this->assertSame('1 trillion', Number::forHumans(1000000000000));
$this->assertSame('1 quadrillion', Number::forHumans(1000000000000000));
$this->assertSame('1 thousand quadrillion', Number::forHumans(1000000000000000000));

$this->assertSame('123', Number::forHumans(123));
$this->assertSame('1.23 thousand', Number::forHumans(1234));
$this->assertSame('12.35 thousand', Number::forHumans(12345));
$this->assertSame('1.23 million', Number::forHumans(1234567));
$this->assertSame('1.23 billion', Number::forHumans(1234567890));
$this->assertSame('1.23 trillion', Number::forHumans(1234567890123));
$this->assertSame('1.23 quadrillion', Number::forHumans(1234567890123456));
$this->assertSame('1.23 thousand quadrillion', Number::forHumans(1234567890123456789));
$this->assertSame('490 thousand', Number::forHumans(489939, precision: 0));
$this->assertSame('489.939 thousand', Number::forHumans(489939, precision: 4));
$this->assertSame('500 million', Number::forHumans(500000000, precision: 5));

$this->assertSame('1 million quadrillion', Number::forHumans(1000000000000000000000));
$this->assertSame('1 billion quadrillion', Number::forHumans(1000000000000000000000000));
$this->assertSame('1 trillion quadrillion', Number::forHumans(1000000000000000000000000000));
$this->assertSame('1 quadrillion quadrillion', Number::forHumans(1000000000000000000000000000000));
$this->assertSame('1 thousand quadrillion quadrillion', Number::forHumans(1000000000000000000000000000000000));

$this->assertSame('0', Number::forHumans(0));
$this->assertSame('-1', Number::forHumans(-1));
$this->assertSame('-10', Number::forHumans(-10));
$this->assertSame('-100', Number::forHumans(-100));
$this->assertSame('-1 thousand', Number::forHumans(-1000));
$this->assertSame('-1 million', Number::forHumans(-1000000));
$this->assertSame('-1 billion', Number::forHumans(-1000000000));
$this->assertSame('-1 trillion', Number::forHumans(-1000000000000));
$this->assertSame('-1 quadrillion', Number::forHumans(-1000000000000000));
$this->assertSame('-1 thousand quadrillion', Number::forHumans(-1000000000000000000));
}

protected function needsIntlExtension()
Expand Down

0 comments on commit 6d3a6c2

Please sign in to comment.