Development

/branches/1.0/lib/i18n/sfNumberFormat.class.php

You must first sign up to be able to contribute.

root/branches/1.0/lib/i18n/sfNumberFormat.class.php

Revision 18607, 8.7 kB (checked in by fabien, 5 years ago)

[1.0, 1.1, 1.2, 1.3] fixed format_currency returns Exponents (closes #5715)

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Rev Date
Line 
1 <?php
2
3 /**
4  * sfNumberFormat class file.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the BSD License.
8  *
9  * Copyright(c) 2004 by Qiang Xue. All rights reserved.
10  *
11  * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
12  * The latest version of PRADO can be obtained from:
13  * {@link http://prado.sourceforge.net/}
14  *
15  * @author     Wei Zhuo <weizhuo[at]gmail[dot]com>
16  * @version    $Id$
17  * @package    symfony
18  * @subpackage i18n
19  */
20
21 /**
22  * Get the encoding utilities
23  */
24 require_once(dirname(__FILE__).'/util.php');
25
26 /**
27  * sfNumberFormat class.
28  *
29  * sfNumberFormat formats decimal numbers in any locale. The decimal
30  * number is formatted according to a particular pattern. These
31  * patterns can arise from the sfNumberFormatInfo object which is
32  * culturally sensitive. The sfNumberFormat class can be instantiated in
33  * many ways. E.g.
34  *
35  * <code>
36  *  //create a invariant number formatter.
37  *  $formatter = new sfNumberFormat();
38  *
39  *  //create a number format for the french language locale.
40  *  $fr = new sfNumberFormat('fr');
41  *
42  *  //create a number format base on a sfNumberFormatInfo instance $numberInfo.
43  *  $format = new sfNumberFormat($numberInfo);
44  * </code>
45  *
46  * A normal decimal number can also be displayed as a currency
47  * or as a percentage. For example
48  * <code>
49  * $format->format(1234.5); //Decimal number "1234.5"
50  * $format->format(1234.5,'c'); //Default currency "$1234.50"
51  * $format->format(0.25, 'p') //Percent "25%"
52  * </code>
53  *
54  * Currency is formated using the localized currency pattern. For example
55  * to format the number as Japanese Yen:
56  * <code>
57  *  $ja = new sfNumberFormat('ja_JP');
58  *
59  *  //Japanese currency pattern, and using Japanese Yen symbol
60  *  $ja->format(123.14,'c','JPY'); //ï¿?123 (Yen 123)
61  * </code>
62  * For each culture, the symbol for each currency may be different.
63  *
64  * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
65  * @version v1.0, last update on Fri Dec 10 18:10:20 EST 2004
66  * @package    symfony
67  * @subpackage i18n
68  */
69 class sfNumberFormat
70 {
71   /**
72    * The DateTimeFormatInfo, containing culture specific patterns and names.
73    * @var DateTimeFormatInfo
74    */
75   protected $formatInfo;
76
77   /**
78    * Creates a new number format instance. The constructor can be instantiated
79    * with a string that represent a culture/locale. Similarly, passing
80    * a sfCultureInfo or sfNumberFormatInfo instance will instantiated a instance
81    * for that particular culture.
82    *
83    * @param mixed either null, a sfCultureInfo, a sfNumberFormatInfo, or string
84    * @return sfNumberFormat
85    */
86   function __construct($formatInfo = null)
87   {
88     if (is_null($formatInfo))
89     {
90       $this->formatInfo = sfNumberFormatInfo::getInvariantInfo();
91     }
92     else if ($formatInfo instanceof sfCultureInfo)
93     {
94       $this->formatInfo = $formatInfo->sfNumberFormat;
95     }
96     else if ($formatInfo instanceof sfNumberFormatInfo)
97     {
98       $this->formatInfo = $formatInfo;
99     }
100     else
101     {
102       $this->formatInfo = sfNumberFormatInfo::getInstance($formatInfo);
103     }
104   }
105
106   /**
107    * Formats the number for a certain pattern. The valid patterns are
108    * 'c', 'd', 'e', 'p' or a custom pattern, such as "#.000" for
109    * 3 decimal places.
110    *
111    * @param mixed the number to format.
112    * @param string the format pattern, either, 'c', 'd', 'e', 'p'
113    * or a custom pattern. E.g. "#.000" will format the number to
114    * 3 decimal places.
115    * @param string 3-letter ISO 4217 code. For example, the code
116    * "USD" represents the US Dollar and "EUR" represents the Euro currency.
117    * @return string formatted number string
118    */
119   function format($number, $pattern = 'd', $currency = 'USD', $charset = 'UTF-8')
120   {
121     $this->setPattern($pattern);
122
123     if (strtolower($pattern) == 'p')
124     {
125       $number = $number * 100;
126     }
127
128     // avoid conversion with exponents
129     // see http://trac.symfony-project.org/ticket/5715
130     $precision = ini_set('precision', 14);
131     $string = $this->fixFloat($number);
132     ini_set('precision', $precision);
133
134     list($number, $decimal) = $this->formatDecimal($string);
135     $integer = $this->formatInteger($this->fixFloat(abs($number)));
136
137     $result = (strlen($decimal) > 0) ? $integer.$decimal : $integer;
138
139     // get the suffix
140     if ($number >= 0)
141     {
142       $suffix = $this->formatInfo->PositivePattern;
143     }
144     else if ($number < 0)
145     {
146       $suffix = $this->formatInfo->NegativePattern;
147     }
148     else
149     {
150       $suffix = array('', '');
151     }
152
153     // append and prepend suffix
154     $result = $suffix[0].$result.$suffix[1];
155
156     // replace currency sign
157     $symbol = @$this->formatInfo->getCurrencySymbol($currency);
158     if (is_null($symbol))
159     {
160       $symbol = $currency;
161     }
162
163     $result = str_replace('¤', $symbol, $result);
164
165     return I18N_toEncoding($result, $charset);
166   }
167
168   /**
169    * Formats the integer, perform groupings and string padding.
170    *
171    * @param string the decimal number in string form.
172    * @return string  formatted integer string with grouping
173    */
174   protected function formatInteger($string)
175   {
176     $string = (string) $string;
177     $dp = strpos($string, '.');
178
179     if (is_int($dp))
180     {
181       $string = substr($string, 0, $dp);
182     }
183
184     $integer = '';
185
186     $len = strlen($string);
187
188     $groupSeparator = $this->formatInfo->GroupSeparator;
189     $groupSize = $this->formatInfo->GroupSizes;
190
191     $firstGroup = true;
192     $multiGroup = is_int($groupSize[1]);
193     $count = 0;
194
195     if (is_int($groupSize[0]))
196     {
197       // now for the integer groupings
198       for ($i = 0; $i < $len; $i++)
199       {
200         $char = $string{$len - $i - 1};
201
202         if ($multiGroup && $count == 0)
203         {
204           if ($i != 0 && $i % $groupSize[0] == 0)
205           {
206             $integer = $groupSeparator.$integer;
207             $count++;
208           }
209         }
210         else if ($multiGroup && $count >= 1)
211         {
212           if ($i != 0 && ($i - $groupSize[0]) % $groupSize[1] == 0)
213           {
214             $integer = $groupSeparator.$integer;
215             $count++;
216           }
217         }
218         else
219         {
220           if ($i != 0 && $i % $groupSize[0] == 0)
221           {
222             $integer = $groupSeparator.$integer;
223             $count++;
224           }
225         }
226
227         $integer = $char.$integer;
228       }
229     }
230     else
231     {
232       $integer = $string;
233     }
234
235     return $integer;
236   }
237
238   /**
239    * Formats the decimal places.
240    *
241    * @param string the decimal number in string form.
242    * @return string formatted decimal places.
243    */
244   protected function formatDecimal($string)
245   {
246     $dp = strpos($string, '.');
247     $decimal = '';
248
249     $decimalDigits = $this->formatInfo->DecimalDigits;
250     $decimalSeparator = $this->formatInfo->DecimalSeparator;
251
252     if (is_int($dp))
253     {
254       if ($decimalDigits == -1)
255       {
256         $decimal = substr($string, $dp + 1);
257       }
258       else if (is_int($decimalDigits))
259       {
260         if (false === $pos = strpos($string, '.'))
261         {
262           $decimal = str_pad($decimal, $decimalDigits, '0');
263         }
264         else
265         {
266           $decimal = substr($string, $pos + 1);
267           if (strlen($decimal) <= $decimalDigits)
268           {
269             $decimal = str_pad($decimal, $decimalDigits, '0');
270           }
271           else
272           {
273             $decimal = substr($decimal, 0, $decimalDigits);
274           }
275         }
276       }
277       else
278       {
279         return array($string, $decimal);
280       }
281
282       return array($string, $decimalSeparator.$decimal);
283     }
284     else if ($decimalDigits > 0)
285     {
286       return array($string, $decimalSeparator.str_pad($decimal, $decimalDigits, '0'));
287     }
288
289     return array($string, $decimal);
290   }
291
292   /**
293    * Sets the pattern to format against. The default patterns
294    * are retrieved from the sfNumberFormatInfo instance.
295    *
296    * @param string the requested patterns.
297    * @return string a number format pattern.
298    */
299   protected function setPattern($pattern)
300   {
301     switch ($pattern)
302     {
303       case 'c':
304       case 'C':
305         $this->formatInfo->setPattern(sfNumberFormatInfo::CURRENCY);
306         break;
307       case 'd':
308       case 'D':
309         $this->formatInfo->setPattern(sfNumberFormatInfo::DECIMAL);
310         break;
311       case 'e':
312       case 'E':
313         $this->formatInfo->setPattern(sfNumberFormatInfo::SCIENTIFIC);
314         break;
315       case 'p':
316       case 'P':
317         $this->formatInfo->setPattern(sfNumberFormatInfo::PERCENTAGE);
318         break;
319       default:
320         $this->formatInfo->setPattern($pattern);
321         break;
322     }
323   }
324
325   protected function fixFloat($float)
326   {
327     $string = (string) $float;
328
329     if (false === strstr($float, 'E'))
330     {
331       return $string;
332     }
333
334     list($significand, $exp) = explode('E', $string);
335     list(, $decimal) = explode('.', $significand);
336     $exp = str_replace('+', '', $exp) - strlen($decimal);
337
338     return str_replace('.', '', $significand).str_repeat('0', $exp);
339   }
340 }
341
Note: See TracBrowser for help on using the browser.