Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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