WebOrbiton
v1.0.0.0

StocketBase

152 lines · 8.4 KB
  1. <?php
  2. ​
  3. declare(strict_types=1);
  4. ​
  5. final class Currency
  6. {
  7. public const DEFAULT_CODE = 'USD';
  8. ​
  9. private const ZERO_DECIMALS = ['BIF', 'CLP', 'DJF', 'GNF', 'ISK', 'JPY', 'KMF', 'KRW', 'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'];
  10. private const THREE_DECIMALS = ['BHD', 'IQD', 'JOD', 'KWD', 'LYD', 'OMR', 'TND'];
  11. ​
  12. private const NAMES = [
  13. 'AED' => 'UAE Dirham', 'AFN' => 'Afghan Afghani', 'ALL' => 'Albanian Lek', 'AMD' => 'Armenian Dram',
  14. 'ANG' => 'Netherlands Antillean Guilder', 'AOA' => 'Angolan Kwanza', 'ARS' => 'Argentine Peso',
  15. 'AUD' => 'Australian Dollar', 'AWG' => 'Aruban Florin', 'AZN' => 'Azerbaijani Manat',
  16. 'BAM' => 'Bosnia-Herzegovina Convertible Mark', 'BBD' => 'Barbadian Dollar', 'BDT' => 'Bangladeshi Taka',
  17. 'BGN' => 'Bulgarian Lev', 'BHD' => 'Bahraini Dinar', 'BIF' => 'Burundian Franc', 'BMD' => 'Bermudian Dollar',
  18. 'BND' => 'Brunei Dollar', 'BOB' => 'Bolivian Boliviano', 'BRL' => 'Brazilian Real', 'BSD' => 'Bahamian Dollar',
  19. 'BTN' => 'Bhutanese Ngultrum', 'BWP' => 'Botswana Pula', 'BYN' => 'Belarusian Ruble', 'BZD' => 'Belize Dollar',
  20. 'CAD' => 'Canadian Dollar', 'CDF' => 'Congolese Franc', 'CHF' => 'Swiss Franc', 'CLP' => 'Chilean Peso',
  21. 'CNY' => 'Chinese Yuan', 'COP' => 'Colombian Peso', 'CRC' => 'Costa Rican Colón', 'CUP' => 'Cuban Peso',
  22. 'CVE' => 'Cape Verdean Escudo', 'CZK' => 'Czech Koruna', 'DJF' => 'Djiboutian Franc', 'DKK' => 'Danish Krone',
  23. 'DOP' => 'Dominican Peso', 'DZD' => 'Algerian Dinar', 'EGP' => 'Egyptian Pound', 'ERN' => 'Eritrean Nakfa',
  24. 'ETB' => 'Ethiopian Birr', 'EUR' => 'Euro', 'FJD' => 'Fijian Dollar', 'FKP' => 'Falkland Islands Pound',
  25. 'GBP' => 'British Pound', 'GEL' => 'Georgian Lari', 'GHS' => 'Ghanaian Cedi', 'GIP' => 'Gibraltar Pound',
  26. 'GMD' => 'Gambian Dalasi', 'GNF' => 'Guinean Franc', 'GTQ' => 'Guatemalan Quetzal', 'GYD' => 'Guyanese Dollar',
  27. 'HKD' => 'Hong Kong Dollar', 'HNL' => 'Honduran Lempira', 'HTG' => 'Haitian Gourde', 'HUF' => 'Hungarian Forint',
  28. 'IDR' => 'Indonesian Rupiah', 'ILS' => 'Israeli New Shekel', 'INR' => 'Indian Rupee', 'IQD' => 'Iraqi Dinar',
  29. 'IRR' => 'Iranian Rial', 'ISK' => 'Icelandic Króna', 'JMD' => 'Jamaican Dollar', 'JOD' => 'Jordanian Dinar',
  30. 'JPY' => 'Japanese Yen', 'KES' => 'Kenyan Shilling', 'KGS' => 'Kyrgyzstani Som', 'KHR' => 'Cambodian Riel',
  31. 'KMF' => 'Comorian Franc', 'KPW' => 'North Korean Won', 'KRW' => 'South Korean Won', 'KWD' => 'Kuwaiti Dinar',
  32. 'KYD' => 'Cayman Islands Dollar', 'KZT' => 'Kazakhstani Tenge', 'LAK' => 'Lao Kip', 'LBP' => 'Lebanese Pound',
  33. 'LKR' => 'Sri Lankan Rupee', 'LRD' => 'Liberian Dollar', 'LSL' => 'Lesotho Loti', 'LYD' => 'Libyan Dinar',
  34. 'MAD' => 'Moroccan Dirham', 'MDL' => 'Moldovan Leu', 'MGA' => 'Malagasy Ariary', 'MKD' => 'Macedonian Denar',
  35. 'MMK' => 'Myanmar Kyat', 'MNT' => 'Mongolian Tögrög', 'MOP' => 'Macanese Pataca', 'MRU' => 'Mauritanian Ouguiya',
  36. 'MUR' => 'Mauritian Rupee', 'MVR' => 'Maldivian Rufiyaa', 'MWK' => 'Malawian Kwacha', 'MXN' => 'Mexican Peso',
  37. 'MYR' => 'Malaysian Ringgit', 'MZN' => 'Mozambican Metical', 'NAD' => 'Namibian Dollar', 'NGN' => 'Nigerian Naira',
  38. 'NIO' => 'Nicaraguan Córdoba', 'NOK' => 'Norwegian Krone', 'NPR' => 'Nepalese Rupee', 'NZD' => 'New Zealand Dollar',
  39. 'OMR' => 'Omani Rial', 'PAB' => 'Panamanian Balboa', 'PEN' => 'Peruvian Sol', 'PGK' => 'Papua New Guinean Kina',
  40. 'PHP' => 'Philippine Peso', 'PKR' => 'Pakistani Rupee', 'PLN' => 'Polish Złoty', 'PYG' => 'Paraguayan Guaraní',
  41. 'QAR' => 'Qatari Riyal', 'RON' => 'Romanian Leu', 'RSD' => 'Serbian Dinar', 'RUB' => 'Russian Ruble',
  42. 'RWF' => 'Rwandan Franc', 'SAR' => 'Saudi Riyal', 'SBD' => 'Solomon Islands Dollar', 'SCR' => 'Seychellois Rupee',
  43. 'SDG' => 'Sudanese Pound', 'SEK' => 'Swedish Krona', 'SGD' => 'Singapore Dollar', 'SHP' => 'Saint Helena Pound',
  44. 'SLE' => 'Sierra Leonean Leone', 'SOS' => 'Somali Shilling', 'SRD' => 'Surinamese Dollar', 'SSP' => 'South Sudanese Pound',
  45. 'STN' => 'São Tomé and Príncipe Dobra', 'SVC' => 'Salvadoran Colón', 'SYP' => 'Syrian Pound', 'SZL' => 'Swazi Lilangeni',
  46. 'THB' => 'Thai Baht', 'TJS' => 'Tajikistani Somoni', 'TMT' => 'Turkmenistani Manat', 'TND' => 'Tunisian Dinar',
  47. 'TOP' => 'Tongan Paʻanga', 'TRY' => 'Turkish Lira', 'TTD' => 'Trinidad and Tobago Dollar', 'TWD' => 'New Taiwan Dollar',
  48. 'TZS' => 'Tanzanian Shilling', 'UAH' => 'Ukrainian Hryvnia', 'UGX' => 'Ugandan Shilling', 'USD' => 'US Dollar',
  49. 'UYU' => 'Uruguayan Peso', 'UZS' => 'Uzbekistani Som', 'VES' => 'Venezuelan Bolívar', 'VND' => 'Vietnamese Dong',
  50. 'VUV' => 'Vanuatu Vatu', 'WST' => 'Samoan Tala', 'XAF' => 'Central African CFA Franc', 'XCD' => 'East Caribbean Dollar',
  51. 'XOF' => 'West African CFA Franc', 'XPF' => 'CFP Franc', 'YER' => 'Yemeni Rial', 'ZAR' => 'South African Rand',
  52. 'ZMW' => 'Zambian Kwacha', 'ZWL' => 'Zimbabwean Dollar',
  53. ];
  54. ​
  55. private const UNITS = [
  56. 'USD' => ['dollars', 'cents'], 'CAD' => ['dollars', 'cents'], 'AUD' => ['dollars', 'cents'], 'NZD' => ['dollars', 'cents'],
  57. 'HKD' => ['dollars', 'cents'], 'SGD' => ['dollars', 'cents'], 'EUR' => ['euros', 'cents'], 'PLN' => ['złote', 'grosze'],
  58. 'GBP' => ['pounds', 'pence'], 'CHF' => ['francs', 'rappen'], 'CZK' => ['koruny', 'haléře'], 'SEK' => ['kronor', 'öre'],
  59. 'NOK' => ['kroner', 'øre'], 'DKK' => ['kroner', 'øre'], 'HUF' => ['forints', 'fillér'], 'RON' => ['lei', 'bani'],
  60. 'BGN' => ['leva', 'stotinki'], 'UAH' => ['hryvnias', 'kopiykas'], 'TRY' => ['lira', 'kuruş'], 'INR' => ['rupees', 'paise'],
  61. 'CNY' => ['yuan', 'fen'], 'BRL' => ['reais', 'centavos'], 'MXN' => ['pesos', 'centavos'], 'RUB' => ['rubles', 'kopecks'],
  62. 'ZAR' => ['rand', 'cents'], 'ILS' => ['shekels', 'agorot'], 'JPY' => ['yen', ''], 'KRW' => ['won', ''],
  63. ];
  64. ​
  65. public static function searchLabel(string $code): string
  66. {
  67. $name = self::NAMES[$code] ?? $code;
  68. $plain = strtr($name, ['ł' => 'l', 'Ł' => 'L', 'ó' => 'o', 'í' => 'i', 'é' => 'e', 'ö' => 'o', 'ã' => 'a', 'ú' => 'u', 'ʻ' => '', 'ʼ' => '']);
  69. ​
  70. return $plain === $name ? $name : $name . ' / ' . $plain;
  71. }
  72. ​
  73. public static function units(string $code): array
  74. {
  75. $code = self::normalize($code);
  76. ​
  77. return self::UNITS[$code] ?? ['whole units', 'fraction'];
  78. }
  79. ​
  80. public static function clientMap(): array
  81. {
  82. $map = [];
  83. foreach (self::NAMES as $code => $name) {
  84. [$major, $minor] = self::units($code);
  85. $map[$code] = ['d' => self::decimals($code), 'major' => $major, 'minor' => $minor];
  86. }
  87. ​
  88. return $map;
  89. }
  90. ​
  91. public static function toMinor(int $major, int $fraction, string $code): int
  92. {
  93. $decimals = self::decimals($code);
  94. $scale = 10 ** $decimals;
  95. $major = max(0, min(100000000, $major));
  96. $fraction = $decimals === 0 ? 0 : max(0, min($scale - 1, $fraction));
  97. ​
  98. return $major * $scale + $fraction;
  99. }
  100. ​
  101. public static function split(int $minorUnits, string $code): array
  102. {
  103. $scale = 10 ** self::decimals($code);
  104. $minorUnits = max(0, $minorUnits);
  105. ​
  106. return [intdiv($minorUnits, $scale), $minorUnits % $scale];
  107. }
  108. ​
  109. public static function all(): array
  110. {
  111. return self::NAMES;
  112. }
  113. ​
  114. public static function isValid(string $code): bool
  115. {
  116. return isset(self::NAMES[$code]);
  117. }
  118. ​
  119. public static function normalize(string $code): string
  120. {
  121. $code = strtoupper(trim($code));
  122. ​
  123. return self::isValid($code) ? $code : self::DEFAULT_CODE;
  124. }
  125. ​
  126. public static function decimals(string $code): int
  127. {
  128. if (in_array($code, self::ZERO_DECIMALS, true)) {
  129. return 0;
  130. }
  131. ​
  132. return in_array($code, self::THREE_DECIMALS, true) ? 3 : 2;
  133. }
  134. ​
  135. public static function format(int $minorUnits, string $code): string
  136. {
  137. $code = self::normalize($code);
  138. $decimals = self::decimals($code);
  139. $amount = $minorUnits / (10 ** $decimals);
  140. ​
  141. if (class_exists('NumberFormatter')) {
  142. $formatter = new NumberFormatter('en_US', NumberFormatter::CURRENCY);
  143. $formatted = $formatter->formatCurrency($amount, $code);
  144. if (is_string($formatted) && $formatted !== '') {
  145. return $formatted;
  146. }
  147. }
  148. ​
  149. return number_format($amount, $decimals, '.', ',') . ' ' . $code;
  150. }
  151. }
  152. ​