4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Form\Extension\Core\DataTransformer
;
14 use Symfony\Component\Form\DataTransformerInterface
;
15 use Symfony\Component\Form\Exception\TransformationFailedException
;
18 * Transforms between a number type and a localized number with grouping
19 * (each thousand) and comma separators.
21 * @author Bernhard Schussek <bschussek@gmail.com>
22 * @author Florian Eckerstorfer <florian@eckerstorfer.org>
24 class NumberToLocalizedStringTransformer
implements DataTransformerInterface
26 const ROUND_FLOOR
= \NumberFormatter
::ROUND_FLOOR
;
27 const ROUND_DOWN
= \NumberFormatter
::ROUND_DOWN
;
28 const ROUND_HALFDOWN
= \NumberFormatter
::ROUND_HALFDOWN
;
29 const ROUND_HALFEVEN
= \NumberFormatter
::ROUND_HALFEVEN
;
30 const ROUND_HALFUP
= \NumberFormatter
::ROUND_HALFUP
;
31 const ROUND_UP
= \NumberFormatter
::ROUND_UP
;
32 const ROUND_CEILING
= \NumberFormatter
::ROUND_CEILING
;
38 protected $roundingMode;
40 public function __construct($precision = null, $grouping = null, $roundingMode = null)
42 if (null === $grouping) {
46 if (null === $roundingMode) {
47 $roundingMode = self
::ROUND_HALFUP
;
50 $this->precision
= $precision;
51 $this->grouping
= $grouping;
52 $this->roundingMode
= $roundingMode;
56 * Transforms a number type into localized number.
58 * @param integer|float $value Number value.
60 * @return string Localized value.
62 * @throws TransformationFailedException If the given value is not numeric
63 * or if the value can not be transformed.
65 public function transform($value)
67 if (null === $value) {
71 if (!is_numeric($value)) {
72 throw new TransformationFailedException('Expected a numeric.');
75 $formatter = $this->getNumberFormatter();
76 $value = $formatter->format($value);
78 if (intl_is_failure($formatter->getErrorCode())) {
79 throw new TransformationFailedException($formatter->getErrorMessage());
82 // Convert fixed spaces to normal ones
83 $value = str_replace("\xc2\xa0", ' ', $value);
89 * Transforms a localized number into an integer or float
91 * @param string $value The localized value
93 * @return integer|float The numeric value
95 * @throws TransformationFailedException If the given value is not a string
96 * or if the value can not be transformed.
98 public function reverseTransform($value)
100 if (!is_string($value)) {
101 throw new TransformationFailedException('Expected a string.');
108 if ('NaN' === $value) {
109 throw new TransformationFailedException('"NaN" is not a valid number');
113 $formatter = $this->getNumberFormatter();
114 $groupSep = $formatter->getSymbol(\NumberFormatter
::GROUPING_SEPARATOR_SYMBOL
);
115 $decSep = $formatter->getSymbol(\NumberFormatter
::DECIMAL_SEPARATOR_SYMBOL
);
117 if ('.' !== $decSep && (!$this->grouping
|| '.' !== $groupSep)) {
118 $value = str_replace('.', $decSep, $value);
121 if (',' !== $decSep && (!$this->grouping
|| ',' !== $groupSep)) {
122 $value = str_replace(',', $decSep, $value);
125 $result = $formatter->parse($value, \NumberFormatter
::TYPE_DOUBLE
, $position);
127 if (intl_is_failure($formatter->getErrorCode())) {
128 throw new TransformationFailedException($formatter->getErrorMessage());
131 if ($result >= PHP_INT_MAX
|| $result <= -PHP_INT_MAX
) {
132 throw new TransformationFailedException('I don\'t have a clear idea what infinity looks like');
135 if (function_exists('mb_detect_encoding') && false !== $encoding = mb_detect_encoding($value)) {
136 $strlen = function ($string) use ($encoding) {
137 return mb_strlen($string, $encoding);
139 $substr = function ($string, $offset, $length) use ($encoding) {
140 return mb_substr($string, $offset, $length, $encoding);
147 $length = $strlen($value);
149 // After parsing, position holds the index of the character where the
151 if ($position < $length) {
152 // Check if there are unrecognized characters at the end of the
153 // number (excluding whitespace characters)
154 $remainder = trim($substr($value, $position, $length), " \t\n\r\0\x0b\xc2\xa0");
156 if ('' !== $remainder) {
157 throw new TransformationFailedException(
158 sprintf('The number contains unrecognized characters: "%s"', $remainder)
167 * Returns a preconfigured \NumberFormatter instance
169 * @return \NumberFormatter
171 protected function getNumberFormatter()
173 $formatter = new \
NumberFormatter(\Locale
::getDefault(), \NumberFormatter
::DECIMAL
);
175 if (null !== $this->precision
) {
176 $formatter->setAttribute(\NumberFormatter
::FRACTION_DIGITS
, $this->precision
);
177 $formatter->setAttribute(\NumberFormatter
::ROUNDING_MODE
, $this->roundingMode
);
180 $formatter->setAttribute(\NumberFormatter
::GROUPING_USED
, $this->grouping
);