diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php index b5cdae110d5a..7c21249fad5b 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php @@ -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; } @@ -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) + ); } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php index 151dbc80f606..09af7243d66d 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php @@ -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() @@ -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')); @@ -299,6 +340,7 @@ public function testReverseTransformDisallowsLeadingExtraCharacters() /** * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage The number contains unrecognized characters: "foo3" */ public function testReverseTransformDisallowsCenteredExtraCharacters() { @@ -309,6 +351,41 @@ 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() { @@ -316,4 +393,21 @@ public function testReverseTransformDisallowsTrailingExtraCharacters() $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"); + } }