StubNumberFormatter.php 28 KB

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