File: D:/HostingSpaces/SBogers10/blije-gasten.komma.pro/app/Komma/Globalization/RegionInfo.php
<?php
namespace App\Komma\Globalization;
use App\Komma\Globalization\Datasets\Countries;
use Closure;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
/**
* Class RegionInfo
*
* Contains information about the country/region after constructing it.
* Inspired on the RegionInfo class from C#. But modified to our needs.
*
* @see https://docs.microsoft.com/en-us/dotnet/api/system.globalization.regioninfo?view=netframework-4.7.2
* @package App\Komma\Globalization
*/
class RegionInfo extends AbstractCultureParser implements RegionInfoInterface
{
/** @var RegionInfoInterface[] */
private static $cache = [];
/** @var string $displayName */
private $displayName = '';
/** @var string $nativeName */
private $nativeName = '';
/** @var string $currencyName */
private $currencyName = '';
/** @var string */
private $currencyMinorName = '';
/** @var string $currencySymbol */
private $currencySymbol = '';
/** @var string $ISOCurrencySymbol */
private $ISOCurrencySymbol = '';
/** @var bool $isMetric */
private $isMetric = false;
/** @var string $threeLetterISORegionName */
private $threeLetterISORegionName = '';
/** @var string $twoLetterISORegionName */
private $twoLetterISORegionName = '';
/** @var NumberFormatInfo $numberFormat */
private $numberFormat;
/** @var Collection */
private $languages;
/** @var bool True if it is bound to a Region only. false if it is bound to a Region AND a language */
private $isNeutral = false;
/**
* AbstractCultureParser constructor.
*
* Is protected to prevent construction using the constructor. Use getInstance instead.
*/
protected function __construct() {}
/**
* RegionInfo constructor.
*
* Use this function to get a new instance or cached instance of this class.
*
* The format for the culture name based on RFC 4646 is languagecode2>-country/regioncode2, where languagecode2 is the two-letter
* language code and country/regioncode2 is the two-letter subculture code. Examples include ja-JP for Japanese (Japan)
* and en-US for English (United States). In cases where a two-letter language code is not available,
* a three-letter code derived from ISO 639-2 is used.
*
* Notice that some culture names also specify an ISO 15924 script. For example, Cyrl specifies the Cyrillic script
* and Latin specifies the Latin script. A culture name that includes a script uses the pattern languagecode2-scripttag-country/regioncode2.
* An example of this type of culture name is uz-Cyrl-UZ for Uzbek (Cyrillic, Uzbekistan).
*
* A neutral culture is specified by only the two-letter lowercase language code. For example, fr specifies the neutral culture for French, and de specifies the neutral culture for German.
*
* @param string $name
* @return RegionInfoInterface|null
*/
public static function getInstance($name)
{
//Create a new instance and parse its name.
$instance = new static();
$instance->parseName($name);
$instance->name = implode('-', array_values(array_filter([strtolower($instance->languageIso2), ucfirst(strtolower($instance->scriptTagCountry)), strtoupper($instance->countryOrRegionCode2)])));
if(in_array($instance->name, array_keys(self::$cache), true)) return self::$cache[$instance->name]; //Return a cached instance if available.
//First find the country by its ISO2 name
$found = false;
foreach(Countries::DATA as $index => $data) {
if(strtolower($data['Name']) === strtolower($instance->countryOrRegionCode2)) {
//Then set data
$instance->isMetric = $data['IsMetric'];
$instance->twoLetterISORegionName = $data['TwoLetterISORegionName'];
$instance->threeLetterISORegionName = $data['ThreeLetterISORegionName'];
$instance->currencySymbol = $data['CurrencySymbol'];
$instance->ISOCurrencySymbol = $data['ISOCurrencySymbol'];
$instance->currencyName = $data['CurrencyName'];
$instance->currencyMinorName = $data['CurrencyMinorName'];
$instance->currencySymbol = $data['CurrencySymbol'];
$instance->isNeutral = !($instance->countryOrRegionCode2 && $instance->languageIso2);
$instance->setupNumberFormat($data);
$instance->setupLanguages($data);
$instance->setDisplayName($data);
$instance->setNativeName($data);
$found = true;
break;
}
}
if(!$found) {
throw new \InvalidArgumentException('RegionInfo: No information available for: '.$name);
}
self::$cache[$instance->name] = $instance; //Cache this instance if not cached.
return $instance;
}
/**
* Sets up the number format info instance.
*/
private function setupNumberFormat($data)
{
$this->numberFormat = new NumberFormatInfo();
$this->numberFormat->setCurrencyDecimalDigits($data['numberFormatting']['currencyDecimalDigits']);
$this->numberFormat->setCurrencyDecimalSeparator($data['numberFormatting']['currencyDecimalSeparator']);
$this->numberFormat->setCurrencyGroupSeparator($data['numberFormatting']['currencyGroupSeparator']);
$this->numberFormat->setCurrencyGroupSizes($data['numberFormatting']['currencyGroupSizes']);
}
/**
* Parses the region name. It must either be a two-letter region name, such as "US" for the United States,
* or the name of a specific culture, such as "en-US" for English (United States)
*
* @param string $name
*/
function parseName(string $name)
{
if ($name !== "") {
$explodedName = explode('-', $name);
$count = count($explodedName);
if ($count == 2) {
$this->languageIso2 = strtolower($explodedName[0]);
$this->countryOrRegionCode2 = strtolower($explodedName[1]);
return;
} elseif ($count == 1) {
$this->countryOrRegionCode2 = strtolower($explodedName[0]);
return;
}
} else {
return;
}
throw new \RuntimeException('The name was invalid it must either be a two-letter region name, such as "US" for the United States, or the name of a specific culture, such as "en-US" for English (United States). Was: '.$name);
}
/**
* @param array $data
*/
private function setupLanguages(array $data)
{
$this->languages = new Collection();
if($this->languageIso2 == '') {
//Push all of its languages
foreach ($data['ISOLanguages'] as $languageIso2) {
if($languageIso2 != '') {
$language = Language::getInstance($languageIso2);
if($language->isComplete()) $this->languages->push($language);
}
}
} else {
//Push a specific language
$language = Language::getInstance($this->languageIso2);
if($language->isComplete()) $this->languages->push($language);
}
}
/**
* @param $data
*/
private function setDisplayName($data)
{
if($this->isNeutral) {
$this->displayName = $data['DisplayName'];
} else {
/** @var Language $language */
$language = $this->languages->first();
$languageDisplayName = $language->getDisplayName() !== '' ? $language->getDisplayName() : $language->getName();
$this->displayName = $languageDisplayName.' ('.$data['DisplayName'].')';
}
}
/**
* @param $data
*/
private function setNativeName($data)
{
if($this->isNeutral) {
$this->nativeName = $data['NativeName'];
} else {
/** @var Language $language */
$language = $this->languages->first();
$languageNativeName = $language->getNativeName() !== '' ? $language->getNativeName() : $language->getName();
$nativeNamePart = ($data['NativeName'] !== '') ? '('.$data['NativeName'].')' : '('.$data['DisplayName'].')';
$this->nativeName = $languageNativeName.' '.$nativeNamePart;
}
}
/**
* @return Collection
*/
public static function getAllCultures()
{
$allCultures = static::cultureBuilderLoop(function(RegionInfo $neutralCultureRegionInfo) {
$specificCultures = $neutralCultureRegionInfo->getLanguages()->map(function(Language $language) use($neutralCultureRegionInfo) {
$name = strtolower($language->getName()).'-'.strtoupper($neutralCultureRegionInfo->getName());
$specificCulture = self::getInstance($name);
return $specificCulture;
});
return collect([$neutralCultureRegionInfo])->merge($specificCultures);
});
return $allCultures;
}
/**
* Return all cultures that are both bound to a language and a region
* @return Collection
*/
public static function getSpecificCultures()
{
$specificCultures = static::cultureBuilderLoop(function(RegionInfo $neutralCultureRegionInfo) {
$specificCultures = $neutralCultureRegionInfo->getLanguages()->map(function(Language $language) use($neutralCultureRegionInfo) {
$name = strtolower($language->getName()).'-'.strtoupper($neutralCultureRegionInfo->getName());
$specificCulture = self::getInstance($name);
return $specificCulture;
});
return $specificCultures;
});
return $specificCultures;
}
/**
* Return all cultures that are bound to a language but not to a region.
*
* @return Collection
*/
public static function getNeutralCultures()
{
return static::cultureBuilderLoop();
}
/**
* Loops over all countries and creates regionInfo instances for them (or retrieves cached versions).
* And for each of them executes an optional closure that can edit the regionInfo instance, or return multiple of them
*
* @param Closure|null $cultureEditor
* @return Collection
*/
private static function cultureBuilderLoop(Closure $cultureEditor = null)
{
$instances = collect();
foreach(Countries::DATA as $index => $data) {
$neutralInstance = self::getInstance($data['Name']);
if($cultureEditor) {
$neutralInstances = $cultureEditor($neutralInstance); //Must returns an instance of RegionInfo or a collection of RegionInfo instances.
$neutralInstances = Collection::wrap($neutralInstances);
$neutralInstances->each(function($regionInfo) use(&$instances, $neutralInstances) {
$instances->push($regionInfo);
});
} else {
$instances->push($neutralInstance);
}
}
return $instances;
}
/**
* Returns the neutral region (the one not associated to a language
*/
public function getNeutralRegionInfo(): RegionInfoInterface
{
return self::getInstance($this->countryOrRegionCode2);
}
/**
* Find one or more regionInfo's by a certain where statement
*
* @param $name
* @param $operator
* @param $value
* @return Collection|null
*/
public static function getWhere($name, $operator, $value)
{
$foundRegionInfos = self::getAllCultures()->filter(function(RegionInfo $regionInfo) use ($name, $operator, $value) {
$methodName = 'get'.ucfirst(Str::camel($name));
if(!method_exists(self::class, $methodName)) throw new \InvalidArgumentException('Could not execute where statement since there is no method called "'.$methodName.'". This getter is used for the where statement.');
$regionInfoValue = $regionInfo->{$methodName}();
switch ($operator) {
case '==';
case '=';
return $regionInfoValue == $value;
case '!=';
case '<>';
return $regionInfoValue != $value;
case '===';
return $regionInfoValue === $value;
case '!==';
return $regionInfoValue !== $value;
case '<';
return $regionInfoValue < $value;
case '>';
return $regionInfoValue > $value;
case '<=';
return $regionInfoValue <= $value;
case '>=';
return $regionInfoValue >= $value;
default:
return false;
}
});
return $foundRegionInfos;
}
/**
* @return string
*/
public function getDisplayName(): string
{
return $this->displayName;
}
/**
* @return string
*/
public function getNativeName(): string
{
return $this->nativeName;
}
/**
* @return string
*/
public function getCurrencyName(): string
{
return $this->currencyName;
}
/**
* @return string
*/
public function getCurrencyMinorName(): string
{
return $this->currencyMinorName;
}
/**
* @return string
*/
public function getCurrencySymbol(): string
{
return $this->currencySymbol;
}
/**
* @return string
*/
public function getISOCurrencySymbol(): string
{
return $this->ISOCurrencySymbol;
}
/**
* @return bool
*/
public function isMetric(): bool
{
return $this->isMetric;
}
/**
* @return string
*/
public function getThreeLetterISORegionName(): string
{
return $this->threeLetterISORegionName;
}
/**
* @return string
*/
public function getTwoLetterISORegionName(): string
{
return $this->twoLetterISORegionName;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/** @return NumberFormatInfo */
public function getNumberFormat(): NumberFormatInfo
{
return $this->numberFormat;
}
/**
* @return Collection
*/
public function getLanguages(): Collection
{
return $this->languages;
}
/**
* @return bool
*/
public function isNeutral(): bool
{
return $this->isNeutral;
}
/** @return bool Alias for isNeutral */
public function getNeutral(): bool { return $this->isNeutral(); }
}