diff options
Diffstat (limited to 'vendor/commerceguys/intl/src/Formatter/CurrencyFormatter.php')
-rw-r--r-- | vendor/commerceguys/intl/src/Formatter/CurrencyFormatter.php | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/vendor/commerceguys/intl/src/Formatter/CurrencyFormatter.php b/vendor/commerceguys/intl/src/Formatter/CurrencyFormatter.php new file mode 100644 index 000000000..8d4d11f27 --- /dev/null +++ b/vendor/commerceguys/intl/src/Formatter/CurrencyFormatter.php @@ -0,0 +1,241 @@ +<?php + +namespace CommerceGuys\Intl\Formatter; + +use CommerceGuys\Intl\Currency\Currency; +use CommerceGuys\Intl\Currency\CurrencyRepositoryInterface; +use CommerceGuys\Intl\Exception\InvalidArgumentException; +use CommerceGuys\Intl\Exception\UnknownCurrencyException; +use CommerceGuys\Intl\NumberFormat\NumberFormat; +use CommerceGuys\Intl\NumberFormat\NumberFormatRepositoryInterface; + +/** + * Formats currency amounts using locale-specific patterns. + */ +class CurrencyFormatter implements CurrencyFormatterInterface +{ + use FormatterTrait; + + /** + * The number format repository. + * + * @var NumberFormatRepositoryInterface + */ + protected $numberFormatRepository; + + /** + * The currency repository. + * + * @var CurrencyRepositoryInterface + */ + protected $currencyRepository; + + /** + * The default locale. + * + * @var string + */ + protected $defaultLocale; + + /** + * The loaded number formats. + * + * @var NumberFormat[] + */ + protected $numberFormats = []; + + /** + * The loaded currencies. + * + * @var Currency[] + */ + protected $currencies = []; + + /** + * The default options. + * + * @var array + */ + protected $defaultOptions = [ + 'locale' => 'en', + 'use_grouping' => true, + 'minimum_fraction_digits' => null, + 'maximum_fraction_digits' => null, + 'rounding_mode' => PHP_ROUND_HALF_UP, + 'style' => 'standard', + 'currency_display' => 'symbol', + ]; + + /** + * Creates a CurrencyFormatter instance. + * + * @param NumberFormatRepositoryInterface $numberFormatRepository The number format repository. + * @param CurrencyRepositoryInterface $currencyRepository The currency repository. + * @param array $defaultOptions The default options. + * + * @throws \RuntimeException + */ + public function __construct(NumberFormatRepositoryInterface $numberFormatRepository, CurrencyRepositoryInterface $currencyRepository, array $defaultOptions = []) + { + if (!extension_loaded('bcmath')) { + throw new \RuntimeException('The bcmath extension is required by CurrencyFormatter.'); + } + $this->validateOptions($defaultOptions); + + $this->numberFormatRepository = $numberFormatRepository; + $this->currencyRepository = $currencyRepository; + $this->defaultOptions = array_replace($this->defaultOptions, $defaultOptions); + } + + /** + * {@inheritdoc} + */ + public function format($number, $currencyCode, array $options = []) + { + if (!is_numeric($number)) { + $message = sprintf('The provided value "%s" is not a valid number or numeric string.', $number); + throw new InvalidArgumentException($message); + } + + $this->validateOptions($options); + $options = array_replace($this->defaultOptions, $options); + $numberFormat = $this->getNumberFormat($options['locale']); + $currency = $this->getCurrency($currencyCode, $options['locale']); + // Use the currency defaults if the values weren't set by the caller. + if (!isset($options['minimum_fraction_digits'])) { + $options['minimum_fraction_digits'] = $currency->getFractionDigits(); + } + if (!isset($options['maximum_fraction_digits'])) { + $options['maximum_fraction_digits'] = $currency->getFractionDigits(); + } + + $number = (string) $number; + $number = $this->formatNumber($number, $numberFormat, $options); + if ($options['currency_display'] == 'symbol') { + $number = str_replace('¤', $currency->getSymbol(), $number); + } elseif ($options['currency_display'] == 'code') { + $number = str_replace('¤', $currency->getCurrencyCode(), $number); + } else { + // No symbol should be displayed. Remove leftover whitespace. + $number = str_replace('¤', '', $number); + $number = trim($number, " \xC2\xA0"); + } + + return $number; + } + + /** + * {@inheritdoc} + */ + public function parse($number, $currencyCode, array $options = []) + { + $this->validateOptions($options); + $options = array_replace($this->defaultOptions, $options); + $numberFormat = $this->getNumberFormat($options['locale']); + $currency = $this->getCurrency($currencyCode, $options['locale']); + $replacements = [ + // Strip the currency code or symbol. + $currency->getCurrencyCode() => '', + $currency->getSymbol() => '', + ]; + $number = strtr($number, $replacements); + $number = $this->parseNumber($number, $numberFormat); + + return $number; + } + + /** + * Gets the number format for the provided locale. + * + * @param string $locale The locale. + * + * @return NumberFormat + */ + protected function getNumberFormat($locale) + { + if (!isset($this->numberFormats[$locale])) { + $this->numberFormats[$locale] = $this->numberFormatRepository->get($locale); + } + + return $this->numberFormats[$locale]; + } + + /** + * Gets the currency for the provided currency code and locale. + * + * @param string $currencyCode The currency code. + * @param string $locale The locale. + * + * @return Currency + */ + protected function getCurrency($currencyCode, $locale) + { + if (!isset($this->currencies[$currencyCode][$locale])) { + try { + $currency = $this->currencyRepository->get($currencyCode, $locale); + } catch (UnknownCurrencyException $e) { + // The requested currency was not found. Fall back + // to a dummy object to show just the currency code. + $currency = new Currency([ + 'currency_code' => $currencyCode, + 'name' => $currencyCode, + 'numeric_code' => '000', + 'locale' => $locale, + ]); + } + $this->currencies[$currencyCode][$locale] = $currency; + } + + return $this->currencies[$currencyCode][$locale]; + } + + /** + * {@inheritdoc} + */ + protected function getAvailablePatterns(NumberFormat $numberFormat) + { + return [ + 'standard' => $numberFormat->getCurrencyPattern(), + 'accounting' => $numberFormat->getAccountingCurrencyPattern(), + ]; + } + + /** + * Validates the provided options. + * + * Ensures the absence of unknown keys, correct data types and values. + * + * @param array $options The options. + * + * @throws \InvalidArgumentException + */ + protected function validateOptions(array $options) + { + foreach ($options as $option => $value) { + if (!array_key_exists($option, $this->defaultOptions)) { + throw new InvalidArgumentException(sprintf('Unrecognized option "%s".', $option)); + } + } + if (isset($options['use_grouping']) && !is_bool($options['use_grouping'])) { + throw new InvalidArgumentException('The option "use_grouping" must be a boolean.'); + } + foreach (['minimum_fraction_digits', 'maximum_fraction_digits'] as $option) { + if (array_key_exists($option, $options) && !is_numeric($options[$option])) { + throw new InvalidArgumentException(sprintf('The option "%s" must be numeric.', $option)); + } + } + $roundingModes = [ + PHP_ROUND_HALF_UP, PHP_ROUND_HALF_DOWN, + PHP_ROUND_HALF_EVEN, PHP_ROUND_HALF_ODD, 'none', + ]; + if (!empty($options['rounding_mode']) && !in_array($options['rounding_mode'], $roundingModes)) { + throw new InvalidArgumentException(sprintf('Unrecognized rounding mode "%s".', $options['rounding_mode'])); + } + if (!empty($options['style']) && !in_array($options['style'], ['standard', 'accounting'])) { + throw new InvalidArgumentException(sprintf('Unrecognized style "%s".', $options['style'])); + } + if (!empty($options['currency_display']) && !in_array($options['currency_display'], ['code', 'symbol', 'none'])) { + throw new InvalidArgumentException(sprintf('Unrecognized currency display "%s".', $options['currency_display'])); + } + } +} |