code; } public function equals(self $other): bool { return $this->code === $other->code; } /** * Create a new ValueObject from an ISO 3166 Country Code * Country codes are 2 letter, uppercase strings representing a country identifier on planet earth. The given * value must also represent a country known by PHP’s intl extension. * Valid values include 'US', 'GB', 'ZA', 'FR' etc. * * @link https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes * * @param non-empty-string $code * @throws InvalidArgumentException An invalid string or an unknown country will cause an exception. * @psalm-pure */ public static function fromString(string $code): self { $code = strtoupper($code); if (! preg_match('/^[A-Z]{2}$/', $code)) { throw InvalidArgumentException::withInvalidCountryCode($code); } $displayName = Locale::getDisplayRegion('-' . $code, 'GB'); if ($displayName === '' || $displayName === 'Unknown Region') { throw InvalidArgumentException::withUnknownCountryCode($code); } return new self($code); } /** * Create a new value object from a locale string * * Given a well-formed locale, this method will extract the relevant country code and proxy to @link fromString * Valid values include: 'en_GB', 'en-GB', 'zh-Hans-CN' * * @param non-empty-string $locale * @throws InvalidArgumentException An unrecognizable locale will cause an exception. * @psalm-pure */ public static function fromLocaleString(string $locale): self { $region = Locale::getRegion($locale); /** @psalm-suppress TypeDoesNotContainNull */ if ($region === null || $region === '') { throw InvalidArgumentException::withUnrecognizableLocaleString($locale); } return self::fromString($region); } /** * Return a country code from either a string code or a locale string falling back to the system locale if null * * @link fromLocaleString * @link fromString * * @throws InvalidArgumentException When a non-empty string is provided that cannot be recognized, * an exception will be thrown. */ public static function detect(string|self|null $countryCodeOrLocale = null): self { if ($countryCodeOrLocale instanceof self) { return $countryCodeOrLocale; } if ($countryCodeOrLocale === null || $countryCodeOrLocale === '') { $countryCodeOrLocale = Locale::getDefault(); } assert($countryCodeOrLocale !== ''); $code = self::tryFromString($countryCodeOrLocale); if ($code) { return $code; } throw InvalidArgumentException::withUndetectableCountryCode($countryCodeOrLocale); } /** * Attempt to create a value object from either a country code or a locale string * * This method returns null if the input cannot be recognized as either a code or a locale. * * @link fromLocaleString * @link fromString * * @param non-empty-string $countryCodeOrLocale * @psalm-pure */ public static function tryFromString(string $countryCodeOrLocale): ?self { try { return self::fromLocaleString($countryCodeOrLocale); } catch (InvalidArgumentException) { } try { return self::fromString($countryCodeOrLocale); } catch (InvalidArgumentException) { } return null; } }