Bladeren bron

[Locale] StubNumberFormatter allow to parse 64bit number in 64bit mode

stealth35 13 jaren geleden
bovenliggende
commit
3759ff0777

+ 39 - 12
src/Symfony/Component/Locale/Stub/StubNumberFormatter.php

@@ -203,11 +203,20 @@ class StubNumberFormatter
      * The maximum values of the integer type in 32 bit platforms.
      * @var array
      */
-    static private $intRange = array(
+    static private $int32Range = array(
         'positive' => 2147483647,
         'negative' => -2147483648
     );
 
+    /**
+     * The maximum values of the integer type in 64 bit platforms.
+     * @var array
+     */
+    static private $int64Range = array(
+        'positive' => 9223372036854775807,
+        'negative' => -9223372036854775808
+    );
+
     /**
      * @var string
      */
@@ -450,7 +459,6 @@ class StubNumberFormatter
      * @param  int          $position                       Offset to begin the parsing on return this value will hold the offset at which the parsing ended
      * @return Boolean|string                               The parsed value of false on error
      * @see    http://www.php.net/manual/en/numberformatter.parse.php
-     * @throws MethodArgumentValueNotImplementedException   When $type equals to TYPE_INT64, behavior not implemented
      * @throws MethodArgumentNotImplementedException        When $position different than null, behavior not implemented
      */
     public function parse($value, $type = self::TYPE_DOUBLE, &$position = null)
@@ -461,11 +469,6 @@ class StubNumberFormatter
             return false;
         }
 
-        // Not implemented, the NumberFormatter behavior is inconsistency
-        if ($type == self::TYPE_INT64) {
-            throw new MethodArgumentValueNotImplementedException(__METHOD__, 'type', $type);
-        }
-
         // We don't calculate the position when parsing the value
         if (null !== $position) {
             throw new MethodArgumentNotImplementedException(__METHOD__, 'position');
@@ -719,9 +722,10 @@ class StubNumberFormatter
     {
         if ($type == self::TYPE_DOUBLE) {
             $value = (float) $value;
-        }
-        elseif ($type == self::TYPE_INT32) {
-            $value = $this->getIntValue($value);
+        } elseif ($type == self::TYPE_INT32) {
+            $value = $this->getInt32Value($value);
+        } elseif ($type == self::TYPE_INT64) {
+            $value = $this->getInt64Value($value);
         }
 
         return $value;
@@ -733,12 +737,35 @@ class StubNumberFormatter
      * @param  mixed  $value   The value to be converted
      * @return int             The converted value
      */
-    private function getIntValue($value)
+    private function getInt32Value($value)
+    {
+        if ($value > self::$int32Range['positive'] || $value < self::$int32Range['negative']) {
+            return false;
+        }
+
+        return (int) $value;
+    }
+
+    /**
+     * Convert the value data type to int or returns false if the value is out of the integer value range.
+     *
+     * @param  mixed  $value   The value to be converted
+     * @return int|float       The converted value
+     */
+    private function getInt64Value($value)
     {
-        if ($value > self::$intRange['positive'] || $value < self::$intRange['negative']) {
+        if ($value > self::$int64Range['positive'] || $value < self::$int64Range['negative']) {
             return false;
         }
 
+        if (PHP_INT_SIZE !== 8 && ($value > self::$int32Range['positive'] || $value <= self::$int32Range['negative'])) {
+            return (float) $value;
+        }
+
+        if (PHP_INT_SIZE === 8 && ($value > self::$int32Range['positive'] || $value < self::$int32Range['negative'])) {
+            $value = (-2147483648 - ($value % -2147483648)) * ($value / abs($value));
+        }
+
         return (int) $value;
     }
 

+ 75 - 25
tests/Symfony/Tests/Component/Locale/Stub/StubNumberFormatterTest.php

@@ -752,28 +752,84 @@ class StubNumberFormatterTest extends LocaleTestCase
         );
     }
 
+    // Stub Tests
+
+    public function testParseTypeInt64StubWith32BitIntegerInPhp32Bit()
+    {
+        $this->skipIfIntlExtensionIsNotLoaded();
+        $this->skipIfNot32Bit();
+
+        $formatter = $this->getStubFormatterWithDecimalStyle();
+
+        $parsedValue = $formatter->parse('2,147,483,647', \NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('integer', $parsedValue);
+        $this->assertEquals(2147483647, $parsedValue);
+
+        // Look that the parsing of '-2,147,483,648' results in a float like the literal -2147483648
+        $parsedValue = $formatter->parse('-2,147,483,648', \NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('float', $parsedValue);
+        $this->assertEquals(((float) -2147483647 - 1), $parsedValue);
+    }
+
+    public function testParseTypeInt64StubWith32BitIntegerInPhp64Bit()
+    {
+        $this->skipIfNot64Bit();
+
+        $formatter = $this->getStubFormatterWithDecimalStyle();
+
+        $parsedValue = $formatter->parse('2,147,483,647', \NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('integer', $parsedValue);
+        $this->assertEquals(2147483647, $parsedValue);
+
+        $parsedValue = $formatter->parse('-2,147,483,648', \NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('integer', $parsedValue);
+        $this->assertEquals(-2147483647 - 1, $parsedValue);
+    }
+
     /**
-     * There are a lot of hard behaviors with TYPE_INT64, see the intl tests
-     *
-     * @expectedException Symfony\Component\Locale\Exception\MethodArgumentValueNotImplementedException
-     * @see testParseTypeInt64IntlWith32BitIntegerInPhp32Bit
-     * @see testParseTypeInt64IntlWith32BitIntegerInPhp64Bit
-     * @see testParseTypeInt64IntlWith64BitIntegerInPhp32Bit
-     * @see testParseTypeInt64IntlWith64BitIntegerInPhp64Bit
+     * If PHP is compiled in 32bit mode, the returned value for a 64bit integer are float numbers.
+     */
+    public function testParseTypeInt64StubWith64BitIntegerInPhp32Bit()
+    {
+        $this->skipIfNot32Bit();
+
+        $formatter = $this->getStubFormatterWithDecimalStyle();
+
+        // int 64 using only 32 bit range strangeness
+        $parsedValue = $formatter->parse('2,147,483,648', \NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('float', $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->assertInternalType('float', $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 testParseTypeInt64Stub()
+    public function testParseTypeInt64StubWith64BitIntegerInPhp64Bit()
     {
+        $this->skipIfNot64Bit();
+
         $formatter = $this->getStubFormatterWithDecimalStyle();
-        $formatter->parse('1', StubNumberFormatter::TYPE_INT64);
+
+        $parsedValue = $formatter->parse('2,147,483,648', \NumberFormatter::TYPE_INT64);
+        $this->assertInternalType('integer', $parsedValue);
+        $this->assertEquals(-2147483647 - 1, $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->assertInternalType('integer', $parsedValue);
+        $this->assertEquals(2147483647, $parsedValue, '->parse() TYPE_INT64 does not use true 64 bit integers, using only the 32 bit range.');
     }
 
+    // Intl Tests
+
     public function testParseTypeInt64IntlWith32BitIntegerInPhp32Bit()
     {
         $this->skipIfIntlExtensionIsNotLoaded();
-        if (!$this->is32Bit()) {
-            // the PHP must be compiled in 32 bit mode to run this test (64 bits test will be run instead).
-            return;
-        }
+        $this->skipIfNot32Bit();
+
         $formatter = $this->getIntlFormatterWithDecimalStyle();
 
         $parsedValue = $formatter->parse('2,147,483,647', \NumberFormatter::TYPE_INT64);
@@ -789,10 +845,8 @@ class StubNumberFormatterTest extends LocaleTestCase
     public function testParseTypeInt64IntlWith32BitIntegerInPhp64Bit()
     {
         $this->skipIfIntlExtensionIsNotLoaded();
-        if (!$this->is64Bit()) {
-            // the PHP must be compiled in 64 bit mode to run this test (32 bits test will be run instead).
-            return;
-        }
+        $this->skipIfNot64Bit();
+
         $formatter = $this->getIntlFormatterWithDecimalStyle();
 
         $parsedValue = $formatter->parse('2,147,483,647', \NumberFormatter::TYPE_INT64);
@@ -810,10 +864,8 @@ class StubNumberFormatterTest extends LocaleTestCase
     public function testParseTypeInt64IntlWith64BitIntegerInPhp32Bit()
     {
         $this->skipIfIntlExtensionIsNotLoaded();
-        if (!$this->is32Bit()) {
-            // the PHP must be compiled in 32 bit mode to run this test (64 bits test will be run instead).
-            return;
-        }
+        $this->skipIfNot32Bit();
+
         $formatter = $this->getIntlFormatterWithDecimalStyle();
 
         // int 64 using only 32 bit range strangeness
@@ -832,10 +884,8 @@ class StubNumberFormatterTest extends LocaleTestCase
     public function testParseTypeInt64IntlWith64BitIntegerInPhp64Bit()
     {
         $this->skipIfIntlExtensionIsNotLoaded();
-        if (!$this->is64Bit()) {
-            // the PHP must be compiled in 64 bit mode to run this test (32 bits test will be run instead).
-            return;
-        }
+        $this->skipIfNot64Bit();
+
         $formatter = $this->getIntlFormatterWithDecimalStyle();
 
         $parsedValue = $formatter->parse('2,147,483,648', \NumberFormatter::TYPE_INT64);

+ 14 - 0
tests/Symfony/Tests/Component/Locale/TestCase.php

@@ -44,6 +44,20 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
         }
     }
 
+    protected function skipIfNot32Bit()
+    {
+        if (!$this->is32Bit()) {
+            $this->markTestSkipped('PHP must be compiled in 32 bit mode to run this test');
+        }
+    }
+
+    protected function skipIfNot64Bit()
+    {
+        if (!$this->is64Bit()) {
+            $this->markTestSkipped('PHP must be compiled in 64 bit mode to run this test');
+        }
+    }
+
     protected function isGreaterOrEqualThanIcuVersion($version)
     {
         $version = $this->normalizeIcuVersion($version);