These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | /* |
||
6 | * soluble-flexstore library |
||
7 | * |
||
8 | * @author Vanvelthem Sébastien |
||
9 | * @link https://github.com/belgattitude/soluble-flexstore |
||
10 | * @copyright Copyright (c) 2016-2017 Vanvelthem Sébastien |
||
11 | * @license MIT License https://github.com/belgattitude/soluble-flexstore/blob/master/LICENSE.md |
||
12 | * |
||
13 | */ |
||
14 | |||
15 | namespace Soluble\FlexStore\Formatter; |
||
16 | |||
17 | use Soluble\FlexStore\Exception; |
||
18 | use Soluble\FlexStore\I18n\LocalizableInterface; |
||
19 | use ArrayObject; |
||
20 | use Locale; |
||
21 | use NumberFormatter as IntlNumberFormatter; |
||
22 | |||
23 | class NumberFormatter implements FormatterInterface, LocalizableInterface, FormatterNumberInterface |
||
24 | { |
||
25 | public const NO_BREAK_SPACE_HEX = 'c2a0'; |
||
26 | public const NARROW_NO_BREAK_SPACE_HEX = 'e280af'; |
||
27 | |||
28 | /** |
||
29 | * Formatter instances. |
||
30 | * |
||
31 | * @var array |
||
32 | */ |
||
33 | protected $formatters = []; |
||
34 | |||
35 | /** |
||
36 | * @var array |
||
37 | */ |
||
38 | protected $params = []; |
||
39 | |||
40 | /** |
||
41 | * @var array |
||
42 | */ |
||
43 | protected $default_params = [ |
||
44 | 'decimals' => 2, |
||
45 | 'locale' => null, |
||
46 | 'pattern' => null, |
||
47 | 'disableUseOfNonBreakingSpaces' => false |
||
48 | ]; |
||
49 | |||
50 | /** |
||
51 | * @param array $params |
||
52 | * |
||
53 | * @throws Exception\ExtensionNotLoadedException if ext/intl is not present |
||
54 | * @throws Exception\InvalidArgumentException |
||
55 | */ |
||
56 | 24 | public function __construct(array $params = []) |
|
57 | { |
||
58 | 24 | if (!extension_loaded('intl')) { |
|
59 | throw new Exception\ExtensionNotLoadedException(sprintf( |
||
60 | '%s component requires the intl PHP extension', |
||
61 | __NAMESPACE__ |
||
62 | )); |
||
63 | } |
||
64 | |||
65 | // As default locale may include unsupported |
||
66 | // variants (like 'en_US_POSIX' for example), |
||
67 | // only the 5 chars will be taken into consideration |
||
68 | |||
69 | 24 | $default_locale = Locale::getDefault(); |
|
70 | 24 | $this->default_params['locale'] = substr($default_locale, 0, 5); |
|
71 | 24 | $this->setParams($params); |
|
72 | 24 | } |
|
73 | |||
74 | /** |
||
75 | * @throws Exception\InvalidArgumentException |
||
76 | * |
||
77 | * @param array $params |
||
78 | */ |
||
79 | 24 | protected function setParams($params) |
|
80 | { |
||
81 | 24 | $this->params = $this->default_params; |
|
82 | 24 | foreach ($params as $name => $value) { |
|
83 | 17 | $method = 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($name)))); |
|
84 | 17 | if (!method_exists($this, $method)) { |
|
85 | 3 | throw new Exception\InvalidArgumentException(__METHOD__ . " Parameter '$name' does not exists."); |
|
86 | } |
||
87 | 14 | $this->$method($value); |
|
88 | } |
||
89 | 24 | } |
|
90 | |||
91 | 12 | protected function initWhitespaceSeparator(IntlNumberFormatter $formatter): void |
|
92 | { |
||
93 | 12 | if ($this->params['disableUseOfNonBreakingSpaces'] === true |
|
94 | 3 | && in_array(bin2hex($formatter->getSymbol(IntlNumberFormatter::GROUPING_SEPARATOR_SYMBOL)), [ |
|
95 | 3 | self::NARROW_NO_BREAK_SPACE_HEX, |
|
96 | 3 | self::NO_BREAK_SPACE_HEX |
|
97 | 12 | ], true)) { |
|
0 ignored issues
–
show
Coding Style
introduced
by
Loading history...
|
|||
98 | |||
99 | 3 | $formatter->setSymbol(IntlNumberFormatter::GROUPING_SEPARATOR_SYMBOL, ' '); |
|
100 | } |
||
101 | 12 | } |
|
102 | |||
103 | 6 | protected function loadFormatterId(string $formatterId): void |
|
104 | { |
||
105 | 6 | $locale = $this->params['locale']; |
|
106 | 6 | $formatter = new IntlNumberFormatter( |
|
107 | 6 | $locale, |
|
108 | 6 | IntlNumberFormatter::DECIMAL |
|
109 | ); |
||
110 | 6 | $formatter->setAttribute(IntlNumberFormatter::FRACTION_DIGITS, $this->params['decimals']); |
|
111 | 6 | if ($this->params['pattern'] !== null) { |
|
112 | 2 | $formatter->setPattern($this->params['pattern']); |
|
113 | } |
||
114 | |||
115 | 6 | $this->initWhitespaceSeparator($formatter); |
|
116 | |||
117 | 6 | $this->formatters[$formatterId] = $formatter; |
|
118 | 6 | } |
|
119 | |||
120 | /** |
||
121 | * Format a number. |
||
122 | * |
||
123 | * @param float $number |
||
124 | * |
||
125 | * @return string |
||
126 | */ |
||
127 | 3 | public function format($number, ArrayObject $row = null): string |
|
128 | { |
||
129 | 3 | $locale = $this->params['locale']; |
|
130 | //$formatterId = md5($locale); |
||
131 | 3 | $formatterId = $locale . (string) $this->params['pattern']; |
|
132 | 3 | if (!array_key_exists($formatterId, $this->formatters)) { |
|
133 | 3 | $this->loadFormatterId($formatterId); |
|
134 | } |
||
135 | |||
136 | 3 | if (!is_numeric($number)) { |
|
137 | 2 | $this->throwNumberFormatterException($this->formatters[$formatterId], $number); |
|
138 | } |
||
139 | |||
140 | 1 | return $this->formatters[$formatterId]->format($number); |
|
141 | } |
||
142 | |||
143 | /** |
||
144 | * Throws an Exception when number cannot be formatted. |
||
145 | * |
||
146 | * @param IntlNumberFormatter $intlFormatter |
||
147 | * @param int|string|float $number |
||
148 | * |
||
149 | * @throws Exception\RuntimeException |
||
150 | */ |
||
151 | 4 | protected function throwNumberFormatterException(IntlNumberFormatter $intlFormatter, $number): void |
|
152 | { |
||
153 | 4 | $error_code = $intlFormatter->getErrorCode(); |
|
154 | 4 | if (is_scalar($number)) { |
|
155 | 2 | $val = (string) $number; |
|
156 | } else { |
||
157 | 2 | $val = 'type: ' . gettype($number); |
|
158 | } |
||
159 | 4 | throw new Exception\RuntimeException(__METHOD__ . " Cannot format value '$val', Intl/NumberFormatter error code: $error_code."); |
|
160 | } |
||
161 | |||
162 | /** |
||
163 | * Set locale to use instead of the default. |
||
164 | * |
||
165 | * @param string $locale |
||
166 | */ |
||
167 | 17 | public function setLocale(?string $locale): self |
|
168 | { |
||
169 | 17 | $this->params['locale'] = $locale; |
|
170 | |||
171 | 17 | return $this; |
|
172 | } |
||
173 | |||
174 | /** |
||
175 | * Get the locale to use. |
||
176 | * |
||
177 | * @return string|null |
||
178 | */ |
||
179 | 6 | public function getLocale(): ?string |
|
180 | { |
||
181 | 6 | return $this->params['locale']; |
|
182 | } |
||
183 | |||
184 | /** |
||
185 | * Set decimals. |
||
186 | * |
||
187 | * @param int $decimals |
||
188 | */ |
||
189 | 15 | public function setDecimals($decimals): self |
|
190 | { |
||
191 | 15 | $this->params['decimals'] = $decimals; |
|
192 | |||
193 | 15 | return $this; |
|
194 | } |
||
195 | |||
196 | /** |
||
197 | * @return int |
||
198 | */ |
||
199 | 6 | public function getDecimals(): int |
|
200 | { |
||
201 | 6 | return $this->params['decimals']; |
|
202 | } |
||
203 | |||
204 | /** |
||
205 | * Set the number pattern, (#,##0.###, ....). |
||
206 | * |
||
207 | * @see http://php.net/manual/en/numberformatter.setpattern.php |
||
208 | * |
||
209 | * @param string $pattern |
||
210 | */ |
||
211 | 8 | public function setPattern($pattern): self |
|
212 | { |
||
213 | 8 | $this->params['pattern'] = $pattern; |
|
214 | |||
215 | 8 | return $this; |
|
216 | } |
||
217 | |||
218 | /** |
||
219 | * Get the number pattern. |
||
220 | * |
||
221 | * @return string|null |
||
222 | */ |
||
223 | 6 | public function getPattern(): ?string |
|
224 | { |
||
225 | 6 | return $this->params['pattern']; |
|
226 | } |
||
227 | |||
228 | 3 | public function setDisableUseOfNonBreakingSpaces(bool $disable=true): self { |
|
0 ignored issues
–
show
|
|||
229 | 3 | $this->params['disableUseOfNonBreakingSpaces'] = $disable; |
|
230 | 3 | return $this; |
|
231 | } |
||
232 | } |
||
233 |