PageRenderTime 26ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php

https://gitlab.com/jeisoncg/personal_collection_mv
PHP | 272 lines | 139 code | 44 blank | 89 comment | 27 complexity | bca84c4b0fa84137668f34fe8f79b950 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Form\Extension\Core\DataTransformer;
  11. use Symfony\Component\Form\DataTransformerInterface;
  12. use Symfony\Component\Form\Exception\TransformationFailedException;
  13. /**
  14. * Transforms between a number type and a localized number with grouping
  15. * (each thousand) and comma separators.
  16. *
  17. * @author Bernhard Schussek <bschussek@gmail.com>
  18. * @author Florian Eckerstorfer <florian@eckerstorfer.org>
  19. */
  20. class NumberToLocalizedStringTransformer implements DataTransformerInterface
  21. {
  22. /**
  23. * Rounds a number towards positive infinity.
  24. *
  25. * Rounds 1.4 to 2 and -1.4 to -1.
  26. */
  27. const ROUND_CEILING = \NumberFormatter::ROUND_CEILING;
  28. /**
  29. * Rounds a number towards negative infinity.
  30. *
  31. * Rounds 1.4 to 1 and -1.4 to -2.
  32. */
  33. const ROUND_FLOOR = \NumberFormatter::ROUND_FLOOR;
  34. /**
  35. * Rounds a number away from zero.
  36. *
  37. * Rounds 1.4 to 2 and -1.4 to -2.
  38. */
  39. const ROUND_UP = \NumberFormatter::ROUND_UP;
  40. /**
  41. * Rounds a number towards zero.
  42. *
  43. * Rounds 1.4 to 1 and -1.4 to -1.
  44. */
  45. const ROUND_DOWN = \NumberFormatter::ROUND_DOWN;
  46. /**
  47. * Rounds to the nearest number and halves to the next even number.
  48. *
  49. * Rounds 2.5, 1.6 and 1.5 to 2 and 1.4 to 1.
  50. */
  51. const ROUND_HALF_EVEN = \NumberFormatter::ROUND_HALFEVEN;
  52. /**
  53. * Rounds to the nearest number and halves away from zero.
  54. *
  55. * Rounds 2.5 to 3, 1.6 and 1.5 to 2 and 1.4 to 1.
  56. */
  57. const ROUND_HALF_UP = \NumberFormatter::ROUND_HALFUP;
  58. /**
  59. * Rounds to the nearest number and halves towards zero.
  60. *
  61. * Rounds 2.5 and 1.6 to 2, 1.5 and 1.4 to 1.
  62. */
  63. const ROUND_HALF_DOWN = \NumberFormatter::ROUND_HALFDOWN;
  64. protected $grouping;
  65. protected $roundingMode;
  66. private $scale;
  67. public function __construct($scale = null, $grouping = false, $roundingMode = self::ROUND_HALF_UP)
  68. {
  69. if (null === $grouping) {
  70. $grouping = false;
  71. }
  72. if (null === $roundingMode) {
  73. $roundingMode = self::ROUND_HALF_UP;
  74. }
  75. $this->scale = $scale;
  76. $this->grouping = $grouping;
  77. $this->roundingMode = $roundingMode;
  78. }
  79. /**
  80. * Transforms a number type into localized number.
  81. *
  82. * @param int|float $value Number value.
  83. *
  84. * @return string Localized value.
  85. *
  86. * @throws TransformationFailedException If the given value is not numeric
  87. * or if the value can not be transformed.
  88. */
  89. public function transform($value)
  90. {
  91. if (null === $value) {
  92. return '';
  93. }
  94. if (!is_numeric($value)) {
  95. throw new TransformationFailedException('Expected a numeric.');
  96. }
  97. $formatter = $this->getNumberFormatter();
  98. $value = $formatter->format($value);
  99. if (intl_is_failure($formatter->getErrorCode())) {
  100. throw new TransformationFailedException($formatter->getErrorMessage());
  101. }
  102. // Convert fixed spaces to normal ones
  103. $value = str_replace("\xc2\xa0", ' ', $value);
  104. return $value;
  105. }
  106. /**
  107. * Transforms a localized number into an integer or float.
  108. *
  109. * @param string $value The localized value
  110. *
  111. * @return int|float The numeric value
  112. *
  113. * @throws TransformationFailedException If the given value is not a string
  114. * or if the value can not be transformed.
  115. */
  116. public function reverseTransform($value)
  117. {
  118. if (!is_string($value)) {
  119. throw new TransformationFailedException('Expected a string.');
  120. }
  121. if ('' === $value) {
  122. return;
  123. }
  124. if ('NaN' === $value) {
  125. throw new TransformationFailedException('"NaN" is not a valid number');
  126. }
  127. $position = 0;
  128. $formatter = $this->getNumberFormatter();
  129. $groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
  130. $decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
  131. if ('.' !== $decSep && (!$this->grouping || '.' !== $groupSep)) {
  132. $value = str_replace('.', $decSep, $value);
  133. }
  134. if (',' !== $decSep && (!$this->grouping || ',' !== $groupSep)) {
  135. $value = str_replace(',', $decSep, $value);
  136. }
  137. if (false !== strpos($value, $decSep)) {
  138. $type = \NumberFormatter::TYPE_DOUBLE;
  139. } else {
  140. $type = PHP_INT_SIZE === 8
  141. ? \NumberFormatter::TYPE_INT64
  142. : \NumberFormatter::TYPE_INT32;
  143. }
  144. $result = $formatter->parse($value, $type, $position);
  145. if (intl_is_failure($formatter->getErrorCode())) {
  146. throw new TransformationFailedException($formatter->getErrorMessage());
  147. }
  148. if ($result >= PHP_INT_MAX || $result <= -PHP_INT_MAX) {
  149. throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like');
  150. }
  151. if (false !== $encoding = mb_detect_encoding($value, null, true)) {
  152. $length = mb_strlen($value, $encoding);
  153. $remainder = mb_substr($value, $position, $length, $encoding);
  154. } else {
  155. $length = strlen($value);
  156. $remainder = substr($value, $position, $length);
  157. }
  158. // After parsing, position holds the index of the character where the
  159. // parsing stopped
  160. if ($position < $length) {
  161. // Check if there are unrecognized characters at the end of the
  162. // number (excluding whitespace characters)
  163. $remainder = trim($remainder, " \t\n\r\0\x0b\xc2\xa0");
  164. if ('' !== $remainder) {
  165. throw new TransformationFailedException(
  166. sprintf('The number contains unrecognized characters: "%s"', $remainder)
  167. );
  168. }
  169. }
  170. // NumberFormatter::parse() does not round
  171. return $this->round($result);
  172. }
  173. /**
  174. * Returns a preconfigured \NumberFormatter instance.
  175. *
  176. * @return \NumberFormatter
  177. */
  178. protected function getNumberFormatter()
  179. {
  180. $formatter = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::DECIMAL);
  181. if (null !== $this->scale) {
  182. $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->scale);
  183. $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, $this->roundingMode);
  184. }
  185. $formatter->setAttribute(\NumberFormatter::GROUPING_USED, $this->grouping);
  186. return $formatter;
  187. }
  188. /**
  189. * Rounds a number according to the configured scale and rounding mode.
  190. *
  191. * @param int|float $number A number.
  192. *
  193. * @return int|float The rounded number.
  194. */
  195. private function round($number)
  196. {
  197. if (null !== $this->scale && null !== $this->roundingMode) {
  198. // shift number to maintain the correct scale during rounding
  199. $roundingCoef = pow(10, $this->scale);
  200. $number *= $roundingCoef;
  201. switch ($this->roundingMode) {
  202. case self::ROUND_CEILING:
  203. $number = ceil($number);
  204. break;
  205. case self::ROUND_FLOOR:
  206. $number = floor($number);
  207. break;
  208. case self::ROUND_UP:
  209. $number = $number > 0 ? ceil($number) : floor($number);
  210. break;
  211. case self::ROUND_DOWN:
  212. $number = $number > 0 ? floor($number) : ceil($number);
  213. break;
  214. case self::ROUND_HALF_EVEN:
  215. $number = round($number, 0, PHP_ROUND_HALF_EVEN);
  216. break;
  217. case self::ROUND_HALF_UP:
  218. $number = round($number, 0, PHP_ROUND_HALF_UP);
  219. break;
  220. case self::ROUND_HALF_DOWN:
  221. $number = round($number, 0, PHP_ROUND_HALF_DOWN);
  222. break;
  223. }
  224. $number /= $roundingCoef;
  225. }
  226. return $number;
  227. }
  228. }