1. sfNumberFormat.class.php
  2. /** * sfNumberFormat class. * * sfNumberFormat formats decimal numbers in any locale. The decimal * number is formatted according to a particular pattern. These * patterns can arise from the sfNumberFormatInfo object which is * culturally sensitive. The sfNumberFormat class can be instantiated in * many ways. E.g. * * * //create a invariant number formatter. * $formatter = new sfNumberFormat(); * * //create a number format for the french language locale. * $fr = new sfNumberFormat('fr'); * * //create a number format base on a sfNumberFormatInfo instance $numberInfo. * $format = new sfNumberFormat($numberInfo); * * * A normal decimal number can also be displayed as a currency * or as a percentage. For example * * $format->format(1234.5); //Decimal number "1234.5" * $format->format(1234.5,'c'); //Default currency "$1234.50" * $format->format(0.25, 'p') //Percent "25%" * * * Currency is formated using the localized currency pattern. For example * to format the number as Japanese Yen: * * $ja = new sfNumberFormat('ja_JP'); * * //Japanese currency pattern, and using Japanese Yen symbol * $ja->format(123.14,'c','JPY'); //ï¿?123 (Yen 123) * * For each culture, the symbol for each currency may be different. * * @author Xiang Wei Zhuo * @version v1.0, last update on Fri Dec 10 18:10:20 EST 2004 * @package symfony * @subpackage i18n */
  3. class sfNumberFormat
  4. {
  5. /**
  6. * The DateTimeFormatInfo, containing culture specific patterns and names.
  7. * @var DateTimeFormatInfo
  8. */
  9. protected $formatInfo;
  10. /**
  11. * Creates a new number format instance. The constructor can be instantiated
  12. * with a string that represent a culture/locale. Similarly, passing
  13. * a sfCultureInfo or sfNumberFormatInfo instance will instantiated a instance
  14. * for that particular culture.
  15. *
  16. * @param mixed $formatInfo either null, a sfCultureInfo, a sfNumberFormatInfo, or string
  17. * @return sfNumberFormat
  18. */
  19. function __construct($formatInfo = null)
  20. {
  21. if (null === $formatInfo)
  22. {
  23. $this->formatInfo = sfNumberFormatInfo::getInvariantInfo();
  24. }
  25. else if ($formatInfo instanceof sfCultureInfo)
  26. {
  27. $this->formatInfo = $formatInfo->sfNumberFormat;
  28. }
  29. else if ($formatInfo instanceof sfNumberFormatInfo)
  30. {
  31. $this->formatInfo = $formatInfo;
  32. }
  33. else
  34. {
  35. $this->formatInfo = sfNumberFormatInfo::getInstance($formatInfo);
  36. }
  37. }
  38. /**
  39. * Formats the number for a certain pattern. The valid patterns are
  40. * 'c', 'd', 'e', 'p' or a custom pattern, such as "#.000" for
  41. * 3 decimal places.
  42. *
  43. * @param mixed $number the number to format.
  44. * @param string $pattern the format pattern, either, 'c', 'd', 'e', 'p'
  45. * or a custom pattern. E.g. "#.000" will format the number to
  46. * 3 decimal places.
  47. * @param string $currency 3-letter ISO 4217 code. For example, the code
  48. * "USD" represents the US Dollar and "EUR" represents the Euro currency.
  49. * @param string $charset The charset
  50. * @return string formatted number string
  51. */
  52. function format($number, $pattern = 'd', $currency = 'USD', $charset = 'UTF-8')
  53. {
  54. $this->setPattern($pattern);
  55. if (strtolower($pattern) == 'p')
  56. {
  57. $number = $number * 100;
  58. }
  59. // avoid conversion with exponents
  60. // see http://trac.symfony-project.org/ticket/5715
  61. $precision = ini_set('precision', 14);
  62. $string = $this->fixFloat($number);
  63. ini_set('precision', $precision);
  64. $decimal = $this->formatDecimal($string);
  65. $integer = $this->formatInteger($this->fixFloat(abs($number)));
  66. $result = (strlen($decimal) > 0) ? $integer.$decimal : $integer;
  67. // get the suffix
  68. if ($number >= 0)
  69. {
  70. $suffix = $this->formatInfo->PositivePattern;
  71. }
  72. else if ($number < 0)
  73. {
  74. $suffix = $this->formatInfo->NegativePattern;
  75. }
  76. else
  77. {
  78. $suffix = array('', '');
  79. }
  80. // append and prepend suffix
  81. $result = $suffix[0].$result.$suffix[1];
  82. // replace currency sign
  83. $symbol = @$this->formatInfo->getCurrencySymbol($currency);
  84. if (null === $symbol)
  85. {
  86. $symbol = $currency;
  87. }
  88. $result = str_replace('¤', $symbol, $result);
  89. return sfToolkit::I18N_toEncoding($result, $charset);
  90. }
  91. /**
  92. * Formats the integer, perform groupings and string padding.
  93. *
  94. * @param string $string the decimal number in string form.
  95. * @return string formatted integer string with grouping
  96. */
  97. protected function formatInteger($string)
  98. {
  99. $string = (string) $string;
  100. $dp = strpos($string, '.');
  101. if (is_int($dp))
  102. {
  103. $string = substr($string, 0, $dp);
  104. }
  105. $integer = '';
  106. $digitSize = $this->formatInfo->getDigitSize();
  107. $string = str_pad($string, $digitSize, '0', STR_PAD_LEFT);
  108. $len = strlen($string);
  109. $groupSeparator = $this->formatInfo->GroupSeparator;
  110. $groupSize = $this->formatInfo->GroupSizes;
  111. $firstGroup = true;
  112. $multiGroup = is_int($groupSize[1]);
  113. $count = 0;
  114. if (is_int($groupSize[0]))
  115. {
  116. // now for the integer groupings
  117. for ($i = 0; $i < $len; $i++)
  118. {
  119. $char = $string{$len - $i - 1};
  120. if ($multiGroup && $count == 0)
  121. {
  122. if ($i != 0 && $i % $groupSize[0] == 0)
  123. {
  124. $integer = $groupSeparator.$integer;
  125. $count++;
  126. }
  127. }
  128. else if ($multiGroup && $count >= 1)
  129. {
  130. if ($i != 0 && ($i - $groupSize[0]) % $groupSize[1] == 0)
  131. {
  132. $integer = $groupSeparator.$integer;
  133. $count++;
  134. }
  135. }
  136. else
  137. {
  138. if ($i != 0 && $i % $groupSize[0] == 0)
  139. {
  140. $integer = $groupSeparator.$integer;
  141. $count++;
  142. }
  143. }
  144. $integer = $char.$integer;
  145. }
  146. }
  147. else
  148. {
  149. $integer = $string;
  150. }
  151. return $integer;
  152. }
  153. /**
  154. * Formats the decimal places.
  155. *
  156. * @param string $string the decimal number in string form.
  157. * @return string formatted decimal places.
  158. */
  159. protected function formatDecimal($string)
  160. {
  161. $dp = strpos($string, '.');
  162. $decimal = '';
  163. $decimalDigits = $this->formatInfo->DecimalDigits;
  164. $decimalSeparator = $this->formatInfo->DecimalSeparator;
  165. if (is_int($dp))
  166. {
  167. if ($decimalDigits == -1)
  168. {
  169. $decimal = substr($string, $dp + 1);
  170. }
  171. else if (is_int($decimalDigits))
  172. {
  173. if (false === $pos = strpos($string, '.'))
  174. {
  175. $decimal = str_pad($decimal, $decimalDigits, '0');
  176. }
  177. else
  178. {
  179. $decimal = substr($string, $pos + 1);
  180. if (strlen($decimal) <= $decimalDigits)
  181. {
  182. $decimal = str_pad($decimal, $decimalDigits, '0');
  183. }
  184. else
  185. {
  186. $decimal = substr($decimal, 0, $decimalDigits);
  187. }
  188. }
  189. }
  190. else
  191. {
  192. return $decimal;
  193. }
  194. return $decimalSeparator.$decimal;
  195. }
  196. else if ($decimalDigits > 0)
  197. {
  198. return $decimalSeparator.str_pad($decimal, $decimalDigits, '0');
  199. }
  200. return $decimal;
  201. }
  202. /**
  203. * Sets the pattern to format against. The default patterns
  204. * are retrieved from the sfNumberFormatInfo instance.
  205. *
  206. * @param string $pattern the requested patterns.
  207. * @return string a number format pattern.
  208. */
  209. protected function setPattern($pattern)
  210. {
  211. switch ($pattern)
  212. {
  213. case 'c':
  214. case 'C':
  215. $this->formatInfo->setPattern(sfNumberFormatInfo::CURRENCY);
  216. break;
  217. case 'd':
  218. case 'D':
  219. $this->formatInfo->setPattern(sfNumberFormatInfo::DECIMAL);
  220. break;
  221. case 'e':
  222. case 'E':
  223. $this->formatInfo->setPattern(sfNumberFormatInfo::SCIENTIFIC);
  224. break;
  225. case 'p':
  226. case 'P':
  227. $this->formatInfo->setPattern(sfNumberFormatInfo::PERCENTAGE);
  228. break;
  229. default:
  230. $this->formatInfo->setPattern($pattern);
  231. break;
  232. }
  233. }
  234. protected function fixFloat($float)
  235. {
  236. $string = (string) $float;
  237. if (false === strstr($float, 'E'))
  238. {
  239. return $string;
  240. }
  241. list($significand, $exp) = explode('E', $string);
  242. list(, $decimal) = explode('.', $significand);
  243. $exp = str_replace('+', '', $exp) - strlen($decimal);
  244. return str_replace('.', '', $significand).str_repeat('0', $exp);
  245. }
  246. }

Debug toolbar