diff options
author | Nicolas LÅ“uillet <nicolas.loeuillet@gmail.com> | 2013-08-03 19:26:54 +0200 |
---|---|---|
committer | Nicolas LÅ“uillet <nicolas.loeuillet@gmail.com> | 2013-08-03 19:26:54 +0200 |
commit | 4f5b44bd3bd490309eb2ba7b44df4769816ba729 (patch) | |
tree | 6cefe170dfe0a5a361cb1e2d1fc4d580a3316d02 /vendor/symfony/intl/Symfony/Component/Intl/NumberFormatter | |
parent | 2b840e0cfb63a453bea67a98541f3df9c273c5f5 (diff) | |
download | wallabag-4f5b44bd3bd490309eb2ba7b44df4769816ba729.tar.gz wallabag-4f5b44bd3bd490309eb2ba7b44df4769816ba729.tar.zst wallabag-4f5b44bd3bd490309eb2ba7b44df4769816ba729.zip |
twig implementation
Diffstat (limited to 'vendor/symfony/intl/Symfony/Component/Intl/NumberFormatter')
-rw-r--r-- | vendor/symfony/intl/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php | 891 |
1 files changed, 891 insertions, 0 deletions
diff --git a/vendor/symfony/intl/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/vendor/symfony/intl/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php new file mode 100644 index 00000000..0e5652e2 --- /dev/null +++ b/vendor/symfony/intl/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php | |||
@@ -0,0 +1,891 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of the Symfony package. | ||
5 | * | ||
6 | * (c) Fabien Potencier <fabien@symfony.com> | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | |||
12 | namespace Symfony\Component\Intl\NumberFormatter; | ||
13 | |||
14 | use Symfony\Component\Intl\Exception\NotImplementedException; | ||
15 | use Symfony\Component\Intl\Exception\MethodNotImplementedException; | ||
16 | use Symfony\Component\Intl\Exception\MethodArgumentNotImplementedException; | ||
17 | use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException; | ||
18 | use Symfony\Component\Intl\Globals\IntlGlobals; | ||
19 | use Symfony\Component\Intl\Intl; | ||
20 | use Symfony\Component\Intl\Locale\Locale; | ||
21 | |||
22 | /** | ||
23 | * Replacement for PHP's native {@link \NumberFormatter} class. | ||
24 | * | ||
25 | * The only methods currently supported in this class are: | ||
26 | * | ||
27 | * - {@link __construct} | ||
28 | * - {@link create} | ||
29 | * - {@link formatCurrency} | ||
30 | * - {@link format} | ||
31 | * - {@link getAttribute} | ||
32 | * - {@link getErrorCode} | ||
33 | * - {@link getErrorMessage} | ||
34 | * - {@link getLocale} | ||
35 | * - {@link parse} | ||
36 | * - {@link setAttribute} | ||
37 | * | ||
38 | * @author Eriksen Costa <eriksen.costa@infranology.com.br> | ||
39 | * @author Bernhard Schussek <bschussek@gmail.com> | ||
40 | */ | ||
41 | class NumberFormatter | ||
42 | { | ||
43 | /* Format style constants */ | ||
44 | const PATTERN_DECIMAL = 0; | ||
45 | const DECIMAL = 1; | ||
46 | const CURRENCY = 2; | ||
47 | const PERCENT = 3; | ||
48 | const SCIENTIFIC = 4; | ||
49 | const SPELLOUT = 5; | ||
50 | const ORDINAL = 6; | ||
51 | const DURATION = 7; | ||
52 | const PATTERN_RULEBASED = 9; | ||
53 | const IGNORE = 0; | ||
54 | const DEFAULT_STYLE = 1; | ||
55 | |||
56 | /* Format type constants */ | ||
57 | const TYPE_DEFAULT = 0; | ||
58 | const TYPE_INT32 = 1; | ||
59 | const TYPE_INT64 = 2; | ||
60 | const TYPE_DOUBLE = 3; | ||
61 | const TYPE_CURRENCY = 4; | ||
62 | |||
63 | /* Numeric attribute constants */ | ||
64 | const PARSE_INT_ONLY = 0; | ||
65 | const GROUPING_USED = 1; | ||
66 | const DECIMAL_ALWAYS_SHOWN = 2; | ||
67 | const MAX_INTEGER_DIGITS = 3; | ||
68 | const MIN_INTEGER_DIGITS = 4; | ||
69 | const INTEGER_DIGITS = 5; | ||
70 | const MAX_FRACTION_DIGITS = 6; | ||
71 | const MIN_FRACTION_DIGITS = 7; | ||
72 | const FRACTION_DIGITS = 8; | ||
73 | const MULTIPLIER = 9; | ||
74 | const GROUPING_SIZE = 10; | ||
75 | const ROUNDING_MODE = 11; | ||
76 | const ROUNDING_INCREMENT = 12; | ||
77 | const FORMAT_WIDTH = 13; | ||
78 | const PADDING_POSITION = 14; | ||
79 | const SECONDARY_GROUPING_SIZE = 15; | ||
80 | const SIGNIFICANT_DIGITS_USED = 16; | ||
81 | const MIN_SIGNIFICANT_DIGITS = 17; | ||
82 | const MAX_SIGNIFICANT_DIGITS = 18; | ||
83 | const LENIENT_PARSE = 19; | ||
84 | |||
85 | /* Text attribute constants */ | ||
86 | const POSITIVE_PREFIX = 0; | ||
87 | const POSITIVE_SUFFIX = 1; | ||
88 | const NEGATIVE_PREFIX = 2; | ||
89 | const NEGATIVE_SUFFIX = 3; | ||
90 | const PADDING_CHARACTER = 4; | ||
91 | const CURRENCY_CODE = 5; | ||
92 | const DEFAULT_RULESET = 6; | ||
93 | const PUBLIC_RULESETS = 7; | ||
94 | |||
95 | /* Format symbol constants */ | ||
96 | const DECIMAL_SEPARATOR_SYMBOL = 0; | ||
97 | const GROUPING_SEPARATOR_SYMBOL = 1; | ||
98 | const PATTERN_SEPARATOR_SYMBOL = 2; | ||
99 | const PERCENT_SYMBOL = 3; | ||
100 | const ZERO_DIGIT_SYMBOL = 4; | ||
101 | const DIGIT_SYMBOL = 5; | ||
102 | const MINUS_SIGN_SYMBOL = 6; | ||
103 | const PLUS_SIGN_SYMBOL = 7; | ||
104 | const CURRENCY_SYMBOL = 8; | ||
105 | const INTL_CURRENCY_SYMBOL = 9; | ||
106 | const MONETARY_SEPARATOR_SYMBOL = 10; | ||
107 | const EXPONENTIAL_SYMBOL = 11; | ||
108 | const PERMILL_SYMBOL = 12; | ||
109 | const PAD_ESCAPE_SYMBOL = 13; | ||
110 | const INFINITY_SYMBOL = 14; | ||
111 | const NAN_SYMBOL = 15; | ||
112 | const SIGNIFICANT_DIGIT_SYMBOL = 16; | ||
113 | const MONETARY_GROUPING_SEPARATOR_SYMBOL = 17; | ||
114 | |||
115 | /* Rounding mode values used by NumberFormatter::setAttribute() with NumberFormatter::ROUNDING_MODE attribute */ | ||
116 | const ROUND_CEILING = 0; | ||
117 | const ROUND_FLOOR = 1; | ||
118 | const ROUND_DOWN = 2; | ||
119 | const ROUND_UP = 3; | ||
120 | const ROUND_HALFEVEN = 4; | ||
121 | const ROUND_HALFDOWN = 5; | ||
122 | const ROUND_HALFUP = 6; | ||
123 | |||
124 | /* Pad position values used by NumberFormatter::setAttribute() with NumberFormatter::PADDING_POSITION attribute */ | ||
125 | const PAD_BEFORE_PREFIX = 0; | ||
126 | const PAD_AFTER_PREFIX = 1; | ||
127 | const PAD_BEFORE_SUFFIX = 2; | ||
128 | const PAD_AFTER_SUFFIX = 3; | ||
129 | |||
130 | /** | ||
131 | * The error code from the last operation | ||
132 | * | ||
133 | * @var integer | ||
134 | */ | ||
135 | protected $errorCode = IntlGlobals::U_ZERO_ERROR; | ||
136 | |||
137 | /** | ||
138 | * The error message from the last operation | ||
139 | * | ||
140 | * @var string | ||
141 | */ | ||
142 | protected $errorMessage = 'U_ZERO_ERROR'; | ||
143 | |||
144 | /** | ||
145 | * @var int | ||
146 | */ | ||
147 | private $style; | ||
148 | |||
149 | /** | ||
150 | * Default values for the en locale | ||
151 | * | ||
152 | * @var array | ||
153 | */ | ||
154 | private $attributes = array( | ||
155 | self::FRACTION_DIGITS => 0, | ||
156 | self::GROUPING_USED => 1, | ||
157 | self::ROUNDING_MODE => self::ROUND_HALFEVEN | ||
158 | ); | ||
159 | |||
160 | /** | ||
161 | * Holds the initialized attributes code | ||
162 | * | ||
163 | * @var array | ||
164 | */ | ||
165 | private $initializedAttributes = array(); | ||
166 | |||
167 | /** | ||
168 | * The supported styles to the constructor $styles argument | ||
169 | * | ||
170 | * @var array | ||
171 | */ | ||
172 | private static $supportedStyles = array( | ||
173 | 'CURRENCY' => self::CURRENCY, | ||
174 | 'DECIMAL' => self::DECIMAL | ||
175 | ); | ||
176 | |||
177 | /** | ||
178 | * Supported attributes to the setAttribute() $attr argument | ||
179 | * | ||
180 | * @var array | ||
181 | */ | ||
182 | private static $supportedAttributes = array( | ||
183 | 'FRACTION_DIGITS' => self::FRACTION_DIGITS, | ||
184 | 'GROUPING_USED' => self::GROUPING_USED, | ||
185 | 'ROUNDING_MODE' => self::ROUNDING_MODE | ||
186 | ); | ||
187 | |||
188 | /** | ||
189 | * The available rounding modes for setAttribute() usage with | ||
190 | * NumberFormatter::ROUNDING_MODE. NumberFormatter::ROUND_DOWN | ||
191 | * and NumberFormatter::ROUND_UP does not have a PHP only equivalent | ||
192 | * | ||
193 | * @var array | ||
194 | */ | ||
195 | private static $roundingModes = array( | ||
196 | 'ROUND_HALFEVEN' => self::ROUND_HALFEVEN, | ||
197 | 'ROUND_HALFDOWN' => self::ROUND_HALFDOWN, | ||
198 | 'ROUND_HALFUP' => self::ROUND_HALFUP | ||
199 | ); | ||
200 | |||
201 | /** | ||
202 | * The mapping between NumberFormatter rounding modes to the available | ||
203 | * modes in PHP's round() function. | ||
204 | * | ||
205 | * @see http://www.php.net/manual/en/function.round.php | ||
206 | * | ||
207 | * @var array | ||
208 | */ | ||
209 | private static $phpRoundingMap = array( | ||
210 | self::ROUND_HALFDOWN => \PHP_ROUND_HALF_DOWN, | ||
211 | self::ROUND_HALFEVEN => \PHP_ROUND_HALF_EVEN, | ||
212 | self::ROUND_HALFUP => \PHP_ROUND_HALF_UP | ||
213 | ); | ||
214 | |||
215 | /** | ||
216 | * The maximum values of the integer type in 32 bit platforms. | ||
217 | * | ||
218 | * @var array | ||
219 | */ | ||
220 | private static $int32Range = array( | ||
221 | 'positive' => 2147483647, | ||
222 | 'negative' => -2147483648 | ||
223 | ); | ||
224 | |||
225 | /** | ||
226 | * The maximum values of the integer type in 64 bit platforms. | ||
227 | * | ||
228 | * @var array | ||
229 | */ | ||
230 | private static $int64Range = array( | ||
231 | 'positive' => 9223372036854775807, | ||
232 | 'negative' => -9223372036854775808 | ||
233 | ); | ||
234 | |||
235 | /** | ||
236 | * Constructor. | ||
237 | * | ||
238 | * @param string $locale The locale code. The only currently supported locale is "en". | ||
239 | * @param int $style Style of the formatting, one of the format style constants. | ||
240 | * The only supported styles are NumberFormatter::DECIMAL | ||
241 | * and NumberFormatter::CURRENCY. | ||
242 | * @param string $pattern Not supported. A pattern string in case $style is NumberFormat::PATTERN_DECIMAL or | ||
243 | * NumberFormat::PATTERN_RULEBASED. It must conform to the syntax | ||
244 | * described in the ICU DecimalFormat or ICU RuleBasedNumberFormat documentation | ||
245 | * | ||
246 | * @see http://www.php.net/manual/en/numberformatter.create.php | ||
247 | * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details | ||
248 | * @see http://www.icu-project.org/apiref/icu4c/classRuleBasedNumberFormat.html#_details | ||
249 | * | ||
250 | * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed | ||
251 | * @throws MethodArgumentValueNotImplementedException When the $style is not supported | ||
252 | * @throws MethodArgumentNotImplementedException When the pattern value is different than null | ||
253 | */ | ||
254 | public function __construct($locale = 'en', $style = null, $pattern = null) | ||
255 | { | ||
256 | if ('en' != $locale) { | ||
257 | throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported'); | ||
258 | } | ||
259 | |||
260 | if (!in_array($style, self::$supportedStyles)) { | ||
261 | $message = sprintf('The available styles are: %s.', implode(', ', array_keys(self::$supportedStyles))); | ||
262 | throw new MethodArgumentValueNotImplementedException(__METHOD__, 'style', $style, $message); | ||
263 | } | ||
264 | |||
265 | if (null !== $pattern) { | ||
266 | throw new MethodArgumentNotImplementedException(__METHOD__, 'pattern'); | ||
267 | } | ||
268 | |||
269 | $this->style = $style; | ||
270 | } | ||
271 | |||
272 | /** | ||
273 | * Static constructor. | ||
274 | * | ||
275 | * @param string $locale The locale code. The only supported locale is "en". | ||
276 | * @param int $style Style of the formatting, one of the format style constants. | ||
277 | * The only currently supported styles are NumberFormatter::DECIMAL | ||
278 | * and NumberFormatter::CURRENCY. | ||
279 | * @param string $pattern Not supported. A pattern string in case $style is NumberFormat::PATTERN_DECIMAL or | ||
280 | * NumberFormat::PATTERN_RULEBASED. It must conform to the syntax | ||
281 | * described in the ICU DecimalFormat or ICU RuleBasedNumberFormat documentation | ||
282 | * | ||
283 | * @return NumberFormatter | ||
284 | * | ||
285 | * @see http://www.php.net/manual/en/numberformatter.create.php | ||
286 | * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details | ||
287 | * @see http://www.icu-project.org/apiref/icu4c/classRuleBasedNumberFormat.html#_details | ||
288 | * | ||
289 | * @throws MethodArgumentValueNotImplementedException When $locale different than "en" is passed | ||
290 | * @throws MethodArgumentValueNotImplementedException When the $style is not supported | ||
291 | * @throws MethodArgumentNotImplementedException When the pattern value is different than null | ||
292 | */ | ||
293 | public static function create($locale = 'en', $style = null, $pattern = null) | ||
294 | { | ||
295 | return new self($locale, $style, $pattern); | ||
296 | } | ||
297 | |||
298 | /** | ||
299 | * Format a currency value | ||
300 | * | ||
301 | * @param float $value The numeric currency value | ||
302 | * @param string $currency The 3-letter ISO 4217 currency code indicating the currency to use | ||
303 | * | ||
304 | * @return string The formatted currency value | ||
305 | * | ||
306 | * @see http://www.php.net/manual/en/numberformatter.formatcurrency.php | ||
307 | * @see http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/currency_codes/currency_codes_list-1.htm | ||
308 | */ | ||
309 | public function formatCurrency($value, $currency) | ||
310 | { | ||
311 | if ($this->style == self::DECIMAL) { | ||
312 | return $this->format($value); | ||
313 | } | ||
314 | |||
315 | $symbol = Intl::getCurrencyBundle()->getCurrencySymbol($currency, 'en'); | ||
316 | $fractionDigits = Intl::getCurrencyBundle()->getFractionDigits($currency); | ||
317 | |||
318 | $value = $this->roundCurrency($value, $currency); | ||
319 | |||
320 | $negative = false; | ||
321 | if (0 > $value) { | ||
322 | $negative = true; | ||
323 | $value *= -1; | ||
324 | } | ||
325 | |||
326 | $value = $this->formatNumber($value, $fractionDigits); | ||
327 | |||
328 | $ret = $symbol.$value; | ||
329 | |||
330 | return $negative ? '('.$ret.')' : $ret; | ||
331 | } | ||
332 | |||
333 | /** | ||
334 | * Format a number | ||
335 | * | ||
336 | * @param number $value The value to format | ||
337 | * @param int $type Type of the formatting, one of the format type constants. | ||
338 | * Only type NumberFormatter::TYPE_DEFAULT is currently supported. | ||
339 | * | ||
340 | * @return Boolean|string The formatted value or false on error | ||
341 | * | ||
342 | * @see http://www.php.net/manual/en/numberformatter.format.php | ||
343 | * | ||
344 | * @throws NotImplementedException If the method is called with the class $style 'CURRENCY' | ||
345 | * @throws MethodArgumentValueNotImplementedException If the $type is different than TYPE_DEFAULT | ||
346 | */ | ||
347 | public function format($value, $type = self::TYPE_DEFAULT) | ||
348 | { | ||
349 | // The original NumberFormatter does not support this format type | ||
350 | if ($type == self::TYPE_CURRENCY) { | ||
351 | trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING); | ||
352 | |||
353 | return false; | ||
354 | } | ||
355 | |||
356 | if ($this->style == self::CURRENCY) { | ||
357 | throw new NotImplementedException(sprintf( | ||
358 | '%s() method does not support the formatting of currencies (instance with CURRENCY style). %s', | ||
359 | __METHOD__, NotImplementedException::INTL_INSTALL_MESSAGE | ||
360 | )); | ||
361 | } | ||
362 | |||
363 | // Only the default type is supported. | ||
364 | if ($type != self::TYPE_DEFAULT) { | ||
365 | throw new MethodArgumentValueNotImplementedException(__METHOD__, 'type', $type, 'Only TYPE_DEFAULT is supported'); | ||
366 | } | ||
367 | |||
368 | $fractionDigits = $this->getAttribute(self::FRACTION_DIGITS); | ||
369 | |||
370 | $value = $this->round($value, $fractionDigits); | ||
371 | $value = $this->formatNumber($value, $fractionDigits); | ||
372 | |||
373 | // behave like the intl extension | ||
374 | $this->resetError(); | ||
375 | |||
376 | return $value; | ||
377 | } | ||
378 | |||
379 | /** | ||
380 | * Returns an attribute value | ||
381 | * | ||
382 | * @param int $attr An attribute specifier, one of the numeric attribute constants | ||
383 | * | ||
384 | * @return Boolean|int The attribute value on success or false on error | ||
385 | * | ||
386 | * @see http://www.php.net/manual/en/numberformatter.getattribute.php | ||
387 | */ | ||
388 | public function getAttribute($attr) | ||
389 | { | ||
390 | return isset($this->attributes[$attr]) ? $this->attributes[$attr] : null; | ||
391 | } | ||
392 | |||
393 | /** | ||
394 | * Returns formatter's last error code. Always returns the U_ZERO_ERROR class constant value | ||
395 | * | ||
396 | * @return int The error code from last formatter call | ||
397 | * | ||
398 | * @see http://www.php.net/manual/en/numberformatter.geterrorcode.php | ||
399 | */ | ||
400 | public function getErrorCode() | ||
401 | { | ||
402 | return $this->errorCode; | ||
403 | } | ||
404 | |||
405 | /** | ||
406 | * Returns formatter's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value | ||
407 | * | ||
408 | * @return string The error message from last formatter call | ||
409 | * | ||
410 | * @see http://www.php.net/manual/en/numberformatter.geterrormessage.php | ||
411 | */ | ||
412 | public function getErrorMessage() | ||
413 | { | ||
414 | return $this->errorMessage; | ||
415 | } | ||
416 | |||
417 | /** | ||
418 | * Returns the formatter's locale | ||
419 | * | ||
420 | * The parameter $type is currently ignored. | ||
421 | * | ||
422 | * @param int $type Not supported. The locale name type to return (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE) | ||
423 | * | ||
424 | * @return string The locale used to create the formatter. Currently always | ||
425 | * returns "en". | ||
426 | * | ||
427 | * @see http://www.php.net/manual/en/numberformatter.getlocale.php | ||
428 | */ | ||
429 | public function getLocale($type = Locale::ACTUAL_LOCALE) | ||
430 | { | ||
431 | return 'en'; | ||
432 | } | ||
433 | |||
434 | /** | ||
435 | * Not supported. Returns the formatter's pattern | ||
436 | * | ||
437 | * @return Boolean|string The pattern string used by the formatter or false on error | ||
438 | * | ||
439 | * @see http://www.php.net/manual/en/numberformatter.getpattern.php | ||
440 | * | ||
441 | * @throws MethodNotImplementedException | ||
442 | */ | ||
443 | public function getPattern() | ||
444 | { | ||
445 | throw new MethodNotImplementedException(__METHOD__); | ||
446 | } | ||
447 | |||
448 | /** | ||
449 | * Not supported. Returns a formatter symbol value | ||
450 | * | ||
451 | * @param int $attr A symbol specifier, one of the format symbol constants | ||
452 | * | ||
453 | * @return Boolean|string The symbol value or false on error | ||
454 | * | ||
455 | * @see http://www.php.net/manual/en/numberformatter.getsymbol.php | ||
456 | * | ||
457 | * @throws MethodNotImplementedException | ||
458 | */ | ||
459 | public function getSymbol($attr) | ||
460 | { | ||
461 | throw new MethodNotImplementedException(__METHOD__); | ||
462 | } | ||
463 | |||
464 | /** | ||
465 | * Not supported. Returns a formatter text attribute value | ||
466 | * | ||
467 | * @param int $attr An attribute specifier, one of the text attribute constants | ||
468 | * | ||
469 | * @return Boolean|string The attribute value or false on error | ||
470 | * | ||
471 | * @see http://www.php.net/manual/en/numberformatter.gettextattribute.php | ||
472 | * | ||
473 | * @throws MethodNotImplementedException | ||
474 | */ | ||
475 | public function getTextAttribute($attr) | ||
476 | { | ||
477 | throw new MethodNotImplementedException(__METHOD__); | ||
478 | } | ||
479 | |||
480 | /** | ||
481 | * Not supported. Parse a currency number | ||
482 | * | ||
483 | * @param string $value The value to parse | ||
484 | * @param string $currency Parameter to receive the currency name (reference) | ||
485 | * @param int $position Offset to begin the parsing on return this value will hold the offset at which the parsing ended | ||
486 | * | ||
487 | * @return Boolean|string The parsed numeric value of false on error | ||
488 | * | ||
489 | * @see http://www.php.net/manual/en/numberformatter.parsecurrency.php | ||
490 | * | ||
491 | * @throws MethodNotImplementedException | ||
492 | */ | ||
493 | public function parseCurrency($value, &$currency, &$position = null) | ||
494 | { | ||
495 | throw new MethodNotImplementedException(__METHOD__); | ||
496 | } | ||
497 | |||
498 | /** | ||
499 | * Parse a number | ||
500 | * | ||
501 | * @param string $value The value to parse | ||
502 | * @param int $type Type of the formatting, one of the format type constants. | ||
503 | * The only currently supported types are NumberFormatter::TYPE_DOUBLE, | ||
504 | * NumberFormatter::TYPE_INT32 and NumberFormatter::TYPE_INT64. | ||
505 | * @param int $position Not supported. Offset to begin the parsing on return this value will hold the offset at which the parsing ended | ||
506 | * | ||
507 | * @return Boolean|string The parsed value of false on error | ||
508 | * | ||
509 | * @see http://www.php.net/manual/en/numberformatter.parse.php | ||
510 | * | ||
511 | * @throws MethodArgumentNotImplementedException When $position different than null, behavior not implemented | ||
512 | */ | ||
513 | public function parse($value, $type = self::TYPE_DOUBLE, &$position = null) | ||
514 | { | ||
515 | if ($type == self::TYPE_DEFAULT || $type == self::TYPE_CURRENCY) { | ||
516 | trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING); | ||
517 | |||
518 | return false; | ||
519 | } | ||
520 | |||
521 | // We don't calculate the position when parsing the value | ||
522 | if (null !== $position) { | ||
523 | throw new MethodArgumentNotImplementedException(__METHOD__, 'position'); | ||
524 | } | ||
525 | |||
526 | preg_match('/^([^0-9\-]{0,})(.*)/', $value, $matches); | ||
527 | |||
528 | // Any string before the numeric value causes error in the parsing | ||
529 | if (isset($matches[1]) && !empty($matches[1])) { | ||
530 | IntlGlobals::setError(IntlGlobals::U_PARSE_ERROR, 'Number parsing failed'); | ||
531 | $this->errorCode = IntlGlobals::getErrorCode(); | ||
532 | $this->errorMessage = IntlGlobals::getErrorMessage(); | ||
533 | |||
534 | return false; | ||
535 | } | ||
536 | |||
537 | // Remove everything that is not number or dot (.) | ||
538 | $value = preg_replace('/[^0-9\.\-]/', '', $value); | ||
539 | $value = $this->convertValueDataType($value, $type); | ||
540 | |||
541 | // behave like the intl extension | ||
542 | $this->resetError(); | ||
543 | |||
544 | return $value; | ||
545 | } | ||
546 | |||
547 | /** | ||
548 | * Set an attribute | ||
549 | * | ||
550 | * @param int $attr An attribute specifier, one of the numeric attribute constants. | ||
551 | * The only currently supported attributes are NumberFormatter::FRACTION_DIGITS, | ||
552 | * NumberFormatter::GROUPING_USED and NumberFormatter::ROUNDING_MODE. | ||
553 | * @param int $value The attribute value. The only currently supported rounding modes are | ||
554 | * NumberFormatter::ROUND_HALFEVEN, NumberFormatter::ROUND_HALFDOWN and | ||
555 | * NumberFormatter::ROUND_HALFUP. | ||
556 | * | ||
557 | * @return Boolean true on success or false on failure | ||
558 | * | ||
559 | * @see http://www.php.net/manual/en/numberformatter.setattribute.php | ||
560 | * | ||
561 | * @throws MethodArgumentValueNotImplementedException When the $attr is not supported | ||
562 | * @throws MethodArgumentValueNotImplementedException When the $value is not supported | ||
563 | */ | ||
564 | public function setAttribute($attr, $value) | ||
565 | { | ||
566 | if (!in_array($attr, self::$supportedAttributes)) { | ||
567 | $message = sprintf( | ||
568 | 'The available attributes are: %s', | ||
569 | implode(', ', array_keys(self::$supportedAttributes)) | ||
570 | ); | ||
571 | |||
572 | throw new MethodArgumentValueNotImplementedException(__METHOD__, 'attr', $value, $message); | ||
573 | } | ||
574 | |||
575 | if (self::$supportedAttributes['ROUNDING_MODE'] == $attr && $this->isInvalidRoundingMode($value)) { | ||
576 | $message = sprintf( | ||
577 | 'The supported values for ROUNDING_MODE are: %s', | ||
578 | implode(', ', array_keys(self::$roundingModes)) | ||
579 | ); | ||
580 | |||
581 | throw new MethodArgumentValueNotImplementedException(__METHOD__, 'attr', $value, $message); | ||
582 | } | ||
583 | |||
584 | if (self::$supportedAttributes['GROUPING_USED'] == $attr) { | ||
585 | $value = $this->normalizeGroupingUsedValue($value); | ||
586 | } | ||
587 | |||
588 | if (self::$supportedAttributes['FRACTION_DIGITS'] == $attr) { | ||
589 | $value = $this->normalizeFractionDigitsValue($value); | ||
590 | } | ||
591 | |||
592 | $this->attributes[$attr] = $value; | ||
593 | $this->initializedAttributes[$attr] = true; | ||
594 | |||
595 | return true; | ||
596 | } | ||
597 | |||
598 | /** | ||
599 | * Not supported. Set the formatter's pattern | ||
600 | * | ||
601 | * @param string $pattern A pattern string in conformance with the ICU DecimalFormat documentation | ||
602 | * | ||
603 | * @return Boolean true on success or false on failure | ||
604 | * | ||
605 | * @see http://www.php.net/manual/en/numberformatter.setpattern.php | ||
606 | * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details | ||
607 | * | ||
608 | * @throws MethodNotImplementedException | ||
609 | */ | ||
610 | public function setPattern($pattern) | ||
611 | { | ||
612 | throw new MethodNotImplementedException(__METHOD__); | ||
613 | } | ||
614 | |||
615 | /** | ||
616 | * Not supported. Set the formatter's symbol | ||
617 | * | ||
618 | * @param int $attr A symbol specifier, one of the format symbol constants | ||
619 | * @param string $value The value for the symbol | ||
620 | * | ||
621 | * @return Boolean true on success or false on failure | ||
622 | * | ||
623 | * @see http://www.php.net/manual/en/numberformatter.setsymbol.php | ||
624 | * | ||
625 | * @throws MethodNotImplementedException | ||
626 | */ | ||
627 | public function setSymbol($attr, $value) | ||
628 | { | ||
629 | throw new MethodNotImplementedException(__METHOD__); | ||
630 | } | ||
631 | |||
632 | /** | ||
633 | * Not supported. Set a text attribute | ||
634 | * | ||
635 | * @param int $attr An attribute specifier, one of the text attribute constants | ||
636 | * @param int $value The attribute value | ||
637 | * | ||
638 | * @return Boolean true on success or false on failure | ||
639 | * | ||
640 | * @see http://www.php.net/manual/en/numberformatter.settextattribute.php | ||
641 | * | ||
642 | * @throws MethodNotImplementedException | ||
643 | */ | ||
644 | public function setTextAttribute($attr, $value) | ||
645 | { | ||
646 | throw new MethodNotImplementedException(__METHOD__); | ||
647 | } | ||
648 | |||
649 | /** | ||
650 | * Set the error to the default U_ZERO_ERROR | ||
651 | */ | ||
652 | protected function resetError() | ||
653 | { | ||
654 | IntlGlobals::setError(IntlGlobals::U_ZERO_ERROR); | ||
655 | $this->errorCode = IntlGlobals::getErrorCode(); | ||
656 | $this->errorMessage = IntlGlobals::getErrorMessage(); | ||
657 | } | ||
658 | |||
659 | /** | ||
660 | * Rounds a currency value, applying increment rounding if applicable | ||
661 | * | ||
662 | * When a currency have a rounding increment, an extra round is made after the first one. The rounding factor is | ||
663 | * determined in the ICU data and is explained as of: | ||
664 | * | ||
665 | * "the rounding increment is given in units of 10^(-fraction_digits)" | ||
666 | * | ||
667 | * The only actual rounding data as of this writing, is CHF. | ||
668 | * | ||
669 | * @param float $value The numeric currency value | ||
670 | * @param string $currency The 3-letter ISO 4217 currency code indicating the currency to use | ||
671 | * | ||
672 | * @return string The rounded numeric currency value | ||
673 | * | ||
674 | * @see http://en.wikipedia.org/wiki/Swedish_rounding | ||
675 | * @see http://www.docjar.com/html/api/com/ibm/icu/util/Currency.java.html#1007 | ||
676 | */ | ||
677 | private function roundCurrency($value, $currency) | ||
678 | { | ||
679 | $fractionDigits = Intl::getCurrencyBundle()->getFractionDigits($currency); | ||
680 | $roundingIncrement = Intl::getCurrencyBundle()->getRoundingIncrement($currency); | ||
681 | |||
682 | // Round with the formatter rounding mode | ||
683 | $value = $this->round($value, $fractionDigits); | ||
684 | |||
685 | // Swiss rounding | ||
686 | if (0 < $roundingIncrement && 0 < $fractionDigits) { | ||
687 | $roundingFactor = $roundingIncrement / pow(10, $fractionDigits); | ||
688 | $value = round($value / $roundingFactor) * $roundingFactor; | ||
689 | } | ||
690 | |||
691 | return $value; | ||
692 | } | ||
693 | |||
694 | /** | ||
695 | * Rounds a value. | ||
696 | * | ||
697 | * @param integer|float $value The value to round | ||
698 | * @param int $precision The number of decimal digits to round to | ||
699 | * | ||
700 | * @return integer|float The rounded value | ||
701 | */ | ||
702 | private function round($value, $precision) | ||
703 | { | ||
704 | $precision = $this->getUnitializedPrecision($value, $precision); | ||
705 | |||
706 | $roundingMode = self::$phpRoundingMap[$this->getAttribute(self::ROUNDING_MODE)]; | ||
707 | $value = round($value, $precision, $roundingMode); | ||
708 | |||
709 | return $value; | ||
710 | } | ||
711 | |||
712 | /** | ||
713 | * Formats a number. | ||
714 | * | ||
715 | * @param integer|float $value The numeric value to format | ||
716 | * @param int $precision The number of decimal digits to use | ||
717 | * | ||
718 | * @return string The formatted number | ||
719 | */ | ||
720 | private function formatNumber($value, $precision) | ||
721 | { | ||
722 | $precision = $this->getUnitializedPrecision($value, $precision); | ||
723 | |||
724 | return number_format($value, $precision, '.', $this->getAttribute(self::GROUPING_USED) ? ',' : ''); | ||
725 | } | ||
726 | |||
727 | /** | ||
728 | * Returns the precision value if the DECIMAL style is being used and the FRACTION_DIGITS attribute is unitialized. | ||
729 | * | ||
730 | * @param integer|float $value The value to get the precision from if the FRACTION_DIGITS attribute is unitialized | ||
731 | * @param int $precision The precision value to returns if the FRACTION_DIGITS attribute is initialized | ||
732 | * | ||
733 | * @return int The precision value | ||
734 | */ | ||
735 | private function getUnitializedPrecision($value, $precision) | ||
736 | { | ||
737 | if ($this->style == self::CURRENCY) { | ||
738 | return $precision; | ||
739 | } | ||
740 | |||
741 | if (!$this->isInitializedAttribute(self::FRACTION_DIGITS)) { | ||
742 | preg_match('/.*\.(.*)/', (string) $value, $digits); | ||
743 | if (isset($digits[1])) { | ||
744 | $precision = strlen($digits[1]); | ||
745 | } | ||
746 | } | ||
747 | |||
748 | return $precision; | ||
749 | } | ||
750 | |||
751 | /** | ||
752 | * Check if the attribute is initialized (value set by client code). | ||
753 | * | ||
754 | * @param string $attr The attribute name | ||
755 | * | ||
756 | * @return Boolean true if the value was set by client, false otherwise | ||
757 | */ | ||
758 | private function isInitializedAttribute($attr) | ||
759 | { | ||
760 | return isset($this->initializedAttributes[$attr]); | ||
761 | } | ||
762 | |||
763 | /** | ||
764 | * Returns the numeric value using the $type to convert to the right data type. | ||
765 | * | ||
766 | * @param mixed $value The value to be converted | ||
767 | * @param int $type The type to convert. Can be TYPE_DOUBLE (float) or TYPE_INT32 (int) | ||
768 | * | ||
769 | * @return integer|float The converted value | ||
770 | */ | ||
771 | private function convertValueDataType($value, $type) | ||
772 | { | ||
773 | if ($type == self::TYPE_DOUBLE) { | ||
774 | $value = (float) $value; | ||
775 | } elseif ($type == self::TYPE_INT32) { | ||
776 | $value = $this->getInt32Value($value); | ||
777 | } elseif ($type == self::TYPE_INT64) { | ||
778 | $value = $this->getInt64Value($value); | ||
779 | } | ||
780 | |||
781 | return $value; | ||
782 | } | ||
783 | |||
784 | /** | ||
785 | * Convert the value data type to int or returns false if the value is out of the integer value range. | ||
786 | * | ||
787 | * @param mixed $value The value to be converted | ||
788 | * | ||
789 | * @return int The converted value | ||
790 | */ | ||
791 | private function getInt32Value($value) | ||
792 | { | ||
793 | if ($value > self::$int32Range['positive'] || $value < self::$int32Range['negative']) { | ||
794 | return false; | ||
795 | } | ||
796 | |||
797 | return (int) $value; | ||
798 | } | ||
799 | |||
800 | /** | ||
801 | * Convert the value data type to int or returns false if the value is out of the integer value range. | ||
802 | * | ||
803 | * @param mixed $value The value to be converted | ||
804 | * | ||
805 | * @return int|float The converted value | ||
806 | * | ||
807 | * @see https://bugs.php.net/bug.php?id=59597 Bug #59597 | ||
808 | */ | ||
809 | private function getInt64Value($value) | ||
810 | { | ||
811 | if ($value > self::$int64Range['positive'] || $value < self::$int64Range['negative']) { | ||
812 | return false; | ||
813 | } | ||
814 | |||
815 | if (PHP_INT_SIZE !== 8 && ($value > self::$int32Range['positive'] || $value <= self::$int32Range['negative'])) { | ||
816 | // Bug #59597 was fixed on PHP 5.3.14 and 5.4.4 | ||
817 | // The negative PHP_INT_MAX was being converted to float | ||
818 | if ( | ||
819 | $value == self::$int32Range['negative'] && | ||
820 | ( | ||
821 | (version_compare(PHP_VERSION, '5.4.0', '<') && version_compare(PHP_VERSION, '5.3.14', '>=')) || | ||
822 | version_compare(PHP_VERSION, '5.4.4', '>=') | ||
823 | ) | ||
824 | ) { | ||
825 | return (int) $value; | ||
826 | } | ||
827 | |||
828 | return (float) $value; | ||
829 | } | ||
830 | |||
831 | if (PHP_INT_SIZE === 8) { | ||
832 | // Bug #59597 was fixed on PHP 5.3.14 and 5.4.4 | ||
833 | // A 32 bit integer was being generated instead of a 64 bit integer | ||
834 | if ( | ||
835 | ($value > self::$int32Range['positive'] || $value < self::$int32Range['negative']) && | ||
836 | ( | ||
837 | (version_compare(PHP_VERSION, '5.3.14', '<')) || | ||
838 | (version_compare(PHP_VERSION, '5.4.0', '>=') && version_compare(PHP_VERSION, '5.4.4', '<')) | ||
839 | ) | ||
840 | ) { | ||
841 | $value = (-2147483648 - ($value % -2147483648)) * ($value / abs($value)); | ||
842 | } | ||
843 | } | ||
844 | |||
845 | return (int) $value; | ||
846 | } | ||
847 | |||
848 | /** | ||
849 | * Check if the rounding mode is invalid. | ||
850 | * | ||
851 | * @param int $value The rounding mode value to check | ||
852 | * | ||
853 | * @return Boolean true if the rounding mode is invalid, false otherwise | ||
854 | */ | ||
855 | private function isInvalidRoundingMode($value) | ||
856 | { | ||
857 | if (in_array($value, self::$roundingModes, true)) { | ||
858 | return false; | ||
859 | } | ||
860 | |||
861 | return true; | ||
862 | } | ||
863 | |||
864 | /** | ||
865 | * Returns the normalized value for the GROUPING_USED attribute. Any value that can be converted to int will be | ||
866 | * cast to Boolean and then to int again. This way, negative values are converted to 1 and string values to 0. | ||
867 | * | ||
868 | * @param mixed $value The value to be normalized | ||
869 | * | ||
870 | * @return int The normalized value for the attribute (0 or 1) | ||
871 | */ | ||
872 | private function normalizeGroupingUsedValue($value) | ||
873 | { | ||
874 | return (int) (Boolean) (int) $value; | ||
875 | } | ||
876 | |||
877 | /** | ||
878 | * Returns the normalized value for the FRACTION_DIGITS attribute. The value is converted to int and if negative, | ||
879 | * the returned value will be 0. | ||
880 | * | ||
881 | * @param mixed $value The value to be normalized | ||
882 | * | ||
883 | * @return int The normalized value for the attribute | ||
884 | */ | ||
885 | private function normalizeFractionDigitsValue($value) | ||
886 | { | ||
887 | $value = (int) $value; | ||
888 | |||
889 | return (0 > $value) ? 0 : $value; | ||
890 | } | ||
891 | } | ||