From f7224d48576ed16c095ee042861a10c4a083e2e5 Mon Sep 17 00:00:00 2001 From: Joshua Gigg Date: Mon, 19 May 2014 13:55:09 +0100 Subject: [PATCH] Add MetadataLoader changes from Google r656 Note: This does not add the newInstance/setInstance changes from upstream. --- .../Tests/core/PhoneNumberUtilTest.php | 6 +- src/libphonenumber/DefaultMetadataLoader.php | 14 ++ src/libphonenumber/MetadataLoader.php | 15 ++ src/libphonenumber/PhoneNumber.php | 2 +- src/libphonenumber/PhoneNumberUtil.php | 151 ++++++++++-------- 5 files changed, 117 insertions(+), 71 deletions(-) create mode 100644 src/libphonenumber/DefaultMetadataLoader.php create mode 100644 src/libphonenumber/MetadataLoader.php diff --git a/Tests/libphonenumber/Tests/core/PhoneNumberUtilTest.php b/Tests/libphonenumber/Tests/core/PhoneNumberUtilTest.php index 87ef68ed8..31016593a 100644 --- a/Tests/libphonenumber/Tests/core/PhoneNumberUtilTest.php +++ b/Tests/libphonenumber/Tests/core/PhoneNumberUtilTest.php @@ -4,6 +4,7 @@ use libphonenumber\CountryCodeSource; use libphonenumber\CountryCodeToRegionCodeMapForTesting; +use libphonenumber\DefaultMetadataLoader; use libphonenumber\MatchType; use libphonenumber\NumberFormat; use libphonenumber\NumberParseException; @@ -158,7 +159,7 @@ public function testMissingMetadataFileThrowsRuntimeException() // do is make sure the exception has the file name in it. try { - $this->phoneUtil->loadMetadataFromFile("no/such/file", "XX", -1); + $this->phoneUtil->loadMetadataFromFile("no/such/file", "XX", -1, new DefaultMetadataLoader()); $this->fail("Expected Exception"); } catch (\RuntimeException $e) { $this->assertContains('no/such/file_XX', $e->getMessage(), "Unexpected error: " . $e->getMessage()); @@ -168,7 +169,8 @@ public function testMissingMetadataFileThrowsRuntimeException() $this->phoneUtil->loadMetadataFromFile( "no/such/file", PhoneNumberUtil::REGION_CODE_FOR_NON_GEO_ENTITY, - 123 + 123, + new DefaultMetadataLoader() ); $this->fail("Expected Exception"); } catch (\RuntimeException $e) { diff --git a/src/libphonenumber/DefaultMetadataLoader.php b/src/libphonenumber/DefaultMetadataLoader.php new file mode 100644 index 000000000..ade7b5744 --- /dev/null +++ b/src/libphonenumber/DefaultMetadataLoader.php @@ -0,0 +1,14 @@ +hasCountryCode()) { $this->setCountryCode($other->getCountryCode()); diff --git a/src/libphonenumber/PhoneNumberUtil.php b/src/libphonenumber/PhoneNumberUtil.php index ba0ae8891..6ce3116f1 100644 --- a/src/libphonenumber/PhoneNumberUtil.php +++ b/src/libphonenumber/PhoneNumberUtil.php @@ -16,6 +16,7 @@ * * @author Shaopeng Jia * @author Lara Rennie + * @see https://code.google.com/p/libphonenumber/ */ class PhoneNumberUtil { @@ -206,6 +207,13 @@ class PhoneNumberUtil */ private static $VALID_PHONE_NUMBER; private static $numericCharacters = array(); + + /** + * The metadata loader used to inject alternative metadata sources. + * @var MetadataLoader + */ + private $metadataLoader; + /** * A mapping from a region code to the PhoneMetadata for that region. * @var array @@ -251,9 +259,62 @@ class PhoneNumberUtil /** * This class implements a singleton, so the only constructor is private. */ - private function __construct() - { - + private function __construct($filePrefix, MetadataLoader $metadataLoader, $countryCallingCodeToRegionCodeMap) + { + $this->metadataLoader = $metadataLoader; + $this->countryCallingCodeToRegionCodeMap = $countryCallingCodeToRegionCodeMap; + $this->init($filePrefix); + self::initCapturingExtnDigits(); + self::initExtnPatterns(); + self::initAsciiDigitMappings(); + self::initExtnPattern(); + self::$PLUS_CHARS_PATTERN = "[" . self::PLUS_CHARS . "]+"; + self::$SEPARATOR_PATTERN = "[" . self::VALID_PUNCTUATION . "]+"; + self::$CAPTURING_DIGIT_PATTERN = "(" . self::DIGITS . ")"; + self::$VALID_START_CHAR_PATTERN = "[" . self::PLUS_CHARS . self::DIGITS . "]"; + + self::$ALPHA_PHONE_MAPPINGS = self::$ALPHA_MAPPINGS + self::$asciiDigitMappings; + + self::$DIALLABLE_CHAR_MAPPINGS = self::$asciiDigitMappings; + self::$DIALLABLE_CHAR_MAPPINGS[self::PLUS_SIGN] = self::PLUS_SIGN; + self::$DIALLABLE_CHAR_MAPPINGS['*'] = '*'; + + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS = array(); + // Put (lower letter -> upper letter) and (upper letter -> upper letter) mappings. + foreach (self::$ALPHA_MAPPINGS as $c => $value) { + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS[strtolower($c)] = $c; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS[$c] = $c; + } + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS += self::$asciiDigitMappings; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["-"] = '-'; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xEF\xBC\x8D"] = '-'; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x80\x90"] = '-'; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x80\x91"] = '-'; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x80\x92"] = '-'; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x80\x93"] = '-'; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x80\x94"] = '-'; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x80\x95"] = '-'; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x88\x92"] = '-'; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["/"] = "/"; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xEF\xBC\x8F"] = "/"; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS[" "] = " "; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE3\x80\x80"] = " "; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x81\xA0"] = " "; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["."] = "."; + self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xEF\xBC\x8E"] = "."; + + + self::$MIN_LENGTH_PHONE_NUMBER_PATTERN = "[" . self::DIGITS . "]{" . self::MIN_LENGTH_FOR_NSN . "}"; + self::$VALID_PHONE_NUMBER = "[" . self::PLUS_CHARS . "]*(?:[" . self::VALID_PUNCTUATION . self::STAR_SIGN . "]*[" . self::DIGITS . "]){3,}[" . self::VALID_PUNCTUATION . self::STAR_SIGN . self::VALID_ALPHA . self::DIGITS . "]*"; + self::$VALID_PHONE_NUMBER_PATTERN = "%^" . self::$MIN_LENGTH_PHONE_NUMBER_PATTERN . "$|^" . self::$VALID_PHONE_NUMBER . "(?:" . self::$EXTN_PATTERNS_FOR_PARSING . ")?%" . self::REGEX_FLAGS; + + self::$UNWANTED_END_CHAR_PATTERN = "[^" . self::DIGITS . self::VALID_ALPHA . "#]+$"; + + self::$MOBILE_TOKEN_MAPPINGS = array(); + self::$MOBILE_TOKEN_MAPPINGS['52'] = "1"; + self::$MOBILE_TOKEN_MAPPINGS['54'] = "9"; + + self::loadNumericCharacters(); } /** @@ -266,68 +327,21 @@ private function __construct() * * @param string $baseFileLocation * @param array|null $countryCallingCodeToRegionCodeMap + * @param MetadataLoader $metadataLoader * @return PhoneNumberUtil instance */ - public static function getInstance($baseFileLocation = self::META_DATA_FILE_PREFIX, array $countryCallingCodeToRegionCodeMap = null) + public static function getInstance($baseFileLocation = self::META_DATA_FILE_PREFIX, array $countryCallingCodeToRegionCodeMap = null, MetadataLoader $metadataLoader = null) { - if ($countryCallingCodeToRegionCodeMap === null) { - $countryCallingCodeToRegionCodeMap = CountryCodeToRegionCodeMap::$countryCodeToRegionCodeMap; - } if (self::$instance === null) { - self::$instance = new PhoneNumberUtil(); - self::$instance->countryCallingCodeToRegionCodeMap = $countryCallingCodeToRegionCodeMap; - self::$instance->init($baseFileLocation); - self::initCapturingExtnDigits(); - self::initExtnPatterns(); - self::initAsciiDigitMappings(); - self::initExtnPattern(); - self::$PLUS_CHARS_PATTERN = "[" . self::PLUS_CHARS . "]+"; - self::$SEPARATOR_PATTERN = "[" . self::VALID_PUNCTUATION . "]+"; - self::$CAPTURING_DIGIT_PATTERN = "(" . self::DIGITS . ")"; - self::$VALID_START_CHAR_PATTERN = "[" . self::PLUS_CHARS . self::DIGITS . "]"; - - self::$ALPHA_PHONE_MAPPINGS = self::$ALPHA_MAPPINGS + self::$asciiDigitMappings; - - self::$DIALLABLE_CHAR_MAPPINGS = self::$asciiDigitMappings; - self::$DIALLABLE_CHAR_MAPPINGS[self::PLUS_SIGN] = self::PLUS_SIGN; - self::$DIALLABLE_CHAR_MAPPINGS['*'] = '*'; - - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS = array(); - // Put (lower letter -> upper letter) and (upper letter -> upper letter) mappings. - foreach (self::$ALPHA_MAPPINGS as $c => $value) { - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS[strtolower($c)] = $c; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS[$c] = $c; + if ($countryCallingCodeToRegionCodeMap === null) { + $countryCallingCodeToRegionCodeMap = CountryCodeToRegionCodeMap::$countryCodeToRegionCodeMap; + } + + if ($metadataLoader === null) { + $metadataLoader = new DefaultMetadataLoader(); } - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS += self::$asciiDigitMappings; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["-"] = '-'; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xEF\xBC\x8D"] = '-'; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x80\x90"] = '-'; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x80\x91"] = '-'; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x80\x92"] = '-'; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x80\x93"] = '-'; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x80\x94"] = '-'; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x80\x95"] = '-'; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x88\x92"] = '-'; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["/"] = "/"; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xEF\xBC\x8F"] = "/"; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS[" "] = " "; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE3\x80\x80"] = " "; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xE2\x81\xA0"] = " "; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["."] = "."; - self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS["\xEF\xBC\x8E"] = "."; - - - self::$MIN_LENGTH_PHONE_NUMBER_PATTERN = "[" . self::DIGITS . "]{" . self::MIN_LENGTH_FOR_NSN . "}"; - self::$VALID_PHONE_NUMBER = "[" . self::PLUS_CHARS . "]*(?:[" . self::VALID_PUNCTUATION . self::STAR_SIGN . "]*[" . self::DIGITS . "]){3,}[" . self::VALID_PUNCTUATION . self::STAR_SIGN . self::VALID_ALPHA . self::DIGITS . "]*"; - self::$VALID_PHONE_NUMBER_PATTERN = "%^" . self::$MIN_LENGTH_PHONE_NUMBER_PATTERN . "$|^" . self::$VALID_PHONE_NUMBER . "(?:" . self::$EXTN_PATTERNS_FOR_PARSING . ")?%" . self::REGEX_FLAGS; - - self::$UNWANTED_END_CHAR_PATTERN = "[^" . self::DIGITS . self::VALID_ALPHA . "#]+$"; - - self::$MOBILE_TOKEN_MAPPINGS = array(); - self::$MOBILE_TOKEN_MAPPINGS['52'] = "1"; - self::$MOBILE_TOKEN_MAPPINGS['54'] = "9"; - - self::loadNumericCharacters(); + + self::$instance = new PhoneNumberUtil($baseFileLocation, $metadataLoader, $countryCallingCodeToRegionCodeMap); } return self::$instance; } @@ -625,7 +639,7 @@ public function getMetadataForRegion($regionCode) if (!isset($this->regionToMetadataMap[$regionCode])) { // The regionCode here will be valid and won't be '001', so we don't need to worry about // what to pass in for the country calling code. - $this->loadMetadataFromFile($this->currentFilePrefix, $regionCode, 0); + $this->loadMetadataFromFile($this->currentFilePrefix, $regionCode, 0, $this->metadataLoader); } return isset($this->regionToMetadataMap[$regionCode]) ? $this->regionToMetadataMap[$regionCode] : null; } @@ -644,16 +658,17 @@ private function isValidRegionCode($regionCode) * @param string $filePrefix * @param string $regionCode * @param int $countryCallingCode + * @param MetadataLoader $metadataLoader * @throws \RuntimeException */ - public function loadMetadataFromFile($filePrefix, $regionCode, $countryCallingCode) + public function loadMetadataFromFile($filePrefix, $regionCode, $countryCallingCode, MetadataLoader $metadataLoader) { $isNonGeoRegion = self::REGION_CODE_FOR_NON_GEO_ENTITY === $regionCode; $fileName = $filePrefix . '_' . ($isNonGeoRegion ? $countryCallingCode : $regionCode) . '.php'; if (!is_readable($fileName)) { throw new \RuntimeException('missing metadata: ' . $fileName); } else { - $data = include $fileName; + $data = $metadataLoader->loadMetadata($fileName); $metadata = new PhoneMetadata(); $metadata->fromArray($data); if ($isNonGeoRegion) { @@ -836,10 +851,10 @@ public function isNumberPossibleForDesc($nationalNumber, PhoneNumberDesc $number * Tests whether a phone number has a geographical association. It checks if the number is * associated to a certain region in the country where it belongs to. Note that this doesn't * verify if the number is actually in use. - * @param string $phoneNumber + * @param PhoneNumber $phoneNumber * @return bool */ - public function isNumberGeographical($phoneNumber) + public function isNumberGeographical(PhoneNumber $phoneNumber) { $numberType = $this->getNumberType($phoneNumber); // TODO: Include mobile phone numbers from countries like Indonesia, which has some @@ -887,7 +902,8 @@ public function getMetadataForNonGeographicalRegion($countryCallingCode) $this->loadMetadataFromFile( $this->currentFilePrefix, self::REGION_CODE_FOR_NON_GEO_ENTITY, - $countryCallingCode + $countryCallingCode, + $this->metadataLoader ); } return $this->countryCodeToNonGeographicalMetadataMap[$countryCallingCode]; @@ -1399,7 +1415,6 @@ private function parseHelper($numberToParse, $defaultRegion, $keepRawInput, $che // Check to see if the number is given in international format so we know whether this number is // from the default region or not. $normalizedNationalNumber = ""; - $countryCode = 0; try { // TODO: This method should really just take in the string buffer that has already // been created, and just remove the prefix, rather than taking in a string and then @@ -3185,4 +3200,4 @@ public function truncateTooLongNumber(PhoneNumber $number) } } -/* EOF */ +/* EOF */ \ No newline at end of file