890 lines
32 KiB
PHP
890 lines
32 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of the Symfony package.
|
|
*
|
|
* (c) Fabien Potencier <fabien@symfony.com>
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Symfony\Component\Intl\Tests\NumberFormatter;
|
|
|
|
use PHPUnit\Framework\Error\Warning;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Symfony\Component\Intl\Globals\IntlGlobals;
|
|
use Symfony\Component\Intl\NumberFormatter\NumberFormatter;
|
|
use Symfony\Component\Intl\Util\IntlTestHelper;
|
|
|
|
/**
|
|
* Note that there are some values written like -2147483647 - 1. This is the lower 32bit int max and is a known
|
|
* behavior of PHP.
|
|
*/
|
|
abstract class AbstractNumberFormatterTest extends TestCase
|
|
{
|
|
/**
|
|
* @dataProvider formatCurrencyWithDecimalStyleProvider
|
|
*/
|
|
public function testFormatCurrencyWithDecimalStyle($value, $currency, $expected)
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$this->assertEquals($expected, $formatter->formatCurrency($value, $currency));
|
|
}
|
|
|
|
public function formatCurrencyWithDecimalStyleProvider()
|
|
{
|
|
return [
|
|
[100, 'ALL', '100'],
|
|
[100, 'BRL', '100'],
|
|
[100, 'CRC', '100'],
|
|
[100, 'JPY', '100'],
|
|
[100, 'CHF', '100'],
|
|
[-100, 'ALL', '-100'],
|
|
[-100, 'BRL', '-100'],
|
|
[-100, 'CRC', '-100'],
|
|
[-100, 'JPY', '-100'],
|
|
[-100, 'CHF', '-100'],
|
|
[1000.12, 'ALL', '1,000.12'],
|
|
[1000.12, 'BRL', '1,000.12'],
|
|
[1000.12, 'CRC', '1,000.12'],
|
|
[1000.12, 'JPY', '1,000.12'],
|
|
[1000.12, 'CHF', '1,000.12'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatCurrencyWithCurrencyStyleProvider
|
|
*/
|
|
public function testFormatCurrencyWithCurrencyStyle($value, $currency, $expected)
|
|
{
|
|
IntlTestHelper::requireIntl($this, '63.1');
|
|
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
|
|
$this->assertEquals($expected, $formatter->formatCurrency($value, $currency));
|
|
}
|
|
|
|
public function formatCurrencyWithCurrencyStyleProvider()
|
|
{
|
|
return [
|
|
[100, 'ALL', "ALL\xc2\xa0100"],
|
|
[-100, 'ALL', "-ALL\xc2\xa0100"],
|
|
[1000.12, 'ALL', "ALL\xc2\xa01,000"],
|
|
|
|
[100, 'JPY', '¥100'],
|
|
[-100, 'JPY', '-¥100'],
|
|
[1000.12, 'JPY', '¥1,000'],
|
|
|
|
[100, 'EUR', '€100.00'],
|
|
[-100, 'EUR', '-€100.00'],
|
|
[1000.12, 'EUR', '€1,000.12'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatCurrencyWithCurrencyStyleCostaRicanColonsRoundingProvider
|
|
*/
|
|
public function testFormatCurrencyWithCurrencyStyleCostaRicanColonsRounding($value, $currency, $symbol, $expected)
|
|
{
|
|
IntlTestHelper::requireIntl($this, '63.1');
|
|
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
|
|
$this->assertEquals(sprintf($expected, $symbol), $formatter->formatCurrency($value, $currency));
|
|
}
|
|
|
|
public function formatCurrencyWithCurrencyStyleCostaRicanColonsRoundingProvider()
|
|
{
|
|
return [
|
|
[100, 'CRC', 'CRC', "%s\xc2\xa0100.00"],
|
|
[-100, 'CRC', 'CRC', "-%s\xc2\xa0100.00"],
|
|
[1000.12, 'CRC', 'CRC', "%s\xc2\xa01,000.12"],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatCurrencyWithCurrencyStyleBrazilianRealRoundingProvider
|
|
*/
|
|
public function testFormatCurrencyWithCurrencyStyleBrazilianRealRounding($value, $currency, $symbol, $expected)
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
|
|
$this->assertEquals(sprintf($expected, $symbol), $formatter->formatCurrency($value, $currency));
|
|
}
|
|
|
|
public function formatCurrencyWithCurrencyStyleBrazilianRealRoundingProvider()
|
|
{
|
|
return [
|
|
[100, 'BRL', 'R', '%s$100.00'],
|
|
[-100, 'BRL', 'R', '-%s$100.00'],
|
|
[1000.12, 'BRL', 'R', '%s$1,000.12'],
|
|
|
|
// Rounding checks
|
|
[1000.121, 'BRL', 'R', '%s$1,000.12'],
|
|
[1000.123, 'BRL', 'R', '%s$1,000.12'],
|
|
[1000.125, 'BRL', 'R', '%s$1,000.12'],
|
|
[1000.127, 'BRL', 'R', '%s$1,000.13'],
|
|
[1000.129, 'BRL', 'R', '%s$1,000.13'],
|
|
[11.50999, 'BRL', 'R', '%s$11.51'],
|
|
[11.9999464, 'BRL', 'R', '%s$12.00'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatCurrencyWithCurrencyStyleSwissRoundingProvider
|
|
*/
|
|
public function testFormatCurrencyWithCurrencyStyleSwissRounding($value, $currency, $symbol, $expected)
|
|
{
|
|
IntlTestHelper::requireIntl($this, '62.1');
|
|
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
|
|
$this->assertEquals(sprintf($expected, $symbol), $formatter->formatCurrency($value, $currency));
|
|
}
|
|
|
|
public function formatCurrencyWithCurrencyStyleSwissRoundingProvider()
|
|
{
|
|
return [
|
|
[100, 'CHF', 'CHF', "%s\xc2\xa0100.00"],
|
|
[-100, 'CHF', 'CHF', "-%s\xc2\xa0100.00"],
|
|
[1000.12, 'CHF', 'CHF', "%s\xc2\xa01,000.12"],
|
|
['1000.12', 'CHF', 'CHF', "%s\xc2\xa01,000.12"],
|
|
|
|
// Rounding checks
|
|
[1000.121, 'CHF', 'CHF', "%s\xc2\xa01,000.12"],
|
|
[1000.123, 'CHF', 'CHF', "%s\xc2\xa01,000.12"],
|
|
[1000.125, 'CHF', 'CHF', "%s\xc2\xa01,000.12"],
|
|
[1000.127, 'CHF', 'CHF', "%s\xc2\xa01,000.13"],
|
|
[1000.129, 'CHF', 'CHF', "%s\xc2\xa01,000.13"],
|
|
|
|
[1200000.00, 'CHF', 'CHF', "%s\xc2\xa01,200,000.00"],
|
|
[1200000.1, 'CHF', 'CHF', "%s\xc2\xa01,200,000.10"],
|
|
[1200000.10, 'CHF', 'CHF', "%s\xc2\xa01,200,000.10"],
|
|
[1200000.101, 'CHF', 'CHF', "%s\xc2\xa01,200,000.10"],
|
|
];
|
|
}
|
|
|
|
public function testFormat()
|
|
{
|
|
$errorCode = IntlGlobals::U_ZERO_ERROR;
|
|
$errorMessage = 'U_ZERO_ERROR';
|
|
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$this->assertSame('9.555', $formatter->format(9.555));
|
|
|
|
$this->assertSame($errorMessage, $this->getIntlErrorMessage());
|
|
$this->assertSame($errorCode, $this->getIntlErrorCode());
|
|
$this->assertFalse($this->isIntlFailure($this->getIntlErrorCode()));
|
|
$this->assertSame($errorMessage, $formatter->getErrorMessage());
|
|
$this->assertSame($errorCode, $formatter->getErrorCode());
|
|
$this->assertFalse($this->isIntlFailure($formatter->getErrorCode()));
|
|
}
|
|
|
|
public function testFormatWithCurrencyStyle()
|
|
{
|
|
IntlTestHelper::requireIntl($this, '63.1');
|
|
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
|
|
$this->assertEquals('¤1.00', $formatter->format(1));
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatTypeInt32Provider
|
|
*/
|
|
public function testFormatTypeInt32($formatter, $value, $expected, $message = '')
|
|
{
|
|
$formattedValue = $formatter->format($value, NumberFormatter::TYPE_INT32);
|
|
$this->assertEquals($expected, $formattedValue, $message);
|
|
}
|
|
|
|
public function formatTypeInt32Provider()
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
|
|
$message = '->format() TYPE_INT32 formats inconsistently an integer if out of the 32 bit range.';
|
|
|
|
return [
|
|
[$formatter, 1, '1'],
|
|
[$formatter, 1.1, '1'],
|
|
[$formatter, 2147483648, '-2,147,483,648', $message],
|
|
[$formatter, -2147483649, '2,147,483,647', $message],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatTypeInt32WithCurrencyStyleProvider
|
|
*/
|
|
public function testFormatTypeInt32WithCurrencyStyle($formatter, $value, $expected, $message = '')
|
|
{
|
|
IntlTestHelper::requireIntl($this, '63.1');
|
|
|
|
$formattedValue = $formatter->format($value, NumberFormatter::TYPE_INT32);
|
|
$this->assertEquals($expected, $formattedValue, $message);
|
|
}
|
|
|
|
public function formatTypeInt32WithCurrencyStyleProvider()
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
|
|
|
|
$message = '->format() TYPE_INT32 formats inconsistently an integer if out of the 32 bit range.';
|
|
|
|
return [
|
|
[$formatter, 1, '¤1.00'],
|
|
[$formatter, 1.1, '¤1.00'],
|
|
[$formatter, 2147483648, '-¤2,147,483,648.00', $message],
|
|
[$formatter, -2147483649, '¤2,147,483,647.00', $message],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* The parse() method works differently with integer out of the 32 bit range. format() works fine.
|
|
*
|
|
* @dataProvider formatTypeInt64Provider
|
|
*/
|
|
public function testFormatTypeInt64($formatter, $value, $expected)
|
|
{
|
|
$formattedValue = $formatter->format($value, NumberFormatter::TYPE_INT64);
|
|
$this->assertEquals($expected, $formattedValue);
|
|
}
|
|
|
|
public function formatTypeInt64Provider()
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
|
|
return [
|
|
[$formatter, 1, '1'],
|
|
[$formatter, 1.1, '1'],
|
|
[$formatter, 2147483648, '2,147,483,648'],
|
|
[$formatter, -2147483649, '-2,147,483,649'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatTypeInt64WithCurrencyStyleProvider
|
|
*/
|
|
public function testFormatTypeInt64WithCurrencyStyle($formatter, $value, $expected)
|
|
{
|
|
IntlTestHelper::requireIntl($this, '63.1');
|
|
|
|
$formattedValue = $formatter->format($value, NumberFormatter::TYPE_INT64);
|
|
$this->assertEquals($expected, $formattedValue);
|
|
}
|
|
|
|
public function formatTypeInt64WithCurrencyStyleProvider()
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
|
|
|
|
return [
|
|
[$formatter, 1, '¤1.00'],
|
|
[$formatter, 1.1, '¤1.00'],
|
|
[$formatter, 2147483648, '¤2,147,483,648.00'],
|
|
[$formatter, -2147483649, '-¤2,147,483,649.00'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatTypeDoubleProvider
|
|
*/
|
|
public function testFormatTypeDouble($formatter, $value, $expected)
|
|
{
|
|
$formattedValue = $formatter->format($value, NumberFormatter::TYPE_DOUBLE);
|
|
$this->assertEquals($expected, $formattedValue);
|
|
}
|
|
|
|
public function formatTypeDoubleProvider()
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
|
|
return [
|
|
[$formatter, 1, '1'],
|
|
[$formatter, 1.1, '1.1'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatTypeDoubleWithCurrencyStyleProvider
|
|
*/
|
|
public function testFormatTypeDoubleWithCurrencyStyle($formatter, $value, $expected)
|
|
{
|
|
IntlTestHelper::requireIntl($this, '63.1');
|
|
|
|
$formattedValue = $formatter->format($value, NumberFormatter::TYPE_DOUBLE);
|
|
$this->assertEquals($expected, $formattedValue);
|
|
}
|
|
|
|
public function formatTypeDoubleWithCurrencyStyleProvider()
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
|
|
|
|
return [
|
|
[$formatter, 1, '¤1.00'],
|
|
[$formatter, 1.1, '¤1.10'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatTypeCurrencyProvider
|
|
*/
|
|
public function testFormatTypeCurrency($formatter, $value)
|
|
{
|
|
if (\PHP_VERSION_ID >= 80000) {
|
|
$this->expectException(\ValueError::class);
|
|
} elseif (method_exists($this, 'expectWarning')) {
|
|
$this->expectWarning();
|
|
} else {
|
|
$this->expectException(Warning::class);
|
|
}
|
|
|
|
$formatter->format($value, NumberFormatter::TYPE_CURRENCY);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatTypeCurrencyProvider
|
|
*/
|
|
public function testFormatTypeCurrencyReturn($formatter, $value)
|
|
{
|
|
if (\PHP_VERSION_ID >= 80000) {
|
|
$this->expectException(\ValueError::class);
|
|
}
|
|
|
|
$this->assertFalse(@$formatter->format($value, NumberFormatter::TYPE_CURRENCY));
|
|
}
|
|
|
|
public function formatTypeCurrencyProvider()
|
|
{
|
|
$df = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$cf = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
|
|
|
|
return [
|
|
[$df, 1],
|
|
[$cf, 1],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatFractionDigitsProvider
|
|
*/
|
|
public function testFormatFractionDigits($value, $expected, $fractionDigits = null, $expectedFractionDigits = 1)
|
|
{
|
|
IntlTestHelper::requireIntl($this, '62.1');
|
|
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
|
|
$attributeRet = null;
|
|
if (null !== $fractionDigits) {
|
|
$attributeRet = $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, $fractionDigits);
|
|
}
|
|
|
|
$formattedValue = $formatter->format($value);
|
|
$this->assertSame($expected, $formattedValue);
|
|
$this->assertSame($expectedFractionDigits, $formatter->getAttribute(NumberFormatter::FRACTION_DIGITS));
|
|
|
|
if (null !== $attributeRet) {
|
|
$this->assertTrue($attributeRet);
|
|
}
|
|
}
|
|
|
|
public function formatFractionDigitsProvider()
|
|
{
|
|
yield [1.123, '1.123', null, 0];
|
|
yield [1.123, '1', 0, 0];
|
|
yield [1.123, '1.1', 1, 1];
|
|
yield [1.123, '1.12', 2, 2];
|
|
yield [1.123, '1.123', -1, 0];
|
|
|
|
if (\PHP_VERSION_ID < 80000) {
|
|
// This dataset will produce a TypeError on php 8.
|
|
yield [1.123, '1', 'abc', 0];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatGroupingUsedProvider
|
|
*/
|
|
public function testFormatGroupingUsed($value, $expected, $groupingUsed = null, $expectedGroupingUsed = 1)
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
|
|
$attributeRet = null;
|
|
if (null !== $groupingUsed) {
|
|
$attributeRet = $formatter->setAttribute(NumberFormatter::GROUPING_USED, $groupingUsed);
|
|
}
|
|
|
|
$formattedValue = $formatter->format($value);
|
|
$this->assertSame($expected, $formattedValue);
|
|
$this->assertSame($expectedGroupingUsed, $formatter->getAttribute(NumberFormatter::GROUPING_USED));
|
|
|
|
if (null !== $attributeRet) {
|
|
$this->assertTrue($attributeRet);
|
|
}
|
|
}
|
|
|
|
public function formatGroupingUsedProvider()
|
|
{
|
|
yield [1000, '1,000', null, 1];
|
|
yield [1000, '1000', 0, 0];
|
|
yield [1000, '1,000', 1, 1];
|
|
yield [1000, '1,000', 2, 1];
|
|
yield [1000, '1,000', -1, 1];
|
|
|
|
if (\PHP_VERSION_ID < 80000) {
|
|
// This dataset will produce a TypeError on php 8.
|
|
yield [1000, '1000', 'abc', 0];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatRoundingModeRoundHalfUpProvider
|
|
*/
|
|
public function testFormatRoundingModeHalfUp($value, $expected)
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
|
|
|
|
$formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_HALFUP);
|
|
$this->assertSame($expected, $formatter->format($value), '->format() with ROUND_HALFUP rounding mode.');
|
|
}
|
|
|
|
public function formatRoundingModeRoundHalfUpProvider()
|
|
{
|
|
// The commented value is differently rounded by intl's NumberFormatter in 32 and 64 bit architectures
|
|
return [
|
|
[1.121, '1.12'],
|
|
[1.123, '1.12'],
|
|
// [1.125, '1.13'],
|
|
[1.127, '1.13'],
|
|
[1.129, '1.13'],
|
|
[1020 / 100, '10.20'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatRoundingModeRoundHalfDownProvider
|
|
*/
|
|
public function testFormatRoundingModeHalfDown($value, $expected)
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
|
|
|
|
$formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_HALFDOWN);
|
|
$this->assertSame($expected, $formatter->format($value), '->format() with ROUND_HALFDOWN rounding mode.');
|
|
}
|
|
|
|
public function formatRoundingModeRoundHalfDownProvider()
|
|
{
|
|
return [
|
|
[1.121, '1.12'],
|
|
[1.123, '1.12'],
|
|
[1.125, '1.12'],
|
|
[1.127, '1.13'],
|
|
[1.129, '1.13'],
|
|
[1020 / 100, '10.20'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatRoundingModeRoundHalfEvenProvider
|
|
*/
|
|
public function testFormatRoundingModeHalfEven($value, $expected)
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
|
|
|
|
$formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_HALFEVEN);
|
|
$this->assertSame($expected, $formatter->format($value), '->format() with ROUND_HALFEVEN rounding mode.');
|
|
}
|
|
|
|
public function formatRoundingModeRoundHalfEvenProvider()
|
|
{
|
|
return [
|
|
[1.121, '1.12'],
|
|
[1.123, '1.12'],
|
|
[1.125, '1.12'],
|
|
[1.127, '1.13'],
|
|
[1.129, '1.13'],
|
|
[1020 / 100, '10.20'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatRoundingModeRoundCeilingProvider
|
|
*/
|
|
public function testFormatRoundingModeCeiling($value, $expected)
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
|
|
|
|
$formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_CEILING);
|
|
$this->assertSame($expected, $formatter->format($value), '->format() with ROUND_CEILING rounding mode.');
|
|
}
|
|
|
|
public function formatRoundingModeRoundCeilingProvider()
|
|
{
|
|
return [
|
|
[1.123, '1.13'],
|
|
[1.125, '1.13'],
|
|
[1.127, '1.13'],
|
|
[-1.123, '-1.12'],
|
|
[-1.125, '-1.12'],
|
|
[-1.127, '-1.12'],
|
|
[1020 / 100, '10.20'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatRoundingModeRoundFloorProvider
|
|
*/
|
|
public function testFormatRoundingModeFloor($value, $expected)
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
|
|
|
|
$formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_FLOOR);
|
|
$this->assertSame($expected, $formatter->format($value), '->format() with ROUND_FLOOR rounding mode.');
|
|
}
|
|
|
|
public function formatRoundingModeRoundFloorProvider()
|
|
{
|
|
return [
|
|
[1.123, '1.12'],
|
|
[1.125, '1.12'],
|
|
[1.127, '1.12'],
|
|
[-1.123, '-1.13'],
|
|
[-1.125, '-1.13'],
|
|
[-1.127, '-1.13'],
|
|
[1020 / 100, '10.20'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatRoundingModeRoundDownProvider
|
|
*/
|
|
public function testFormatRoundingModeDown($value, $expected)
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
|
|
|
|
$formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_DOWN);
|
|
$this->assertSame($expected, $formatter->format($value), '->format() with ROUND_DOWN rounding mode.');
|
|
}
|
|
|
|
public function formatRoundingModeRoundDownProvider()
|
|
{
|
|
return [
|
|
[1.123, '1.12'],
|
|
[1.125, '1.12'],
|
|
[1.127, '1.12'],
|
|
[-1.123, '-1.12'],
|
|
[-1.125, '-1.12'],
|
|
[-1.127, '-1.12'],
|
|
[1020 / 100, '10.20'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider formatRoundingModeRoundUpProvider
|
|
*/
|
|
public function testFormatRoundingModeUp($value, $expected)
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
|
|
|
|
$formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_UP);
|
|
$this->assertSame($expected, $formatter->format($value), '->format() with ROUND_UP rounding mode.');
|
|
}
|
|
|
|
public function formatRoundingModeRoundUpProvider()
|
|
{
|
|
return [
|
|
[1.123, '1.13'],
|
|
[1.125, '1.13'],
|
|
[1.127, '1.13'],
|
|
[-1.123, '-1.13'],
|
|
[-1.125, '-1.13'],
|
|
[-1.127, '-1.13'],
|
|
[1020 / 100, '10.20'],
|
|
];
|
|
}
|
|
|
|
public function testGetLocale()
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$this->assertEquals('en', $formatter->getLocale());
|
|
}
|
|
|
|
public function testGetSymbol()
|
|
{
|
|
$decimalFormatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$currencyFormatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
|
|
|
|
$r = new \ReflectionProperty('Symfony\Component\Intl\NumberFormatter\NumberFormatter', 'enSymbols');
|
|
$r->setAccessible(true);
|
|
$expected = $r->getValue();
|
|
|
|
for ($i = 0; $i <= 17; ++$i) {
|
|
$this->assertSame($expected[1][$i], $decimalFormatter->getSymbol($i));
|
|
$this->assertSame($expected[2][$i], $currencyFormatter->getSymbol($i));
|
|
}
|
|
}
|
|
|
|
public function testGetTextAttribute()
|
|
{
|
|
IntlTestHelper::requireIntl($this, '63.1');
|
|
|
|
$decimalFormatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$currencyFormatter = $this->getNumberFormatter('en', NumberFormatter::CURRENCY);
|
|
|
|
$r = new \ReflectionProperty('Symfony\Component\Intl\NumberFormatter\NumberFormatter', 'enTextAttributes');
|
|
$r->setAccessible(true);
|
|
$expected = $r->getValue();
|
|
|
|
for ($i = 0; $i <= 5; ++$i) {
|
|
$this->assertSame($expected[1][$i], $decimalFormatter->getTextAttribute($i));
|
|
$this->assertSame($expected[2][$i], $currencyFormatter->getTextAttribute($i));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider parseProvider
|
|
*/
|
|
public function testParse($value, $expected, $message, $expectedPosition, $groupingUsed = true)
|
|
{
|
|
IntlTestHelper::requireIntl($this, '62.1');
|
|
|
|
$position = 0;
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$formatter->setAttribute(NumberFormatter::GROUPING_USED, $groupingUsed);
|
|
$parsedValue = $formatter->parse($value, NumberFormatter::TYPE_DOUBLE, $position);
|
|
$this->assertSame($expected, $parsedValue, $message);
|
|
$this->assertSame($expectedPosition, $position, $message);
|
|
|
|
if (false === $expected) {
|
|
$errorCode = IntlGlobals::U_PARSE_ERROR;
|
|
$errorMessage = 'Number parsing failed: U_PARSE_ERROR';
|
|
} else {
|
|
$errorCode = IntlGlobals::U_ZERO_ERROR;
|
|
$errorMessage = 'U_ZERO_ERROR';
|
|
}
|
|
|
|
$this->assertSame($errorMessage, $this->getIntlErrorMessage());
|
|
$this->assertSame($errorCode, $this->getIntlErrorCode());
|
|
$this->assertSame(0 !== $errorCode, $this->isIntlFailure($this->getIntlErrorCode()));
|
|
$this->assertSame($errorMessage, $formatter->getErrorMessage());
|
|
$this->assertSame($errorCode, $formatter->getErrorCode());
|
|
$this->assertSame(0 !== $errorCode, $this->isIntlFailure($formatter->getErrorCode()));
|
|
}
|
|
|
|
public function parseProvider()
|
|
{
|
|
return [
|
|
['prefix1', false, '->parse() does not parse a number with a string prefix.', 0],
|
|
['prefix1', false, '->parse() does not parse a number with a string prefix.', 0, false],
|
|
['1.4suffix', (float) 1.4, '->parse() parses a number with a string suffix.', 3],
|
|
['1.4suffix', (float) 1.4, '->parse() parses a number with a string suffix.', 3, false],
|
|
['1,234.4suffix', 1234.4, '->parse() parses a number with a string suffix.', 7],
|
|
['1,234.4suffix', 1.0, '->parse() parses a number with a string suffix.', 1, false],
|
|
['-.4suffix', (float) -0.4, '->parse() parses a negative dot float with suffix.', 3],
|
|
['-.4suffix', (float) -0.4, '->parse() parses a negative dot float with suffix.', 3, false],
|
|
[',4', false, '->parse() does not parse when invalid grouping used.', 0],
|
|
[',4', false, '->parse() does not parse when invalid grouping used.', 0, false],
|
|
['123,4', false, '->parse() does not parse when invalid grouping used.', 0],
|
|
['123,4', 123.0, '->parse() truncates invalid grouping when grouping is disabled.', 3, false],
|
|
['123,a4', 123.0, '->parse() truncates a string suffix.', 3],
|
|
['123,a4', 123.0, '->parse() truncates a string suffix.', 3, false],
|
|
['-123,4', false, '->parse() does not parse when invalid grouping used.', 1],
|
|
['-123,4', -123.0, '->parse() truncates invalid grouping when grouping is disabled.', 4, false],
|
|
['-123,4567', false, '->parse() does not parse when invalid grouping used.', 1],
|
|
['-123,4567', -123.0, '->parse() truncates invalid grouping when grouping is disabled.', 4, false],
|
|
['-123,456,789', -123456789.0, '->parse() parses a number with grouping.', 12],
|
|
['-123,456,789', -123.0, '->parse() truncates a group if grouping is disabled.', 4, false],
|
|
['-123,456,789.66', -123456789.66, '->parse() parses a number with grouping.', 15],
|
|
['-123,456,789.66', -123.00, '->parse() truncates a group if grouping is disabled.', 4, false],
|
|
['-123,456789.66', false, '->parse() does not parse when invalid grouping used.', 1],
|
|
['-123,456789.66', -123.00, '->parse() truncates a group if grouping is disabled.', 4, false],
|
|
['-123456,789.66', false, '->parse() does not parse when invalid grouping used.', 1],
|
|
['-123456,789.66', -123456.00, '->parse() truncates a group if grouping is disabled.', 7, false],
|
|
['-123,456,78', false, '->parse() does not parse when invalid grouping used.', 1],
|
|
['-123,456,78', -123.0, '->parse() truncates a group if grouping is disabled.', 4, false],
|
|
['-123,45,789', false, '->parse() does not parse when invalid grouping used.', 1],
|
|
['-123,45,789', -123.0, '->parse() truncates a group if grouping is disabled.', 4, false],
|
|
['-123,,456', -123.0, '->parse() parses when grouping is duplicated.', 4],
|
|
['-123,,456', -123.0, '->parse() parses when grouping is disabled.', 4, false],
|
|
['-123,,4', -123.0, '->parse() parses when grouping is duplicated.', 4],
|
|
['-123,,4', -123.0, '->parse() parses when grouping is duplicated.', 4, false],
|
|
['239.', 239.0, '->parse() parses when string ends with decimal separator.', 4],
|
|
['239.', 239.0, '->parse() parses when string ends with decimal separator.', 4, false],
|
|
];
|
|
}
|
|
|
|
public function testParseTypeDefault()
|
|
{
|
|
if (\PHP_VERSION_ID >= 80000) {
|
|
$this->expectException(\ValueError::class);
|
|
} elseif (method_exists($this, 'expectWarning')) {
|
|
$this->expectWarning();
|
|
} else {
|
|
$this->expectException(Warning::class);
|
|
}
|
|
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$formatter->parse('1', NumberFormatter::TYPE_DEFAULT);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider parseTypeInt32Provider
|
|
*/
|
|
public function testParseTypeInt32($value, $expected, $message = '')
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$parsedValue = $formatter->parse($value, NumberFormatter::TYPE_INT32);
|
|
$this->assertSame($expected, $parsedValue, $message);
|
|
}
|
|
|
|
public function parseTypeInt32Provider()
|
|
{
|
|
return [
|
|
['1', 1],
|
|
['1.1', 1],
|
|
['.1', 0],
|
|
['2,147,483,647', 2147483647],
|
|
['-2,147,483,648', -2147483647 - 1],
|
|
['2,147,483,648', false, '->parse() TYPE_INT32 returns false when the number is greater than the integer positive range.'],
|
|
['-2,147,483,649', false, '->parse() TYPE_INT32 returns false when the number is greater than the integer negative range.'],
|
|
];
|
|
}
|
|
|
|
public function testParseTypeInt64With32BitIntegerInPhp32Bit()
|
|
{
|
|
IntlTestHelper::require32Bit($this);
|
|
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
|
|
$parsedValue = $formatter->parse('2,147,483,647', NumberFormatter::TYPE_INT64);
|
|
$this->assertIsInt($parsedValue);
|
|
$this->assertEquals(2147483647, $parsedValue);
|
|
|
|
$parsedValue = $formatter->parse('-2,147,483,648', NumberFormatter::TYPE_INT64);
|
|
$this->assertIsInt($parsedValue);
|
|
$this->assertEquals(-2147483648, $parsedValue);
|
|
}
|
|
|
|
public function testParseTypeInt64With32BitIntegerInPhp64Bit()
|
|
{
|
|
IntlTestHelper::require64Bit($this);
|
|
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
|
|
$parsedValue = $formatter->parse('2,147,483,647', NumberFormatter::TYPE_INT64);
|
|
$this->assertIsInt($parsedValue);
|
|
$this->assertEquals(2147483647, $parsedValue);
|
|
|
|
$parsedValue = $formatter->parse('-2,147,483,648', NumberFormatter::TYPE_INT64);
|
|
$this->assertIsInt($parsedValue);
|
|
$this->assertEquals(-2147483647 - 1, $parsedValue);
|
|
}
|
|
|
|
/**
|
|
* If PHP is compiled in 32bit mode, the returned value for a 64bit integer are float numbers.
|
|
*/
|
|
public function testParseTypeInt64With64BitIntegerInPhp32Bit()
|
|
{
|
|
IntlTestHelper::require32Bit($this);
|
|
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
|
|
// int 64 using only 32 bit range strangeness
|
|
$parsedValue = $formatter->parse('2,147,483,648', NumberFormatter::TYPE_INT64);
|
|
$this->assertIsFloat($parsedValue);
|
|
$this->assertEquals(2147483648, $parsedValue, '->parse() TYPE_INT64 does not use true 64 bit integers, using only the 32 bit range.');
|
|
|
|
$parsedValue = $formatter->parse('-2,147,483,649', NumberFormatter::TYPE_INT64);
|
|
$this->assertIsFloat($parsedValue);
|
|
$this->assertEquals(-2147483649, $parsedValue, '->parse() TYPE_INT64 does not use true 64 bit integers, using only the 32 bit range.');
|
|
}
|
|
|
|
/**
|
|
* If PHP is compiled in 64bit mode, the returned value for a 64bit integer are 32bit integer numbers.
|
|
*/
|
|
public function testParseTypeInt64With64BitIntegerInPhp64Bit()
|
|
{
|
|
IntlTestHelper::require64Bit($this);
|
|
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
|
|
$parsedValue = $formatter->parse('2,147,483,648', NumberFormatter::TYPE_INT64);
|
|
$this->assertIsInt($parsedValue);
|
|
|
|
$this->assertEquals(2147483648, $parsedValue, '->parse() TYPE_INT64 uses true 64 bit integers (PHP >= 5.3.14 and PHP >= 5.4.4).');
|
|
|
|
$parsedValue = $formatter->parse('-2,147,483,649', NumberFormatter::TYPE_INT64);
|
|
$this->assertIsInt($parsedValue);
|
|
|
|
$this->assertEquals(-2147483649, $parsedValue, '->parse() TYPE_INT64 uses true 64 bit integers (PHP >= 5.3.14 and PHP >= 5.4.4).');
|
|
}
|
|
|
|
/**
|
|
* @dataProvider parseTypeDoubleProvider
|
|
*/
|
|
public function testParseTypeDouble($value, $expectedValue)
|
|
{
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$parsedValue = $formatter->parse($value, NumberFormatter::TYPE_DOUBLE);
|
|
$this->assertEqualsWithDelta($expectedValue, $parsedValue, 0.001);
|
|
}
|
|
|
|
public function parseTypeDoubleProvider()
|
|
{
|
|
return [
|
|
['1', (float) 1],
|
|
['1.1', 1.1],
|
|
['9,223,372,036,854,775,808', 9223372036854775808],
|
|
['-9,223,372,036,854,775,809', -9223372036854775809],
|
|
];
|
|
}
|
|
|
|
public function testParseTypeCurrency()
|
|
{
|
|
if (\PHP_VERSION_ID >= 80000) {
|
|
$this->expectException(\ValueError::class);
|
|
} elseif (method_exists($this, 'expectWarning')) {
|
|
$this->expectWarning();
|
|
} else {
|
|
$this->expectException(Warning::class);
|
|
}
|
|
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$formatter->parse('1', NumberFormatter::TYPE_CURRENCY);
|
|
}
|
|
|
|
public function testParseWithNotNullPositionValue()
|
|
{
|
|
$position = 1;
|
|
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
|
|
$formatter->parse('123', NumberFormatter::TYPE_DOUBLE, $position);
|
|
$this->assertEquals(3, $position);
|
|
}
|
|
|
|
/**
|
|
* @param string $locale
|
|
* @param null $style
|
|
* @param null $pattern
|
|
*
|
|
* @return \NumberFormatter
|
|
*/
|
|
abstract protected function getNumberFormatter($locale = 'en', $style = null, $pattern = null);
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
abstract protected function getIntlErrorMessage();
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
abstract protected function getIntlErrorCode();
|
|
|
|
/**
|
|
* @param int $errorCode
|
|
*
|
|
* @return bool
|
|
*/
|
|
abstract protected function isIntlFailure($errorCode);
|
|
}
|