-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[Intl] Add methods to filter currencies more precisely #61431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 7.4
Are you sure you want to change the base?
Changes from all commits
f038af2
1276849
62312ed
449afd2
c7719cc
8d96d1c
ab6e0f1
6ee933d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -139,6 +139,156 @@ public static function forNumericCode(int $numericCode): array | |
return self::readEntry(['NumericToAlpha3', (string) $numericCode], 'meta'); | ||
} | ||
|
||
/** | ||
* @param non-empty-string $country e.g. 'FR' | ||
* @param ?bool $legalTender if the currency must be a legal tender. Using null do not filter anything | ||
* @param bool $activeOnly exclude currencies that are not active on the given $date in countries | ||
* @param string $date The date string on which the check will be performed | ||
* | ||
* @throws MissingResourceException if the given $country does not exist | ||
* | ||
* @return array<non-empty-string, array{from?: non-empty-string, to?: non-empty-string, tender?: bool}> | ||
*/ | ||
public static function forCountry( | ||
string $country, | ||
?bool $legalTender = true, | ||
bool $activeOnly = true, | ||
string $date = 'today', | ||
): array { | ||
$currenciesMetadata = self::readEntry(['Map', $country], 'meta'); | ||
|
||
$currencies = []; | ||
|
||
foreach ($currenciesMetadata as $currency => $currencyMetadata) { | ||
if (null !== $legalTender && $legalTender !== self::isLegalTender($currencyMetadata)) { | ||
continue; | ||
} | ||
|
||
if (!$activeOnly) { | ||
$currencies[$currency] = $currencyMetadata; | ||
|
||
continue; | ||
} | ||
|
||
if (!self::isActive($country, $currency, $currencyMetadata, $date)) { | ||
continue; | ||
} | ||
|
||
$currencies[$currency] = $currencyMetadata; | ||
} | ||
|
||
return $currencies; | ||
} | ||
|
||
/** | ||
* @param non-empty-string $country e.g. 'FR' | ||
* @param non-empty-string $currency e.g. 'USD' | ||
* @param string $date The date that will be checked when $activeOnly is set to true | ||
*/ | ||
public static function existsInCountry( | ||
string $country, | ||
string $currency, | ||
bool $activeOnly = true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nullable boolean There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case, just use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, I'm also fine with the nullable parameter. |
||
string $date = 'today', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
providing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok 👍 |
||
): bool { | ||
try { | ||
$currencyMetadata = self::readEntry(['Map', $country, $currency], 'meta'); | ||
} catch (MissingResourceException) { | ||
return false; | ||
} | ||
|
||
if (!$activeOnly) { | ||
return true; | ||
} | ||
|
||
return self::isActive($country, $currency, $currencyMetadata, $date); | ||
} | ||
|
||
/** | ||
* @param array{tender?: bool} $currencyMetadata | ||
*/ | ||
private static function isLegalTender(array $currencyMetadata): bool | ||
{ | ||
return !\array_key_exists('tender', $currencyMetadata) || false !== $currencyMetadata['tender']; | ||
} | ||
|
||
/** | ||
* @param non-empty-string $country e.g. 'FR' | ||
* @param non-empty-string $currency e.g. 'USD' | ||
* @param array{from?: non-empty-string, to?: non-empty-string} $currencyMetadata | ||
* @param string $date The date on which the check will be performed | ||
*/ | ||
private static function isActive(string $country, string $currency, array $currencyMetadata, string $date = 'today'): bool | ||
{ | ||
if (!\array_key_exists('from', $currencyMetadata)) { | ||
// Note: currencies that are not legal tender don't have often validity dates. | ||
throw new \RuntimeException("Cannot check whether the currency $currency is active or not because they are no validity dates available."); | ||
} | ||
|
||
$from = \DateTimeImmutable::createFromFormat('Y-m-d', $currencyMetadata['from']); | ||
|
||
if (!$from) { | ||
throw new \RuntimeException("Unable to parse the `from` date for currency $currency in country $country."); | ||
} | ||
|
||
if (\array_key_exists('to', $currencyMetadata)) { | ||
$to = \DateTimeImmutable::createFromFormat('Y-m-d', $currencyMetadata['to']); | ||
|
||
if (!$to) { | ||
throw new \RuntimeException("Unable to parse the `to` date for currency $currency in country $country."); | ||
} | ||
} else { | ||
$to = null; | ||
} | ||
|
||
try { | ||
$date = new \DateTimeImmutable($date); | ||
} catch (\DateMalformedStringException $exception) { | ||
throw new \InvalidArgumentException("Invalid date provided: $date.", previous: $exception); | ||
} | ||
|
||
return $from <= $date && (null === $to || $to >= $date); | ||
} | ||
|
||
/** | ||
* @param non-empty-string $currency e.g. 'USD' | ||
* @param ?bool $legalTender if the currency must be a legal tender. Using null do not filter anything | ||
* @param bool $activeOnly exclude currencies that are not active on the given $date in countries | ||
* @param string $date the date on which the check will be performed if $activeOnly is set to true | ||
*/ | ||
public static function existsInAtLeastOneCountry( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this feels a bit weird naming compared to in that sense im still fine with public API There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, renaming the method to |
||
string $currency, | ||
?bool $legalTender = true, | ||
bool $activeOnly = true, | ||
string $date = 'today', | ||
): bool { | ||
$countries = self::readEntry(['Map'], 'meta'); | ||
|
||
foreach ($countries as $countryCode => $country) { | ||
foreach ($country as $currencyCode => $currencyMetadata) { | ||
if ($currencyCode !== $currency) { | ||
continue; | ||
} | ||
|
||
if (null !== $legalTender && $legalTender !== self::isLegalTender($currencyMetadata)) { | ||
continue; | ||
} | ||
|
||
if (!$activeOnly) { | ||
return true; | ||
} | ||
|
||
if (!self::isActive($countryCode, $currencyCode, $currencyMetadata, $date)) { | ||
continue; | ||
} | ||
|
||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
protected static function getPath(): string | ||
{ | ||
return Intl::getDataDirectory().'/'.Intl::CURRENCY_DIR; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
im not sure this is the proper return type compared to
getCurrencyCodes
andforNumeric
, unexpected at least i think