PageRenderTime 39ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/symfony/i18n/sfNumberFormat.class.php

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