Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[Form] Improved multi-byte handling of NumberToLocalizedStringTransfo…
…rmer
  • Loading branch information
webmozart committed May 3, 2013
1 parent 9f02b05 commit dcced01
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 32 deletions.
Expand Up @@ -80,6 +80,9 @@ public function transform($value)
throw new TransformationFailedException($formatter->getErrorMessage());
}

// Convert fixed spaces to normal ones
$value = str_replace("\xc2\xa0", ' ', $value);

return $value;
}

Expand Down Expand Up @@ -130,19 +133,31 @@ public function reverseTransform($value)
throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like');
}

if (function_exists('mb_detect_encoding') && false !== $encoding = mb_detect_encoding($value)) {
$strlen = function ($string) use ($encoding) {
return mb_strlen($string, $encoding);
};
$substr = function ($string, $offset, $length) use ($encoding) {
return mb_substr($string, $offset, $length, $encoding);
};
} else {
$strlen = 'strlen';
$substr = 'substr';
}

$length = $strlen($value);

// After parsing, position holds the index of the character where the
// parsing stopped
if ($position < strlen($value)) {
if ($position < $length) {
// Check if there are unrecognized characters at the end of the
// number
$remainder = substr($value, $position);
// number (excluding whitespace characters)
$remainder = trim($substr($value, $position, $length), " \t\n\r\0\x0b\xc2\xa0");

// Remove all whitespace characters
if ('' !== preg_replace('/[\s\xc2\xa0]*/', '', $remainder)) {
if ('' !== $remainder) {
throw new TransformationFailedException(
sprintf('The number contains unrecognized characters: "%s"',
$remainder
));
sprintf('The number contains unrecognized characters: "%s"', $remainder)
);
}
}

Expand Down
Expand Up @@ -22,29 +22,52 @@ protected function setUp()
\Locale::setDefault('de_AT');
}

public function testTransform()
public function provideTransformations()
{
$transformer = new NumberToLocalizedStringTransformer();

$this->assertEquals('1', $transformer->transform(1));
$this->assertEquals('1,5', $transformer->transform(1.5));
$this->assertEquals('1234,5', $transformer->transform(1234.5));
$this->assertEquals('12345,912', $transformer->transform(12345.9123));
return array(
array(null, '', 'de_AT'),
array(1, '1', 'de_AT'),
array(1.5, '1,5', 'de_AT'),
array(1234.5, '1234,5', 'de_AT'),
array(12345.912, '12345,912', 'de_AT'),
array(1234.5, '1234,5', 'ru'),
array(1234.5, '1234,5', 'fi'),
);
}

public function testTransformEmpty()
/**
* @dataProvider provideTransformations
*/
public function testTransform($from, $to, $locale)
{
\Locale::setDefault($locale);

$transformer = new NumberToLocalizedStringTransformer();

$this->assertSame('', $transformer->transform(null));
$this->assertSame($to, $transformer->transform($from));
}

public function provideTransformationsWithGrouping()
{
return array(
array(1234.5, '1.234,5', 'de_AT'),
array(12345.912, '12.345,912', 'de_AT'),
array(1234.5, '1 234,5', 'fr'),
array(1234.5, '1 234,5', 'ru'),
array(1234.5, '1 234,5', 'fi'),
);
}

public function testTransformWithGrouping()
/**
* @dataProvider provideTransformationsWithGrouping
*/
public function testTransformWithGrouping($from, $to, $locale)
{
\Locale::setDefault($locale);

$transformer = new NumberToLocalizedStringTransformer(null, true);

$this->assertEquals('1.234,5', $transformer->transform(1234.5));
$this->assertEquals('12.345,912', $transformer->transform(12345.9123));
$this->assertSame($to, $transformer->transform($from));
}

public function testTransformWithPrecision()
Expand All @@ -65,30 +88,48 @@ public function testTransformWithRoundingMode()

}

public function testReverseTransform()
/**
* @dataProvider provideTransformations
*/
public function testReverseTransform($to, $from, $locale)
{
\Locale::setDefault($locale);

$transformer = new NumberToLocalizedStringTransformer();

$this->assertEquals(1, $transformer->reverseTransform('1'));
$this->assertEquals(1.5, $transformer->reverseTransform('1,5'));
$this->assertEquals(1234.5, $transformer->reverseTransform('1234,5'));
$this->assertEquals(12345.912, $transformer->reverseTransform('12345,912'));
$this->assertEquals($to, $transformer->reverseTransform($from));
}

public function testReverseTransformEmpty()
/**
* @dataProvider provideTransformationsWithGrouping
*/
public function testReverseTransformWithGrouping($to, $from, $locale)
{
$transformer = new NumberToLocalizedStringTransformer();
\Locale::setDefault($locale);

$transformer = new NumberToLocalizedStringTransformer(null, true);

$this->assertEquals($to, $transformer->reverseTransform($from));
}

// https://github.com/symfony/symfony/issues/7609
public function testReverseTransformWithGroupingAndFixedSpaces()
{
if (!extension_loaded('mbstring')) {
$this->markTestSkipped('The "mbstring" extension is required for this test.');
}

\Locale::setDefault('ru');

$transformer = new NumberToLocalizedStringTransformer(null, true);

$this->assertNull($transformer->reverseTransform(''));
$this->assertEquals(1234.5, $transformer->reverseTransform("1\xc2\xa0234,5"));
}

public function testReverseTransformWithGrouping()
public function testReverseTransformWithGroupingButWithoutGroupSeparator()
{
$transformer = new NumberToLocalizedStringTransformer(null, true);

// completely valid format
$this->assertEquals(1234.5, $transformer->reverseTransform('1.234,5'));
$this->assertEquals(12345.912, $transformer->reverseTransform('12.345,912'));
// omit group separator
$this->assertEquals(1234.5, $transformer->reverseTransform('1234,5'));
$this->assertEquals(12345.912, $transformer->reverseTransform('12345,912'));
Expand Down Expand Up @@ -299,6 +340,7 @@ public function testReverseTransformDisallowsLeadingExtraCharacters()

/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage The number contains unrecognized characters: "foo3"
*/
public function testReverseTransformDisallowsCenteredExtraCharacters()
{
Expand All @@ -309,11 +351,63 @@ public function testReverseTransformDisallowsCenteredExtraCharacters()

/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage The number contains unrecognized characters: "foo8"
*/
public function testReverseTransformDisallowsCenteredExtraCharactersMultibyte()
{
if (!extension_loaded('mbstring')) {
$this->markTestSkipped('The "mbstring" extension is required for this test.');
}

\Locale::setDefault('ru');

$transformer = new NumberToLocalizedStringTransformer(null, true);

$transformer->reverseTransform("12\xc2\xa0345,67foo8");
}

/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage The number contains unrecognized characters: "foo8"
*/
public function testReverseTransformIgnoresTrailingSpacesInExceptionMessage()
{
if (!extension_loaded('mbstring')) {
$this->markTestSkipped('The "mbstring" extension is required for this test.');
}

\Locale::setDefault('ru');

$transformer = new NumberToLocalizedStringTransformer(null, true);

$transformer->reverseTransform("12\xc2\xa0345,67foo8 \xc2\xa0\t");
}

/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage The number contains unrecognized characters: "foo"
*/
public function testReverseTransformDisallowsTrailingExtraCharacters()
{
$transformer = new NumberToLocalizedStringTransformer();

$transformer->reverseTransform('123foo');
}

/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage The number contains unrecognized characters: "foo"
*/
public function testReverseTransformDisallowsTrailingExtraCharactersMultibyte()
{
if (!extension_loaded('mbstring')) {
$this->markTestSkipped('The "mbstring" extension is required for this test.');
}

\Locale::setDefault('ru');

$transformer = new NumberToLocalizedStringTransformer(null, true);

$transformer->reverseTransform("12\xc2\xa0345,678foo");
}
}

0 comments on commit dcced01

Please sign in to comment.