Commit 3f757280 by Carsten Brandt

refactored size format

parent 916d2c18
...@@ -116,11 +116,12 @@ class Formatter extends Component ...@@ -116,11 +116,12 @@ class Formatter extends Component
* Please refer to the [PHP manual](http://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants.unumberformatattribute) * Please refer to the [PHP manual](http://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants.unumberformatattribute)
* for the possible options. * for the possible options.
* *
* For example to change the grouping size you can configure this property like the following: * For example to adjust the maximum and minimum value of fraction digits you can configure this property like the following:
* *
* ```php * ```php
* [ * [
* NumberFormatter::GROUPING_SIZE => 4, * NumberFormatter::MIN_FRACTION_DIGITS => 0,
* NumberFormatter::MAX_FRACTION_DIGITS => 2,
* ] * ]
* ``` * ```
*/ */
...@@ -146,18 +147,15 @@ class Formatter extends Component ...@@ -146,18 +147,15 @@ class Formatter extends Component
/** /**
* @var string the 3-letter ISO 4217 currency code indicating the default currency to use for [[asCurrency]]. * @var string the 3-letter ISO 4217 currency code indicating the default currency to use for [[asCurrency]].
* If not set, the currency code corresponding to [[locale]] will be used. * If not set, the currency code corresponding to [[locale]] will be used.
* Note that in this case the [[locale]] has to be specified with a country code, e.g. `en-US` otherwise it
* is not possible to determine the default currency.
*/ */
public $currencyCode; public $currencyCode;
/** /**
* @var array the format used to format size (bytes). Three elements may be specified: "base", "decimals" and "decimalSeparator". * @var array the base at which a kilobyte is calculated (1000 or 1024 bytes per kilobyte), used by [[asSize]] and [[asShortSize]].
* They correspond to the base at which a kilobyte is calculated (1000 or 1024 bytes per kilobyte, defaults to 1024), * Defaults to 1024.
* the number of digits after the decimal point (defaults to 2) and the character displayed as the decimal point.
*/ */
public $sizeFormat = [ public $sizeFormatBase = 1024;
'base' => 1024,
'decimals' => 2,
'decimalSeparator' => null,
];
/** /**
* @var boolean whether the [PHP intl extension](http://php.net/manual/en/book.intl.php) is loaded. * @var boolean whether the [PHP intl extension](http://php.net/manual/en/book.intl.php) is loaded.
...@@ -1072,66 +1070,128 @@ class Formatter extends Component ...@@ -1072,66 +1070,128 @@ class Formatter extends Component
} }
/** /**
* Formats the value in bytes as a size in human readable form. * Formats the value in bytes as a size in human readable form for example `12 KB`.
* @param integer $value value in bytes to be formatted *
* @param boolean $verbose if full names should be used (e.g. bytes, kilobytes, ...). * This is the short form of [[asSize]].
* Defaults to false meaning that short names will be used (e.g. B, KB, ...). *
* @param boolean $binaryPrefix if binary prefixes should be used for base 1024 * If [[sizeFormatBase]] is 1024, [binary prefixes](http://en.wikipedia.org/wiki/Binary_prefix) (e.g. kibibyte/KiB, mebibyte/MiB, ...)
* Defaults to true meaning that binary prefixes are used (e.g. kibibyte/KiB, mebibyte/MiB, ...). * are used in the formatting result.
* See also <http://en.wikipedia.org/wiki/Binary_prefix>. *
* @param integer $value value in bytes to be formatted.
* @param integer $decimals the number of digits after the decimal point.
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result. * @return string the formatted result.
* @throws InvalidParamException if the input value is not numeric. * @throws InvalidParamException if the input value is not numeric.
* @see sizeFormat * @see sizeFormat
* @see asSize
*/ */
public function asSize($value, $verbose = false, $binaryPrefix = true) // TODO format public function asShortSize($value, $decimals = null, $options = [], $textOptions = [])
{ {
if ($value === null) { if ($value === null) {
return $this->nullDisplay; return $this->nullDisplay;
} }
$position = 0;
do { list($params, $position) = $this->formatSizeNumber($value, $decimals, $options, $textOptions);
if ($value < $this->sizeFormat['base']) {
break; if ($this->sizeFormatBase == 1024) {
switch ($position) {
case 0: return Yii::t('yii', '{n} B', $params, $this->locale);
case 1: return Yii::t('yii', '{n} KiB', $params, $this->locale);
case 2: return Yii::t('yii', '{n} MiB', $params, $this->locale);
case 3: return Yii::t('yii', '{n} GiB', $params, $this->locale);
case 4: return Yii::t('yii', '{n} TiB', $params, $this->locale);
default: return Yii::t('yii', '{n} PiB', $params, $this->locale);
} }
$value = $value / $this->sizeFormat['base']; } else {
$position++; switch ($position) {
} while ($position < 5); case 0: return Yii::t('yii', '{n} B', $params, $this->locale);
case 1: return Yii::t('yii', '{n} KB', $params, $this->locale);
case 2: return Yii::t('yii', '{n} MB', $params, $this->locale);
case 3: return Yii::t('yii', '{n} GB', $params, $this->locale);
case 4: return Yii::t('yii', '{n} TB', $params, $this->locale);
default: return Yii::t('yii', '{n} PB', $params, $this->locale);
}
}
}
$value = round($value, $this->sizeFormat['decimals']); // todo /**
$formattedValue = isset($this->sizeFormat['decimalSeparator']) ? str_replace('.', $this->sizeFormat['decimalSeparator'], $value) : $value; * Formats the value in bytes as a size in human readable form, for example `12 kilobytes`.
$params = ['n' => $formattedValue]; *
* If [[sizeFormatBase]] is 1024, [binary prefixes](http://en.wikipedia.org/wiki/Binary_prefix) (e.g. kibibyte/KiB, mebibyte/MiB, ...)
* are used in the formatting result.
*
* @param integer $value value in bytes to be formatted.
* @param integer $decimals the number of digits after the decimal point.
* @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].
* @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].
* @return string the formatted result.
* @throws InvalidParamException if the input value is not numeric.
* @see sizeFormat
* @see asShortSize
*/
public function asSize($value, $decimals = null, $options = [], $textOptions = [])
{
if ($value === null) {
return $this->nullDisplay;
}
list($params, $position) = $this->formatSizeNumber($value, $decimals, $options, $textOptions);
if ($binaryPrefix && $this->sizeFormat['base'] === 1024) { if ($this->sizeFormatBase == 1024) {
switch ($position) {
case 0: return Yii::t('yii', '{n, plural, =1{# byte} other{# bytes}}', $params, $this->locale);
case 1: return Yii::t('yii', '{n, plural, =1{# kibibyte} other{# kibibytes}}', $params, $this->locale);
case 2: return Yii::t('yii', '{n, plural, =1{# mebibyte} other{# mebibytes}}', $params, $this->locale);
case 3: return Yii::t('yii', '{n, plural, =1{# gibibyte} other{# gibibytes}}', $params, $this->locale);
case 4: return Yii::t('yii', '{n, plural, =1{# tebibyte} other{# tebibytes}}', $params, $this->locale);
default: return Yii::t('yii', '{n, plural, =1{# pebibyte} other{# pebibytes}}', $params, $this->locale);
}
} else {
switch ($position) { switch ($position) {
case 0: case 0: return Yii::t('yii', '{n, plural, =1{# byte} other{# bytes}}', $params, $this->locale);
return $verbose ? Yii::t('yii', '{n, plural, =1{# byte} other{# bytes}}', $params, $this->locale) : Yii::t('yii', '{n} B', $params, $this->locale); case 1: return Yii::t('yii', '{n, plural, =1{# kilobyte} other{# kilobytes}}', $params, $this->locale);
case 1: case 2: return Yii::t('yii', '{n, plural, =1{# megabyte} other{# megabytes}}', $params, $this->locale);
return $verbose ? Yii::t('yii', '{n, plural, =1{# kibibyte} other{# kibibytes}}', $params, $this->locale) : Yii::t('yii', '{n} KiB', $params, $this->locale); case 3: return Yii::t('yii', '{n, plural, =1{# gigabyte} other{# gigabytes}}', $params, $this->locale);
case 2: case 4: return Yii::t('yii', '{n, plural, =1{# terabyte} other{# terabytes}}', $params, $this->locale);
return $verbose ? Yii::t('yii', '{n, plural, =1{# mebibyte} other{# mebibytes}}', $params, $this->locale) : Yii::t('yii', '{n} MiB', $params, $this->locale); default: return Yii::t('yii', '{n, plural, =1{# petabyte} other{# petabytes}}', $params, $this->locale);
case 3:
return $verbose ? Yii::t('yii', '{n, plural, =1{# gibibyte} other{# gibibytes}}', $params, $this->locale) : Yii::t('yii', '{n} GiB', $params, $this->locale);
case 4:
return $verbose ? Yii::t('yii', '{n, plural, =1{# tebibyte} other{# tebibytes}}', $params, $this->locale) : Yii::t('yii', '{n} TiB', $params, $this->locale);
default:
return $verbose ? Yii::t('yii', '{n, plural, =1{# pebibyte} other{# pebibytes}}', $params, $this->locale) : Yii::t('yii', '{n} PiB', $params, $this->locale);
} }
} }
}
private function formatSizeNumber($value, $decimals, $options, $textOptions)
{
if (is_string($value) && is_numeric($value)) {
$value = (int) $value;
}
if (!is_numeric($value)) {
throw new InvalidParamException("'$value' is not a numeric value.");
}
$position = 0;
do {
if ($value < $this->sizeFormatBase) {
break;
}
$value = $value / $this->sizeFormatBase;
$position++;
} while ($position < 5);
switch ($position) { // no decimals for bytes
case 0: if ($position === 0) {
return $verbose ? Yii::t('yii', '{n, plural, =1{# byte} other{# bytes}}', $params, $this->locale) : Yii::t('yii', '{n} B', $params, $this->locale); $decimals = 0;
case 1: } elseif ($decimals !== null) {
return $verbose ? Yii::t('yii', '{n, plural, =1{# kilobyte} other{# kilobytes}}', $params, $this->locale) : Yii::t('yii', '{n} KB', $params, $this->locale); $value = round($value, $decimals);
case 2:
return $verbose ? Yii::t('yii', '{n, plural, =1{# megabyte} other{# megabytes}}', $params, $this->locale) : Yii::t('yii', '{n} MB', $params, $this->locale);
case 3:
return $verbose ? Yii::t('yii', '{n, plural, =1{# gigabyte} other{# gigabytes}}', $params, $this->locale) : Yii::t('yii', '{n} GB', $params, $this->locale);
case 4:
return $verbose ? Yii::t('yii', '{n, plural, =1{# terabyte} other{# terabytes}}', $params, $this->locale) : Yii::t('yii', '{n} TB', $params, $this->locale);
default:
return $verbose ? Yii::t('yii', '{n, plural, =1{# petabyte} other{# petabytes}}', $params, $this->locale) : Yii::t('yii', '{n} PB', $params, $this->locale);
} }
// disable grouping for edge cases like 1023 to get 1023 B instead of 1,023 B
$oldThousandSeparator = $this->thousandSeparator;
$this->thousandSeparator = '';
$options[NumberFormatter::GROUPING_USED] = false;
// format the size value
$params = ['n' => $this->asDecimal($value, $decimals, $options, $textOptions)];
$this->thousandSeparator = $oldThousandSeparator;
return [$params, $position];
} }
/** /**
...@@ -1180,10 +1240,16 @@ class Formatter extends Component ...@@ -1180,10 +1240,16 @@ class Formatter extends Component
$formatter->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimals); $formatter->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimals);
} }
foreach (($this->numberFormatterOptions + $options) as $name => $value) { foreach ($this->numberFormatterOptions as $name => $value) {
$formatter->setAttribute($name, $value); $formatter->setAttribute($name, $value);
} }
foreach (($this->numberFormatterTextOptions + $textOptions) as $name => $attribute) { foreach ($options as $name => $value) {
$formatter->setAttribute($name, $value);
}
foreach ($this->numberFormatterTextOptions as $name => $attribute) {
$formatter->setTextAttribute($name, $attribute);
}
foreach ($textOptions as $name => $attribute) {
$formatter->setTextAttribute($name, $attribute); $formatter->setTextAttribute($name, $attribute);
} }
return $formatter; return $formatter;
......
...@@ -647,34 +647,127 @@ class FormatterTest extends TestCase ...@@ -647,34 +647,127 @@ class FormatterTest extends TestCase
$this->assertSame($this->formatter->nullDisplay, $this->formatter->asOrdinal(null)); $this->assertSame($this->formatter->nullDisplay, $this->formatter->asOrdinal(null));
} }
public function testIntlAsShortSize()
{
$this->formatter->numberFormatterOptions = [
\NumberFormatter::MIN_FRACTION_DIGITS => 0,
\NumberFormatter::MAX_FRACTION_DIGITS => 2,
];
// tests for base 1000
$this->formatter->sizeFormatBase = 1000;
$this->assertSame("999 B", $this->formatter->asShortSize(999));
$this->assertSame("1.05 MB", $this->formatter->asShortSize(1024 * 1024));
$this->assertSame("1 KB", $this->formatter->asShortSize(1000));
$this->assertSame("1.02 KB", $this->formatter->asShortSize(1023));
$this->assertNotEquals("3 PB", $this->formatter->asShortSize(3 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000)); // this is 3 EB not 3 PB
// tests for base 1024
$this->formatter->sizeFormatBase = 1024;
$this->assertSame("1 KiB", $this->formatter->asShortSize(1024));
$this->assertSame("1 MiB", $this->formatter->asShortSize(1024 * 1024));
// https://github.com/yiisoft/yii2/issues/4960
$this->assertSame("1023 B", $this->formatter->asShortSize(1023));
$this->assertSame("5 GiB", $this->formatter->asShortSize(5 * 1024 * 1024 * 1024));
$this->assertNotEquals("5 PiB", $this->formatter->asShortSize(5 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024)); // this is 5 EiB not 5 PiB
//$this->assertSame("1 YiB", $this->formatter->asShortSize(pow(2, 80)));
$this->assertSame("2 GiB", $this->formatter->asShortSize(2147483647)); // round 1.999 up to 2
$this->formatter->decimalSeparator = ',';
$this->formatter->numberFormatterOptions = [];
$this->assertSame("1,001 KiB", $this->formatter->asShortSize(1025, 3));
// null display
$this->assertSame($this->formatter->nullDisplay, $this->formatter->asSize(null));
}
public function testAsShortSize()
{
// tests for base 1000
$this->formatter->sizeFormatBase = 1000;
$this->assertSame("999 B", $this->formatter->asShortSize(999));
$this->assertSame("1.05 MB", $this->formatter->asShortSize(1024 * 1024));
$this->assertSame("1.0486 MB", $this->formatter->asShortSize(1024 * 1024, 4));
$this->assertSame("1.00 KB", $this->formatter->asShortSize(1000));
$this->assertSame("1.02 KB", $this->formatter->asShortSize(1023));
$this->assertNotEquals("3 PB", $this->formatter->asShortSize(3 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000)); // this is 3 EB not 3 PB
// tests for base 1024
$this->formatter->sizeFormatBase = 1024;
$this->assertSame("1.00 KiB", $this->formatter->asShortSize(1024));
$this->assertSame("1.00 MiB", $this->formatter->asShortSize(1024 * 1024));
// https://github.com/yiisoft/yii2/issues/4960
$this->assertSame("1023 B", $this->formatter->asShortSize(1023));
$this->assertSame("5.00 GiB", $this->formatter->asShortSize(5 * 1024 * 1024 * 1024));
$this->assertNotEquals("5.00 PiB", $this->formatter->asShortSize(5 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024)); // this is 5 EiB not 5 PiB
//$this->assertSame("1 YiB", $this->formatter->asShortSize(pow(2, 80)));
$this->assertSame("2.00 GiB", $this->formatter->asShortSize(2147483647)); // round 1.999 up to 2
$this->formatter->decimalSeparator = ',';
$this->assertSame("1,001 KiB", $this->formatter->asShortSize(1025, 3));
// null display
$this->assertSame($this->formatter->nullDisplay, $this->formatter->asSize(null));
}
public function testIntlAsSize() public function testIntlAsSize()
{ {
$this->testAsSize(); $this->formatter->numberFormatterOptions = [
\NumberFormatter::MIN_FRACTION_DIGITS => 0,
\NumberFormatter::MAX_FRACTION_DIGITS => 2,
];
// tests for base 1000
$this->formatter->sizeFormatBase = 1000;
$this->assertSame("999 bytes", $this->formatter->asSize(999));
$this->assertSame("1.05 megabytes", $this->formatter->asSize(1024 * 1024));
$this->assertSame("1 kilobyte", $this->formatter->asSize(1000));
$this->assertSame("1.02 kilobytes", $this->formatter->asSize(1023));
$this->assertSame("3 gigabytes", $this->formatter->asSize(3 * 1000 * 1000 * 1000));
$this->assertNotEquals("3 PB", $this->formatter->asSize(3 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000)); // this is 3 EB not 3 PB
// tests for base 1024
$this->formatter->sizeFormatBase = 1024;
$this->assertSame("1 kibibyte", $this->formatter->asSize(1024));
$this->assertSame("1 mebibyte", $this->formatter->asSize(1024 * 1024));
// https://github.com/yiisoft/yii2/issues/4960
// $this->assertSame("1023 bytes", $this->formatter->asSize(1023));
$this->assertSame("5 gibibytes", $this->formatter->asSize(5 * 1024 * 1024 * 1024));
$this->assertNotEquals("5 pibibytes", $this->formatter->asSize(5 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024)); // this is 5 EiB not 5 PiB
//$this->assertSame("1 YiB", $this->formatter->asSize(pow(2, 80)));
$this->assertSame("2 gibibytes", $this->formatter->asSize(2147483647)); // round 1.999 up to 2
$this->formatter->decimalSeparator = ',';
$this->formatter->numberFormatterOptions = [];
// https://github.com/yiisoft/yii2/issues/4960
// $this->assertSame("1,001 KiB", $this->formatter->asSize(1025, 3));
// null display
$this->assertSame($this->formatter->nullDisplay, $this->formatter->asSize(null));
} }
public function testAsSize() public function testAsSize()
{ {
// tests for base 1000 // tests for base 1000
$this->formatter->sizeFormat['base'] = 1000; $this->formatter->sizeFormatBase = 1000;
$this->assertSame("999 B", $this->formatter->asSize(999)); $this->assertSame("999 bytes", $this->formatter->asSize(999));
$this->assertSame("1.05 MB", $this->formatter->asSize(1024 * 1024)); $this->assertSame("1.05 megabytes", $this->formatter->asSize(1024 * 1024));
$this->assertSame("1.05 MB", $this->formatter->asSize(1024 * 1024, false, false)); // https://github.com/yiisoft/yii2/issues/4960
$this->assertSame("1 KB", $this->formatter->asSize(1000)); // $this->assertSame("1.0486 megabytes", $this->formatter->asSize(1024 * 1024, 4));
$this->assertSame("1.02 KB", $this->formatter->asSize(1023)); $this->assertSame("1 kilobyte", $this->formatter->asSize(1000));
$this->assertSame("3 gigabytes", $this->formatter->asSize(3 * 1000 * 1000 * 1000, true)); $this->assertSame("1.02 kilobytes", $this->formatter->asSize(1023));
$this->assertSame("3 gigabytes", $this->formatter->asSize(3 * 1000 * 1000 * 1000));
$this->assertNotEquals("3 PB", $this->formatter->asSize(3 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000)); // this is 3 EB not 3 PB $this->assertNotEquals("3 PB", $this->formatter->asSize(3 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000)); // this is 3 EB not 3 PB
// tests for base 1024 // tests for base 1024
$this->formatter->sizeFormat['base'] = 1024; $this->formatter->sizeFormatBase = 1024;
$this->assertSame("1 KiB", $this->formatter->asSize(1024)); $this->assertSame("1 kibibyte", $this->formatter->asSize(1024));
$this->assertSame("1 MB", $this->formatter->asSize(1024 * 1024, false, false)); $this->assertSame("1 mebibyte", $this->formatter->asSize(1024 * 1024));
$this->assertSame("1023 B", $this->formatter->asSize(1023)); // https://github.com/yiisoft/yii2/issues/4960
$this->assertSame("5 GiB", $this->formatter->asSize(5 * 1024 * 1024 * 1024)); // $this->assertSame("1023 B", $this->formatter->asSize(1023));
$this->assertSame("5 gibibytes", $this->formatter->asSize(5 * 1024 * 1024 * 1024,true)); $this->assertSame("5 gibibytes", $this->formatter->asSize(5 * 1024 * 1024 * 1024));
$this->assertNotEquals("5 PiB", $this->formatter->asSize(5 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024)); // this is 5 EiB not 5 PiB $this->assertNotEquals("5 pibibytes", $this->formatter->asSize(5 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024)); // this is 5 EiB not 5 PiB
//$this->assertSame("1 YiB", $this->formatter->asSize(pow(2, 80))); //$this->assertSame("1 YiB", $this->formatter->asSize(pow(2, 80)));
$this->assertSame("2 GiB", $this->formatter->asSize(2147483647)); // round 1.999 up to 2 $this->assertSame("2 gibibytes", $this->formatter->asSize(2147483647)); // round 1.999 up to 2
$this->formatter->sizeFormat['decimalSeparator'] = ','; $this->formatter->decimalSeparator = ',';
$this->formatter->sizeFormat['decimals'] = 3; $this->formatter->numberFormatterOptions = [];
$this->assertSame("1,001 KiB", $this->formatter->asSize(1025)); // https://github.com/yiisoft/yii2/issues/4960
// $this->assertSame("1,001 kibibytes", $this->formatter->asSize(1025));
// null display
$this->assertSame($this->formatter->nullDisplay, $this->formatter->asSize(null));
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment