PageRenderTime 844ms CodeModel.GetById 14ms RepoModel.GetById 2ms app.codeStats 1ms

/src/Symfony/Component/Locale/Stub/StubNumberFormatter.php

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