Browse Source

[ZF-6723] Zend_Locale:

 - added correct locale support for isNumber()
 - added correct locale support for getNumber()


git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@15890 44c647ce-9c0f-0410-b52a-842ac1e357ba
thomas 16 years ago
parent
commit
df5820bd6f
2 changed files with 133 additions and 37 deletions
  1. 130 34
      library/Zend/Locale/Format.php
  2. 3 3
      tests/Zend/Locale/FormatTest.php

+ 130 - 34
library/Zend/Locale/Format.php

@@ -220,17 +220,15 @@ class Zend_Locale_Format
     }
 
     /**
-     * Returns the first found number from an string
+     * Returns the normalized number from a localized one
      * Parsing depends on given locale (grouping and decimal)
      *
      * Examples for input:
-     * '  2345.4356,1234' = 23455456.1234
+     * '2345.4356,1234' = 23455456.1234
      * '+23,3452.123' = 233452.123
-     * ' 12343 ' = 12343
-     * '-9456km' = -9456
+     * '12343 ' = 12343
+     * '-9456' = -9456
      * '0' = 0
-     * '(-){0,1}(\d+(\.){0,1})*(\,){0,1})\d+'
-     * '١١٠ Tests' = 110  call: getNumber($string, 'Arab');
      *
      * @param  string $input    Input string to parse for numbers
      * @param  array  $options  Options: locale, precision. See {@link setOptions()} for details.
@@ -244,40 +242,37 @@ class Zend_Locale_Format
             return $input;
         }
 
-        // Get correct signs for this locale
-        $symbols = Zend_Locale_Data::getList($options['locale'],'symbols');
-
-        // Parse input locale aware
-        $regex = '/([' . $symbols['minus'] . '-]){0,1}(\d+(\\' . $symbols['group'] . '){0,1})*(\\' .
-                        $symbols['decimal'] . '){0,1}\d+/';
-        preg_match($regex, $input, $found);
-        if (!isset($found[0])) {
+        if (!self::isNumber($input, $options)) {
             require_once 'Zend/Locale/Exception.php';
-            throw new Zend_Locale_Exception('No value in ' . $input . ' found');
+            throw new Zend_Locale_Exception('No localized value in ' . $input . ' found, or the given number does not match the localized format');
         }
-        $found = $found[0];
+
+        // Get correct signs for this locale
+        $symbols = Zend_Locale_Data::getList($options['locale'],'symbols');
         // Change locale input to be default number
-        if ($symbols['minus'] != "-")
-            $found = strtr($found,$symbols['minus'],'-');
-        $found = str_replace($symbols['group'],'', $found);
+        if ((strpos($input, $symbols['minus']) !== false) ||
+            (strpos($input, '-') !== false)) {
+            $input = strtr($input, array($symbols['minus'] => '', '-' => ''));
+            $input = '-' . $input;
+        }
 
-        // Do precision
-        if (strpos($found, $symbols['decimal']) !== false) {
+        $input = str_replace($symbols['group'],'', $input);
+        if (strpos($input, $symbols['decimal']) !== false) {
             if ($symbols['decimal'] != '.') {
-                $found = str_replace($symbols['decimal'], ".", $found);
+                $input = str_replace($symbols['decimal'], ".", $input);
             }
 
-            $pre = substr($found, strpos($found, '.') + 1);
+            $pre = substr($input, strpos($input, '.') + 1);
             if ($options['precision'] === null) {
                 $options['precision'] = strlen($pre);
             }
 
             if (strlen($pre) >= $options['precision']) {
-                $found = substr($found, 0, strlen($found) - strlen($pre) + $options['precision']);
+                $input = substr($input, 0, strlen($input) - strlen($pre) + $options['precision']);
             }
         }
 
-        return $found;
+        return $input;
     }
 
     /**
@@ -498,16 +493,117 @@ class Zend_Locale_Format
         // Get correct signs for this locale
         $symbols = Zend_Locale_Data::getList($options['locale'],'symbols');
 
-        // Parse input locale aware
-        $regex = '/^[' . $symbols['minus'] . $symbols['plus'] . '-+]?'
-               . '\d(\d*(\\' . $symbols['group'] . ')?\d+)*'
-               . '((\\' . $symbols['decimal'] . ')\d*)?([' . $symbols['exponent'] . 'eE]'
-               . '([' . $symbols['minus'] . $symbols['plus'] . '+-])?\d+)?$/';
-        preg_match($regex, $input, $found);
+        $regexs = Zend_Locale_Format::_getRegexForType('decimalnumber', $options);
+        $regexs = array_merge($regexs, Zend_Locale_Format::_getRegexForType('scientificnumber', $options));
+        foreach ($regexs as $regex) {
+            preg_match($regex, $input, $found);
+            if (isset($found[0])) {
+                return true;
+            }
+        }
 
-        if (!isset($found[0]))
-            return false;
-        return true;
+        return false;
+    }
+
+    /**
+     * Internal method to convert cldr number syntax into regex
+     *
+     * @param  string $type
+     * @return string
+     */
+    public static function _getRegexForType($type, $options)
+    {
+        $decimal  = Zend_Locale_Data::getContent($options['locale'], $type);
+        $decimal  = preg_replace('/[^#0,;\.\-Ee]/', '',$decimal);
+        $patterns = explode(';', $decimal);
+
+        if (count($patterns) == 1) {
+            $patterns[1] = '-' . $patterns[0];
+        }
+
+        $symbols = Zend_Locale_Data::getList($options['locale'],'symbols');
+
+        foreach($patterns as $pkey => $pattern) {
+            $regex[$pkey]  = '/^';
+            $rest   = 0;
+            $end    = null;
+            if (strpos($pattern, '.') !== false) {
+                $end     = substr($pattern, strpos($pattern, '.') + 1);
+                $pattern = substr($pattern, 0, -strlen($end) - 1);
+            }
+
+            if (strpos($pattern, ',') !== false) {
+                $parts = explode(',', $pattern);
+                $count = count($parts);
+                foreach($parts as $key => $part) {
+                    switch ($part) {
+                        case '#':
+                        case '-#':
+                            if ($part[0] == '-') {
+                                $regex[$pkey] .= '[' . $symbols['minus'] . '-]{0,1}';
+                            } else {
+                                $regex[$pkey] .= '[' . $symbols['plus'] . '+]{0,1}';
+                            }
+
+                            if (($parts[$key + 1]) == '##0')  {
+                                $regex[$pkey] .= '[0-9]{1,3}';
+                            } else if (($parts[$key + 1]) == '##') {
+                                $regex[$pkey] .= '[0-9]{1,2}';
+                            } else {
+                                throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 1):"' . $pattern . '"');
+                            }
+                            break;
+                        case '##':
+                            if ($parts[$key + 1] == '##0') {
+                                $regex[$pkey] .=  '(\\' . $symbols['group'] . '{0,1}[0-9]{2})*';
+                            } else {
+                                throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 2):"' . $pattern . '"');
+                            }
+                            break;
+                        case '##0':
+                            if ($parts[$key - 1] == '##') {
+                                $regex[$pkey] .= '[0-9]';
+                            } else if (($parts[$key - 1] == '#') || ($parts[$key - 1] == '-#')) {
+                                $regex[$pkey] .= '(\\' . $symbols['group'] . '{0,1}[0-9]{3})*';
+                            } else {
+                                throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 3):"' . $pattern . '"');
+                            }
+                            break;
+                        case '#0':
+                            if ($key == 0) {
+                                $regex[$pkey] .= '[0-9]*';
+                            } else {
+                                throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 4):"' . $pattern . '"');
+                            }
+                            break;
+                    }
+                }
+            }
+
+            if (strpos($pattern, 'E') !== false) {
+                if (($pattern == '#E0') || ($pattern == '#E00')) {
+                    $regex[$pkey] .= '[' . $symbols['plus']. '+]{0,1}[0-9]{1,}(\\' . $symbols['decimal'] . '[0-9]{1,})*[eE][' . $symbols['plus']. '+]{0,1}[0-9]{1,}';
+                } else if (($pattern == '-#E0') || ($pattern == '-#E00')) {
+                    $regex[$pkey] .= '[' . $symbols['minus']. '-]{0,1}[0-9]{1,}(\\' . $symbols['decimal'] . '[0-9]{1,})*[eE][' . $symbols['minus']. '-]{0,1}[0-9]{1,}';
+                } else {
+                    throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 5):"' . $pattern . '"');
+                }
+            }
+
+            if (!empty($end)) {
+                if ($end == '###') {
+                    $regex[$pkey] .= '(\\' . $symbols['decimal'] . '{1}[0-9]{1,}){0,1}';
+                } else if ($end == '###-') {
+                    $regex[$pkey] .= '(\\' . $symbols['decimal'] . '{1}[0-9]{1,}){0,1}[' . $symbols['minus']. '-]';
+                } else {
+                    throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 6):"' . $pattern . '"');
+                }
+            }
+
+            $regex[$pkey] .= '$/';
+        }
+
+        return $regex;
     }
 
     /**

+ 3 - 3
tests/Zend/Locale/FormatTest.php

@@ -69,9 +69,9 @@ class Zend_Locale_FormatTest extends PHPUnit_Framework_TestCase
         $this->assertEquals(       0,         Zend_Locale_Format::getNumber(         '0',         $options));
         $this->assertEquals(-1234567,         Zend_Locale_Format::getNumber(  '-1234567',         $options));
         $this->assertEquals( 1234567,         Zend_Locale_Format::getNumber(   '1234567',         $options));
-        $this->assertEquals(       0.1234567, Zend_Locale_Format::getNumber('         0,1234567', $options));
+        $this->assertEquals(       0.1234567, Zend_Locale_Format::getNumber('0,1234567', $options));
         $this->assertEquals(-1234567.12345,   Zend_Locale_Format::getNumber('-1.234.567,12345',   $options));
-        $this->assertEquals( 1234567.12345,   Zend_Locale_Format::getNumber(' 1.234.567,12345',   $options));
+        $this->assertEquals( 1234567.12345,   Zend_Locale_Format::getNumber('1.234.567,12345',   $options));
         $options = array('locale' => 'de_AT');
         $this->assertEquals(       0,         Zend_Locale_Format::getNumber(         '0',         $options));
         $this->assertEquals(-1234567,         Zend_Locale_Format::getNumber(  '-1234567',         $options));
@@ -128,7 +128,7 @@ class Zend_Locale_FormatTest extends PHPUnit_Framework_TestCase
     public function testIsNumberENotation()
     {
         $this->assertTrue( Zend_Locale_Format::isNumber('5,0004E+5',  array('locale' => 'de_AT')));
-        $this->assertTrue( Zend_Locale_Format::isNumber('2.34E-7',    array('locale' => 'de_AT')));
+        $this->assertTrue( Zend_Locale_Format::isNumber('2,34E-7',    array('locale' => 'de_AT')));
         $this->assertFalse(Zend_Locale_Format::isNumber('2.34E-7E-7', array('locale' => 'de_AT')));
     }