Updates
This commit is contained in:
		
							
								
								
									
										26
									
								
								vendor/giggsey/libphonenumber-for-php/build/libphonenumber/buildtools/BuildApplication.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/giggsey/libphonenumber-for-php/build/libphonenumber/buildtools/BuildApplication.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| <?php | ||||
|  | ||||
| namespace libphonenumber\buildtools; | ||||
|  | ||||
| use libphonenumber\buildtools\Commands\BuildMetadataPHPFromXMLCommand; | ||||
| use libphonenumber\buildtools\Commands\GeneratePhonePrefixDataCommand; | ||||
| use libphonenumber\buildtools\Commands\GenerateTimeZonesMapDataCommand; | ||||
| use Symfony\Component\Console\Application; | ||||
|  | ||||
| class BuildApplication extends Application | ||||
| { | ||||
|     const VERSION = '5'; | ||||
|  | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct('libphonenumber Data Builder', self::VERSION); | ||||
|  | ||||
|         $this->addCommands( | ||||
|             array( | ||||
|                 new BuildMetadataPHPFromXMLCommand(), | ||||
|                 new GeneratePhonePrefixDataCommand(), | ||||
|                 new GenerateTimeZonesMapDataCommand(), | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										798
									
								
								vendor/giggsey/libphonenumber-for-php/build/libphonenumber/buildtools/BuildMetadataFromXml.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										798
									
								
								vendor/giggsey/libphonenumber-for-php/build/libphonenumber/buildtools/BuildMetadataFromXml.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,798 @@ | ||||
| <?php | ||||
|  | ||||
| namespace libphonenumber\buildtools; | ||||
|  | ||||
| use libphonenumber\NumberFormat; | ||||
| use libphonenumber\PhoneMetadata; | ||||
| use libphonenumber\PhoneNumberDesc; | ||||
|  | ||||
| /** | ||||
|  * Library to build phone number metadata from the XML format. | ||||
|  * | ||||
|  * @author Davide Mendolia | ||||
|  */ | ||||
| class BuildMetadataFromXml | ||||
| { | ||||
|     // String constants used to fetch the XML nodes and attributes. | ||||
|     const CARRIER_CODE_FORMATTING_RULE = "carrierCodeFormattingRule"; | ||||
|     const COUNTRY_CODE = "countryCode"; | ||||
|     const EMERGENCY = "emergency"; | ||||
|     const EXAMPLE_NUMBER = "exampleNumber"; | ||||
|     const FIXED_LINE = "fixedLine"; | ||||
|     const FORMAT = "format"; | ||||
|     const GENERAL_DESC = "generalDesc"; | ||||
|     const INTERNATIONAL_PREFIX = "internationalPrefix"; | ||||
|     const INTL_FORMAT = "intlFormat"; | ||||
|     const LEADING_DIGITS = "leadingDigits"; | ||||
|     const LEADING_ZERO_POSSIBLE = "leadingZeroPossible"; | ||||
|     const MOBILE_NUMBER_PORTABLE_REGION = "mobileNumberPortableRegion"; | ||||
|     const MAIN_COUNTRY_FOR_CODE = "mainCountryForCode"; | ||||
|     const MOBILE = "mobile"; | ||||
|     const NATIONAL_NUMBER_PATTERN = "nationalNumberPattern"; | ||||
|     const NATIONAL_PREFIX = "nationalPrefix"; | ||||
|     const NATIONAL_PREFIX_FORMATTING_RULE = "nationalPrefixFormattingRule"; | ||||
|     const NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING = "nationalPrefixOptionalWhenFormatting"; | ||||
|     const NATIONAL_PREFIX_FOR_PARSING = "nationalPrefixForParsing"; | ||||
|     const NATIONAL_PREFIX_TRANSFORM_RULE = "nationalPrefixTransformRule"; | ||||
|     const NO_INTERNATIONAL_DIALLING = "noInternationalDialling"; | ||||
|     const NUMBER_FORMAT = "numberFormat"; | ||||
|     const PAGER = "pager"; | ||||
|     const CARRIER_SPECIFIC = 'carrierSpecific'; | ||||
|     const PATTERN = "pattern"; | ||||
|     const PERSONAL_NUMBER = "personalNumber"; | ||||
|     const POSSIBLE_NUMBER_PATTERN = "possibleNumberPattern"; | ||||
|     const POSSIBLE_LENGTHS = "possibleLengths"; | ||||
|     const NATIONAL = "national"; | ||||
|     const LOCAL_ONLY = "localOnly"; | ||||
|     const PREFERRED_EXTN_PREFIX = "preferredExtnPrefix"; | ||||
|     const PREFERRED_INTERNATIONAL_PREFIX = "preferredInternationalPrefix"; | ||||
|     const PREMIUM_RATE = "premiumRate"; | ||||
|     const SHARED_COST = "sharedCost"; | ||||
|     const SHORT_CODE = "shortCode"; | ||||
|     const STANDARD_RATE = "standardRate"; | ||||
|     const TOLL_FREE = "tollFree"; | ||||
|     const UAN = "uan"; | ||||
|     const VOICEMAIL = "voicemail"; | ||||
|     const VOIP = "voip"; | ||||
|  | ||||
|     private static $phoneNumberDescsWithoutMatchingTypes = array( | ||||
|         self::NO_INTERNATIONAL_DIALLING | ||||
|     ); | ||||
|  | ||||
|     /** | ||||
|      * @internal | ||||
|      * @param $regex | ||||
|      * @param bool $removeWhitespace | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function validateRE($regex, $removeWhitespace = false) | ||||
|     { | ||||
|         $compressedRegex = $removeWhitespace ? preg_replace('/\\s/', '', $regex) : $regex; | ||||
|         // Match regex against an empty string to check the regex is valid | ||||
|         if (preg_match('/' . $compressedRegex . '/', '') === false) { | ||||
|             throw new \RuntimeException("Regex error: " . preg_last_error()); | ||||
|         } | ||||
|         // We don't ever expect to see | followed by a ) in our metadata - this would be an indication | ||||
|         // of a bug. If one wants to make something optional, we prefer ? to using an empty group. | ||||
|         $errorIndex = strpos($compressedRegex, '|)'); | ||||
|         if ($errorIndex !== false) { | ||||
|             throw new \RuntimeException("| followed by )"); | ||||
|         } | ||||
|         // return the regex if it is of correct syntax, i.e. compile did not fail with a | ||||
|         return $compressedRegex; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @param string $inputXmlFile | ||||
|      * @param boolean $liteBuild | ||||
|      * @return PhoneMetadata[] | ||||
|      */ | ||||
|     public static function buildPhoneMetadataCollection($inputXmlFile, $liteBuild) | ||||
|     { | ||||
|         $document = new \DOMDocument(); | ||||
|         $document->load($inputXmlFile); | ||||
|         $document->normalizeDocument(); | ||||
|         $territories = $document->getElementsByTagName("territory"); | ||||
|         $metadataCollection = array(); | ||||
|  | ||||
|         $isShortNumberMetadata = strpos($inputXmlFile, 'ShortNumberMetadata'); | ||||
|         $isAlternateFormatsMetadata = strpos($inputXmlFile, 'PhoneNumberAlternateFormats'); | ||||
|  | ||||
|         foreach ($territories as $territoryElement) { | ||||
|             /** @var $territoryElement \DOMElement */ | ||||
|             // For the main metadata file this should always be set, but for other supplementary data | ||||
|             // files the country calling code may be all that is needed. | ||||
|             if ($territoryElement->hasAttribute("id")) { | ||||
|                 $regionCode = $territoryElement->getAttribute("id"); | ||||
|             } else { | ||||
|                 $regionCode = ""; | ||||
|             } | ||||
|             $metadata = self::loadCountryMetadata($regionCode, $territoryElement, $liteBuild, $isShortNumberMetadata, $isAlternateFormatsMetadata); | ||||
|             $metadataCollection[] = $metadata; | ||||
|         } | ||||
|         return $metadataCollection; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $regionCode | ||||
|      * @param \DOMElement $element | ||||
|      * @param string $liteBuild | ||||
|      * @param string $isShortNumberMetadata | ||||
|      * @param string $isAlternateFormatsMetadata | ||||
|      * @return PhoneMetadata | ||||
|      */ | ||||
|     public static function loadCountryMetadata($regionCode, \DOMElement $element, $liteBuild, $isShortNumberMetadata, $isAlternateFormatsMetadata) | ||||
|     { | ||||
|         $nationalPrefix = self::getNationalPrefix($element); | ||||
|         $metadata = self::loadTerritoryTagMetadata($regionCode, $element, $nationalPrefix); | ||||
|         $nationalPrefixFormattingRule = self::getNationalPrefixFormattingRuleFromElement($element, $nationalPrefix); | ||||
|  | ||||
|         self::loadAvailableFormats($metadata, $element, $nationalPrefix, $nationalPrefixFormattingRule, $element->hasAttribute(self::NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING)); | ||||
|         if (!$isAlternateFormatsMetadata) { | ||||
|             // The alternate formats metadata does not need most of the patterns to be set. | ||||
|             self::setRelevantDescPatterns($metadata, $element, $liteBuild, $isShortNumberMetadata); | ||||
|         } | ||||
|         return $metadata; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the national prefix of the provided country element. | ||||
|      * @internal | ||||
|      * @param \DOMElement $element | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function getNationalPrefix(\DOMElement $element) | ||||
|     { | ||||
|         return $element->hasAttribute(self::NATIONAL_PREFIX) ? $element->getAttribute(self::NATIONAL_PREFIX) : ""; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @internal | ||||
|      * @param \DOMElement $element | ||||
|      * @param string $nationalPrefix | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function getNationalPrefixFormattingRuleFromElement(\DOMElement $element, $nationalPrefix) | ||||
|     { | ||||
|         $nationalPrefixFormattingRule = $element->getAttribute(self::NATIONAL_PREFIX_FORMATTING_RULE); | ||||
| // Replace $NP with national prefix and $FG with the first group ($1). | ||||
|         $nationalPrefixFormattingRule = str_replace('$NP', $nationalPrefix, $nationalPrefixFormattingRule); | ||||
|         $nationalPrefixFormattingRule = str_replace('$FG', '$1', $nationalPrefixFormattingRule); | ||||
|         return $nationalPrefixFormattingRule; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @internal | ||||
|      * @param string $regionCode | ||||
|      * @param \DOMElement $element | ||||
|      * @param string $nationalPrefix | ||||
|      * @return PhoneMetadata | ||||
|      */ | ||||
|     public static function loadTerritoryTagMetadata( | ||||
|         $regionCode, | ||||
|         \DOMElement $element, | ||||
|         $nationalPrefix | ||||
|     ) { | ||||
|         $metadata = new PhoneMetadata(); | ||||
|         $metadata->setId($regionCode); | ||||
|         $metadata->setCountryCode((int)$element->getAttribute(self::COUNTRY_CODE)); | ||||
|         if ($element->hasAttribute(self::LEADING_DIGITS)) { | ||||
|             $metadata->setLeadingDigits(self::validateRE($element->getAttribute(self::LEADING_DIGITS))); | ||||
|         } | ||||
|         $metadata->setInternationalPrefix(self::validateRE($element->getAttribute(self::INTERNATIONAL_PREFIX))); | ||||
|         if ($element->hasAttribute(self::PREFERRED_INTERNATIONAL_PREFIX)) { | ||||
|             $preferredInternationalPrefix = $element->getAttribute(self::PREFERRED_INTERNATIONAL_PREFIX); | ||||
|             $metadata->setPreferredInternationalPrefix($preferredInternationalPrefix); | ||||
|         } | ||||
|         if ($element->hasAttribute(self::NATIONAL_PREFIX_FOR_PARSING)) { | ||||
|             $metadata->setNationalPrefixForParsing( | ||||
|                 self::validateRE($element->getAttribute(self::NATIONAL_PREFIX_FOR_PARSING), true) | ||||
|             ); | ||||
|             if ($element->hasAttribute(self::NATIONAL_PREFIX_TRANSFORM_RULE)) { | ||||
|                 $metadata->setNationalPrefixTransformRule(self::validateRE($element->getAttribute(self::NATIONAL_PREFIX_TRANSFORM_RULE))); | ||||
|             } | ||||
|         } | ||||
|         if ($nationalPrefix != '') { | ||||
|             $metadata->setNationalPrefix($nationalPrefix); | ||||
|             if (!$metadata->hasNationalPrefixForParsing()) { | ||||
|                 $metadata->setNationalPrefixForParsing($nationalPrefix); | ||||
|             } | ||||
|         } | ||||
|         if ($element->hasAttribute(self::PREFERRED_EXTN_PREFIX)) { | ||||
|             $metadata->setPreferredExtnPrefix($element->getAttribute(self::PREFERRED_EXTN_PREFIX)); | ||||
|         } | ||||
|         if ($element->hasAttribute(self::MAIN_COUNTRY_FOR_CODE)) { | ||||
|             $metadata->setMainCountryForCode(true); | ||||
|         } | ||||
|         if ($element->hasAttribute(self::LEADING_ZERO_POSSIBLE)) { | ||||
|             $metadata->setLeadingZeroPossible(true); | ||||
|         } | ||||
|         if ($element->hasAttribute(self::MOBILE_NUMBER_PORTABLE_REGION)) { | ||||
|             $metadata->setMobileNumberPortableRegion(true); | ||||
|         } | ||||
|         return $metadata; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Extracts the available formats from the provided DOM element. If it does not contain any | ||||
|      * nationalPrefixFormattingRule, the one passed-in is retained; similarly for | ||||
|      * nationalPrefixOptionalWhenFormatting. The nationalPrefix, nationalPrefixFormattingRule and | ||||
|      * nationalPrefixOptionalWhenFormatting values are provided from the parent (territory) element. | ||||
|      * @internal | ||||
|      * @param PhoneMetadata $metadata | ||||
|      * @param \DOMElement $element | ||||
|      * @param string $nationalPrefix | ||||
|      * @param string $nationalPrefixFormattingRule | ||||
|      * @param bool $nationalPrefixOptionalWhenFormatting | ||||
|      */ | ||||
|     public static function loadAvailableFormats( | ||||
|         PhoneMetadata $metadata, | ||||
|         \DOMElement $element, | ||||
|         $nationalPrefix, | ||||
|         $nationalPrefixFormattingRule, | ||||
|         $nationalPrefixOptionalWhenFormatting | ||||
|     ) { | ||||
|         $carrierCodeFormattingRule = ""; | ||||
|         if ($element->hasAttribute(self::CARRIER_CODE_FORMATTING_RULE)) { | ||||
|             $carrierCodeFormattingRule = self::validateRE(self::getDomesticCarrierCodeFormattingRuleFromElement($element, $nationalPrefix)); | ||||
|         } | ||||
|         $numberFormatElements = $element->getElementsByTagName(self::NUMBER_FORMAT); | ||||
|         $hasExplicitIntlFormatDefined = false; | ||||
|  | ||||
|         $numOfFormatElements = $numberFormatElements->length; | ||||
|         if ($numOfFormatElements > 0) { | ||||
|             for ($i = 0; $i < $numOfFormatElements; $i++) { | ||||
|                 /** @var \DOMElement $numberFormatElement */ | ||||
|                 $numberFormatElement = $numberFormatElements->item($i); | ||||
|                 $format = new NumberFormat(); | ||||
|  | ||||
|                 if ($numberFormatElement->hasAttribute(self::NATIONAL_PREFIX_FORMATTING_RULE)) { | ||||
|                     $format->setNationalPrefixFormattingRule( | ||||
|                         self::getNationalPrefixFormattingRuleFromElement($numberFormatElement, $nationalPrefix) | ||||
|                     ); | ||||
|                 } else { | ||||
|                     $format->setNationalPrefixFormattingRule($nationalPrefixFormattingRule); | ||||
|                 } | ||||
|                 if ($numberFormatElement->hasAttribute(self::NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING)) { | ||||
|                     $format->setNationalPrefixOptionalWhenFormatting($numberFormatElement->getAttribute(self::NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING) == 'true' ? true : false); | ||||
|                 } else { | ||||
|                     $format->setNationalPrefixOptionalWhenFormatting($nationalPrefixOptionalWhenFormatting); | ||||
|                 } | ||||
|                 if ($numberFormatElement->hasAttribute(self::CARRIER_CODE_FORMATTING_RULE)) { | ||||
|                     $format->setDomesticCarrierCodeFormattingRule( | ||||
|                         self::validateRE(self::getDomesticCarrierCodeFormattingRuleFromElement($numberFormatElement, $nationalPrefix)) | ||||
|                     ); | ||||
|                 } else { | ||||
|                     $format->setDomesticCarrierCodeFormattingRule($carrierCodeFormattingRule); | ||||
|                 } | ||||
|                 self::loadNationalFormat($metadata, $numberFormatElement, $format); | ||||
|                 $metadata->addNumberFormat($format); | ||||
|  | ||||
|                 if (self::loadInternationalFormat($metadata, $numberFormatElement, $format)) { | ||||
|                     $hasExplicitIntlFormatDefined = true; | ||||
|                 } | ||||
|             } | ||||
|             // Only a small number of regions need to specify the intlFormats in the xml. For the majority | ||||
|             // of countries the intlNumberFormat metadata is an exact copy of the national NumberFormat | ||||
|             // metadata. To minimize the size of the metadata file, we only keep intlNumberFormats that | ||||
|             // actually differ in some way to the national formats. | ||||
|             if (!$hasExplicitIntlFormatDefined) { | ||||
|                 $metadata->clearIntlNumberFormat(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @internal | ||||
|      * @param \DOMElement $element | ||||
|      * @param string $nationalPrefix | ||||
|      * @return mixed|string | ||||
|      */ | ||||
|     public static function getDomesticCarrierCodeFormattingRuleFromElement(\DOMElement $element, $nationalPrefix) | ||||
|     { | ||||
|         $carrierCodeFormattingRule = $element->getAttribute(self::CARRIER_CODE_FORMATTING_RULE); | ||||
|         // Replace $FG with the first group ($1) and $NP with the national prefix. | ||||
|         $carrierCodeFormattingRule = str_replace('$NP', $nationalPrefix, $carrierCodeFormattingRule); | ||||
|         $carrierCodeFormattingRule = str_replace('$FG', '$1', $carrierCodeFormattingRule); | ||||
|         return $carrierCodeFormattingRule; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Extracts the pattern for the national format. | ||||
|      * | ||||
|      * @internal | ||||
|      * @param PhoneMetadata $metadata | ||||
|      * @param \DOMElement $numberFormatElement | ||||
|      * @param NumberFormat $format | ||||
|      * @throws \RuntimeException if multiple or no formats have been encountered. | ||||
|      */ | ||||
|     public static function loadNationalFormat( | ||||
|         PhoneMetadata $metadata, | ||||
|         \DOMElement $numberFormatElement, | ||||
|         NumberFormat $format | ||||
|     ) { | ||||
|         self::setLeadingDigitsPatterns($numberFormatElement, $format); | ||||
|         $format->setPattern(self::validateRE($numberFormatElement->getAttribute(self::PATTERN))); | ||||
|  | ||||
|         $formatPattern = $numberFormatElement->getElementsByTagName(self::FORMAT); | ||||
|         if ($formatPattern->length != 1) { | ||||
|             $countryId = strlen($metadata->getId()) > 0 ? $metadata->getId() : $metadata->getCountryCode(); | ||||
|             throw new \RuntimeException("Invalid number of format patterns for country: " . $countryId); | ||||
|         } | ||||
|         $nationalFormat = $formatPattern->item(0)->firstChild->nodeValue; | ||||
|         $format->setFormat($nationalFormat); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @internal | ||||
|      * @param \DOMElement $numberFormatElement | ||||
|      * @param NumberFormat $format | ||||
|      */ | ||||
|     public static function setLeadingDigitsPatterns(\DOMElement $numberFormatElement, NumberFormat $format) | ||||
|     { | ||||
|         $leadingDigitsPatternNodes = $numberFormatElement->getElementsByTagName(self::LEADING_DIGITS); | ||||
|         $numOfLeadingDigitsPatterns = $leadingDigitsPatternNodes->length; | ||||
|         if ($numOfLeadingDigitsPatterns > 0) { | ||||
|             for ($i = 0; $i < $numOfLeadingDigitsPatterns; $i++) { | ||||
|                 $format->addLeadingDigitsPattern(self::validateRE($leadingDigitsPatternNodes->item($i)->firstChild->nodeValue, true)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Extracts the pattern for international format. If there is no intlFormat, default to using the | ||||
|      * national format. If the intlFormat is set to "NA" the intlFormat should be ignored. | ||||
|      * | ||||
|      * @internal | ||||
|      * @param PhoneMetadata $metadata | ||||
|      * @param \DOMElement $numberFormatElement | ||||
|      * @param NumberFormat $nationalFormat | ||||
|      * @throws \RuntimeException if multiple intlFormats have been encountered. | ||||
|      * @return bool whether an international number format is defined. | ||||
|      */ | ||||
|     public static function loadInternationalFormat( | ||||
|         PhoneMetadata $metadata, | ||||
|         \DOMElement $numberFormatElement, | ||||
|         NumberFormat $nationalFormat | ||||
|     ) { | ||||
|         $intlFormat = new NumberFormat(); | ||||
|         $intlFormatPattern = $numberFormatElement->getElementsByTagName(self::INTL_FORMAT); | ||||
|         $hasExplicitIntlFormatDefined = false; | ||||
|  | ||||
|         if ($intlFormatPattern->length > 1) { | ||||
|             $countryId = strlen($metadata->getId()) > 0 ? $metadata->getId() : $metadata->getCountryCode(); | ||||
|             throw new \RuntimeException("Invalid number of intlFormat patterns for country: " . $countryId); | ||||
|         } elseif ($intlFormatPattern->length == 0) { | ||||
|             // Default to use the same as the national pattern if none is defined. | ||||
|             $intlFormat->mergeFrom($nationalFormat); | ||||
|         } else { | ||||
|             $intlFormat->setPattern($numberFormatElement->getAttribute(self::PATTERN)); | ||||
|             self::setLeadingDigitsPatterns($numberFormatElement, $intlFormat); | ||||
|             $intlFormatPatternValue = $intlFormatPattern->item(0)->firstChild->nodeValue; | ||||
|             if ($intlFormatPatternValue !== "NA") { | ||||
|                 $intlFormat->setFormat($intlFormatPatternValue); | ||||
|             } | ||||
|             $hasExplicitIntlFormatDefined = true; | ||||
|         } | ||||
|  | ||||
|         if ($intlFormat->hasFormat()) { | ||||
|             $metadata->addIntlNumberFormat($intlFormat); | ||||
|         } | ||||
|         return $hasExplicitIntlFormatDefined; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @internal | ||||
|      * @param PhoneMetadata $metadata | ||||
|      * @param \DOMElement $element | ||||
|      * @param bool $liteBuild | ||||
|      * @param bool $isShortNumberMetadata | ||||
|      */ | ||||
|     public static function setRelevantDescPatterns(PhoneMetadata $metadata, \DOMElement $element, $liteBuild, $isShortNumberMetadata) | ||||
|     { | ||||
|         $generalDesc = self::processPhoneNumberDescElement(null, $element, self::GENERAL_DESC, $liteBuild); | ||||
|         $metadata->setGeneralDesc($generalDesc); | ||||
|  | ||||
|         $metadataId = $metadata->getId(); | ||||
|         // Calculate the possible lengths for the general description. This will be based on the | ||||
|         // possible lengths of the child elements. | ||||
|         self::setPossibleLengthsGeneralDesc($generalDesc, $metadataId, $element, $isShortNumberMetadata); | ||||
|  | ||||
|         if (!$isShortNumberMetadata) { | ||||
|             // Set fields used by regular length phone numbers. | ||||
|             $metadata->setFixedLine(self::processPhoneNumberDescElement($generalDesc, $element, self::FIXED_LINE, $liteBuild)); | ||||
|             $metadata->setMobile(self::processPhoneNumberDescElement($generalDesc, $element, self::MOBILE, $liteBuild)); | ||||
|             $metadata->setSharedCost(self::processPhoneNumberDescElement($generalDesc, $element, self::SHARED_COST, $liteBuild)); | ||||
|             $metadata->setVoip(self::processPhoneNumberDescElement($generalDesc, $element, self::VOIP, $liteBuild)); | ||||
|             $metadata->setPersonalNumber(self::processPhoneNumberDescElement($generalDesc, $element, self::PERSONAL_NUMBER, $liteBuild)); | ||||
|             $metadata->setPager(self::processPhoneNumberDescElement($generalDesc, $element, self::PAGER, $liteBuild)); | ||||
|             $metadata->setUan(self::processPhoneNumberDescElement($generalDesc, $element, self::UAN, $liteBuild)); | ||||
|             $metadata->setVoicemail(self::processPhoneNumberDescElement($generalDesc, $element, self::VOICEMAIL, $liteBuild)); | ||||
|             $metadata->setNoInternationalDialling(self::processPhoneNumberDescElement($generalDesc, $element, self::NO_INTERNATIONAL_DIALLING, $liteBuild)); | ||||
|             $metadata->setSameMobileAndFixedLinePattern($metadata->getMobile()->getNationalNumberPattern() === $metadata->getFixedLine()->getNationalNumberPattern()); | ||||
|             $metadata->setTollFree(self::processPhoneNumberDescElement($generalDesc, $element, self::TOLL_FREE, $liteBuild)); | ||||
|             $metadata->setPremiumRate(self::processPhoneNumberDescElement($generalDesc, $element, self::PREMIUM_RATE, $liteBuild)); | ||||
|         } else { | ||||
|             // Set fields used by short numbers. | ||||
|             $metadata->setStandardRate(self::processPhoneNumberDescElement($generalDesc, $element, self::STANDARD_RATE, $liteBuild)); | ||||
|             $metadata->setShortCode(self::processPhoneNumberDescElement($generalDesc, $element, self::SHORT_CODE, $liteBuild)); | ||||
|             $metadata->setCarrierSpecific(self::processPhoneNumberDescElement($generalDesc, $element, self::CARRIER_SPECIFIC, $liteBuild)); | ||||
|             $metadata->setEmergency(self::processPhoneNumberDescElement($generalDesc, $element, self::EMERGENCY, $liteBuild)); | ||||
|             $metadata->setTollFree(self::processPhoneNumberDescElement($generalDesc, $element, self::TOLL_FREE, $liteBuild)); | ||||
|             $metadata->setPremiumRate(self::processPhoneNumberDescElement($generalDesc, $element, self::PREMIUM_RATE, $liteBuild)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Parses a possible length string into a set of the integers that are covered. | ||||
|      * | ||||
|      * @param string $possibleLengthString a string specifying the possible lengths of phone numbers. Follows | ||||
|      * this syntax: ranges or elements are separated by commas, and ranges are specified in | ||||
|      * [min-max] notation, inclusive. For example, [3-5],7,9,[11-14] should be parsed to | ||||
|      * 3,4,5,7,9,11,12,13,14 | ||||
|      * @return array | ||||
|      */ | ||||
|     private static function parsePossibleLengthStringToSet($possibleLengthString) | ||||
|     { | ||||
|         if (strlen($possibleLengthString) === 0) { | ||||
|             throw new \RuntimeException("Empty possibleLength string found."); | ||||
|         } | ||||
|  | ||||
|         $lengths = explode(",", $possibleLengthString); | ||||
|         $lengthSet = array(); | ||||
|  | ||||
|  | ||||
|         $lengthLength = count($lengths); | ||||
|         for ($i = 0; $i < $lengthLength; $i++) { | ||||
|             $lengthSubstring = $lengths[$i]; | ||||
|             if (strlen($lengthSubstring) === 0) { | ||||
|                 throw new \RuntimeException("Leading, trailing or adjacent commas in possible " | ||||
|                     . "length string {$possibleLengthString}, these should only separate numbers or ranges."); | ||||
|             } elseif (substr($lengthSubstring, 0, 1) === '[') { | ||||
|                 if (substr($lengthSubstring, -1) !== ']') { | ||||
|                     throw new \RuntimeException("Missing end of range character in possible length string {$possibleLengthString}."); | ||||
|                 } | ||||
|  | ||||
|                 // Strip the leading and trailing [], and split on the -. | ||||
|                 $minMax = explode('-', substr($lengthSubstring, 1, -1)); | ||||
|                 if (count($minMax) !== 2) { | ||||
|                     throw new \RuntimeException("Ranges must have exactly one - character: missing for {$possibleLengthString}."); | ||||
|                 } | ||||
|                 $min = (int)$minMax[0]; | ||||
|                 $max = (int)$minMax[1]; | ||||
|                 // We don't even accept [6-7] since we prefer the shorter 6,7 variant; for a range to be in | ||||
|                 // use the hyphen needs to replace at least one digit. | ||||
|                 if ($max - $min < 2) { | ||||
|                     throw new \RuntimeException("The first number in a range should be two or more digits lower than the second. Culprit possibleLength string: {$possibleLengthString}."); | ||||
|                 } | ||||
|                 for ($j = $min; $j <= $max; $j++) { | ||||
|                     if (in_array($j, $lengthSet)) { | ||||
|                         throw new \RuntimeException("Duplicate length element found ({$j}) in possibleLength string {$possibleLengthString}."); | ||||
|                     } | ||||
|                     $lengthSet[] = (int)$j; | ||||
|                 } | ||||
|             } else { | ||||
|                 $length = $lengthSubstring; | ||||
|                 if (in_array($length, $lengthSet)) { | ||||
|                     throw new \RuntimeException("Duplicate length element found ({$length}) in possibleLength string {$possibleLengthString}."); | ||||
|                 } | ||||
|                 if (!is_numeric($length)) { | ||||
|                     throw new \RuntimeException("For input string \"{$length}\""); | ||||
|                 } | ||||
|                 $lengthSet[] = (int)$length; | ||||
|             } | ||||
|         } | ||||
|         return $lengthSet; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reads the possible length present in the metadata and splits them into two sets: one for | ||||
|      * full-length numbers, one for local numbers. | ||||
|      * | ||||
|      * | ||||
|      * @param \DOMElement $data One or more phone number descriptions | ||||
|      * @param array $lengths An array in which to add possible lengths of full phone numbers | ||||
|      * @param array $localOnlyLengths An array in which to add possible lengths of phone numbers only diallable | ||||
|      *  locally (e.g. within a province) | ||||
|      */ | ||||
|     private static function populatePossibleLengthSets(\DOMElement $data, &$lengths, &$localOnlyLengths) | ||||
|     { | ||||
|         $possibleLengths = $data->getElementsByTagName(self::POSSIBLE_LENGTHS); | ||||
|  | ||||
|         for ($i = 0; $i < $possibleLengths->length; $i++) { | ||||
|             /** @var \DOMElement $element */ | ||||
|             $element = $possibleLengths->item($i); | ||||
|             $nationalLengths = $element->getAttribute(self::NATIONAL); | ||||
|             // We don't add to the phone metadata yet, since we want to sort length elements found under | ||||
|             // different nodes first, make sure there are no duplicates between them and that the | ||||
|             // localOnly lengths don't overlap with the others. | ||||
|             $thisElementLengths = self::parsePossibleLengthStringToSet($nationalLengths); | ||||
|             if ($element->hasAttribute(self::LOCAL_ONLY)) { | ||||
|                 $localLengths = $element->getAttribute(self::LOCAL_ONLY); | ||||
|                 $thisElementLocalOnlyLengths = self::parsePossibleLengthStringToSet($localLengths); | ||||
|                 $intersection = array_intersect($thisElementLengths, $thisElementLocalOnlyLengths); | ||||
|                 if (count($intersection) > 0) { | ||||
|                     throw new \RuntimeException("Possible length(s) found specified as a normal and local-only length: [" . implode(',', $intersection) . '].'); | ||||
|                 } | ||||
|                 // We check again when we set these lengths on the metadata itself in setPossibleLengths | ||||
|                 // that the elements in localOnly are not also in lengths. For e.g. the generalDesc, it | ||||
|                 // might have a local-only length for one type that is a normal length for another type. We | ||||
|                 // don't consider this an error, but we do want to remove the local-only lengths. | ||||
|                 $localOnlyLengths = array_merge($localOnlyLengths, $thisElementLocalOnlyLengths); | ||||
|                 sort($localOnlyLengths); | ||||
|             } | ||||
|             // It is okay if at this time we have duplicates, because the same length might be possible | ||||
|             // for e.g. fixed-line and for mobile numbers, and this method operates potentially on | ||||
|             // multiple phoneNumberDesc XML elements. | ||||
|             $lengths = array_merge($lengths, $thisElementLengths); | ||||
|             sort($lengths); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets possible lengths in the general description, derived from certain child elements | ||||
|      * | ||||
|      * @internal | ||||
|      * @param PhoneNumberDesc $generalDesc | ||||
|      * @param string $metadataId | ||||
|      * @param \DOMElement $data | ||||
|      * @param bool $isShortNumberMetadata | ||||
|      */ | ||||
|     public static function setPossibleLengthsGeneralDesc(PhoneNumberDesc $generalDesc, $metadataId, \DOMElement $data, $isShortNumberMetadata) | ||||
|     { | ||||
|         $lengths = array(); | ||||
|         $localOnlyLengths = array(); | ||||
|         // The general description node should *always* be present if metadata for other types is | ||||
|         // present, aside from in some unit tests. | ||||
|         // (However, for e.g. formatting metadata in PhoneNumberAlternateFormats, no PhoneNumberDesc | ||||
|         // elements are present). | ||||
|         $generalDescNodes = $data->getElementsByTagName(self::GENERAL_DESC); | ||||
|         if ($generalDescNodes->length > 0) { | ||||
|             $generalDescNode = $generalDescNodes->item(0); | ||||
|             self::populatePossibleLengthSets($generalDescNode, $lengths, $localOnlyLengths); | ||||
|             if (count($lengths) > 0 || count($localOnlyLengths) > 0) { | ||||
|                 // We shouldn't have anything specified at the "general desc" level: we are going to | ||||
|                 // calculate this ourselves from child elements. | ||||
|                 throw new \RuntimeException("Found possible lengths specified at general desc: this should be derived from child elements. Affected country: {$metadataId}"); | ||||
|             } | ||||
|         } | ||||
|         if (!$isShortNumberMetadata) { | ||||
|             // Make a copy here since we want to remove some nodes, but we don't want to do that on our | ||||
|             // actual data. | ||||
|             /** @var \DOMElement $allDescData */ | ||||
|             $allDescData = $data->cloneNode(true); | ||||
|             foreach (self::$phoneNumberDescsWithoutMatchingTypes as $tag) { | ||||
|                 $nodesToRemove = $allDescData->getElementsByTagName($tag); | ||||
|                 if ($nodesToRemove->length > 0) { | ||||
|                     // We check when we process phone number descriptions that there are only one of each | ||||
|                     // type, so this is safe to do. | ||||
|                     $allDescData->removeChild($nodesToRemove->item(0)); | ||||
|                 } | ||||
|             } | ||||
|             self::populatePossibleLengthSets($allDescData, $lengths, $localOnlyLengths); | ||||
|         } else { | ||||
|             // For short number metadata, we want to copy the lengths from the "short code" section only. | ||||
|             // This is because it's the more detailed validation pattern, it's not a sub-type of short | ||||
|             // codes. The other lengths will be checked later to see that they are a sub-set of these | ||||
|             // possible lengths. | ||||
|             $shortCodeDescList = $data->getElementsByTagName(self::SHORT_CODE); | ||||
|             if (count($shortCodeDescList) > 0) { | ||||
|                 $shortCodeDesc = $shortCodeDescList->item(0); | ||||
|                 self::populatePossibleLengthSets($shortCodeDesc, $lengths, $localOnlyLengths); | ||||
|             } | ||||
|             if (count($localOnlyLengths) > 0) { | ||||
|                 throw new \RuntimeException("Found local-only lengths in short-number metadata"); | ||||
|             } | ||||
|         } | ||||
|         self::setPossibleLengths($lengths, $localOnlyLengths, null, $generalDesc); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets the possible length fields in the metadata from the sets of data passed in. Checks that | ||||
|      * the length is covered by the "parent" phone number description element if one is present, and | ||||
|      * if the lengths are exactly the same as this, they are not filled in for efficiency reasons. | ||||
|      * | ||||
|      * @param array $lengths | ||||
|      * @param array $localOnlyLengths | ||||
|      * @param PhoneNumberDesc $parentDesc | ||||
|      * @param PhoneNumberDesc $desc | ||||
|      */ | ||||
|     private static function setPossibleLengths($lengths, $localOnlyLengths, PhoneNumberDesc $parentDesc = null, PhoneNumberDesc $desc) | ||||
|     { | ||||
|         // We clear these fields since the metadata tends to inherit from the parent element for other | ||||
|         // fields (via a mergeFrom). | ||||
|         $desc->clearPossibleLength(); | ||||
|         $desc->clearPossibleLengthLocalOnly(); | ||||
|  | ||||
|         // Only add the lengths to this sub-type if they aren't exactly the same as the possible | ||||
|         // lengths in the general desc (for metadata size reasons). | ||||
|         if ($parentDesc === null || !self::arePossibleLengthsEqual($lengths, $parentDesc)) { | ||||
|             foreach ($lengths as $length) { | ||||
|                 if ($parentDesc === null || in_array($length, $parentDesc->getPossibleLength())) { | ||||
|                     $desc->addPossibleLength($length); | ||||
|                 } else { | ||||
|                     // We shouldn't have possible lengths defined in a child element that are not covered by | ||||
|                     // the general description. We check this here even though the general description is | ||||
|                     // derived from child elements because it is only derived from a subset, and we need to | ||||
|                     // ensure *all* child elements have a valid possible length. | ||||
|                     throw new \RuntimeException("Out-of-range possible length found ({$length}), parent lengths " . implode(',', $parentDesc->getPossibleLength())); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // We check that the local-only length isn't also a normal possible length (only relevant for | ||||
|         // the general-desc, since within elements such as fixed-line we would throw an exception if we | ||||
|         // saw this) before adding it to the collection of possible local-only lengths. | ||||
|         foreach ($localOnlyLengths as $length) { | ||||
|             if (!in_array($length, $lengths)) { | ||||
|                 // We check it is covered by either of the possible length sets of the parent | ||||
|                 // PhoneNumberDesc, because for example 7 might be a valid localOnly length for mobile, but | ||||
|                 // a valid national length for fixedLine, so the generalDesc would have the 7 removed from | ||||
|                 // localOnly. | ||||
|                 if ($parentDesc === null | ||||
|                     || in_array($length, $parentDesc->getPossibleLength()) | ||||
|                     || in_array($length, $parentDesc->getPossibleLengthLocalOnly()) | ||||
|                 ) { | ||||
|                     $desc->addPossibleLengthLocalOnly($length); | ||||
|                 } else { | ||||
|                     throw new \RuntimeException("Out-of-range local-only possible length found ({$length}), parent length {$parentDesc->getPossibleLengthLocalOnly()}"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Processes a phone number description element from the XML file and returns it as a | ||||
|      * PhoneNumberDesc. If the description element is a fixed line or mobile number, the parent | ||||
|      * description will be used to fill in the whole element if necessary, or any components that are | ||||
|      * missing. For all other types, the parent description will only be used to fill in missing | ||||
|      * components if the type has a partial definition. For example, if no "tollFree" element exists, | ||||
|      * we assume there are no toll free numbers for that locale, and return a phone number description | ||||
|      * with "NA" for both the national and possible number patterns. Note that the parent description | ||||
|      * must therefore already be processed before this method is called on any child elements. | ||||
|      * | ||||
|      * @internal | ||||
|      * @param PhoneNumberDesc $parentDesc a generic phone number description that will be used to fill in missing | ||||
|      *     parts of the description, or null if this is the root node. This must be processed before | ||||
|      *     this is run on any child elements. | ||||
|      * @param \DOMElement $countryElement XML element representing all the country information | ||||
|      * @param string $numberType name of the number type, corresponding to the appropriate tag in the XML | ||||
|      * file with information about that type | ||||
|      * @param bool $liteBuild | ||||
|      * @return PhoneNumberDesc complete description of that phone number type | ||||
|      */ | ||||
|     public static function processPhoneNumberDescElement( | ||||
|         PhoneNumberDesc $parentDesc = null, | ||||
|         \DOMElement $countryElement, | ||||
|         $numberType, | ||||
|         $liteBuild | ||||
|     ) { | ||||
|         $phoneNumberDescList = $countryElement->getElementsByTagName($numberType); | ||||
|         $numberDesc = new PhoneNumberDesc(); | ||||
|         if ($phoneNumberDescList->length == 0 && !self::numberTypeShouldAlwaysBeFilledIn($numberType)) { | ||||
|             $numberDesc->setNationalNumberPattern("NA"); | ||||
|             $numberDesc->setPossibleNumberPattern("NA"); | ||||
|             // -1 will never match a possible phone number length, so is safe to use to ensure this never | ||||
|             // matches. We don't leave it empty, since for compression reasons, we use the empty list to | ||||
|             // mean that the generalDesc possible lengths apply. | ||||
|             $numberDesc->setPossibleLength(array(-1)); | ||||
|             return $numberDesc; | ||||
|         } | ||||
|  | ||||
|         if ($parentDesc != null) { | ||||
|             if ($parentDesc->getNationalNumberPattern() !== "") { | ||||
|                 $numberDesc->setNationalNumberPattern($parentDesc->getNationalNumberPattern()); | ||||
|             } | ||||
|             if ($parentDesc->getPossibleNumberPattern() !== "") { | ||||
|                 $numberDesc->setPossibleNumberPattern($parentDesc->getPossibleNumberPattern()); | ||||
|             } | ||||
|             if ($parentDesc->getExampleNumber() !== "") { | ||||
|                 $numberDesc->setExampleNumber($parentDesc->getExampleNumber()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ($phoneNumberDescList->length > 0) { | ||||
|             if ($phoneNumberDescList->length > 1) { | ||||
|                 throw new \RuntimeException("Multiple elements with type {$numberType} found."); | ||||
|             } | ||||
|  | ||||
|             /** @var \DOMElement $element */ | ||||
|             $element = $phoneNumberDescList->item(0); | ||||
|             // Old way of handling possible number lengths. This will be deleted when no data is | ||||
|             // represented in this way anymore. | ||||
|             $possiblePattern = $element->getElementsByTagName(self::POSSIBLE_NUMBER_PATTERN); | ||||
|             if ($possiblePattern->length > 0) { | ||||
|                 $numberDesc->setPossibleNumberPattern(self::validateRE($possiblePattern->item(0)->firstChild->nodeValue, true)); | ||||
|             } | ||||
|  | ||||
|             if ($parentDesc != null) { | ||||
|                 // New way of handling possible number lengths. We don't do this for the general | ||||
|                 // description, since these tags won't be present; instead we will calculate its values | ||||
|                 // based on the values for all the other number type descriptions (see | ||||
|                 // setPossibleLengthsGeneralDesc). | ||||
|                 $lengths = array(); | ||||
|                 $localOnlyLengths = array(); | ||||
|                 self::populatePossibleLengthSets($element, $lengths, $localOnlyLengths); | ||||
|                 // NOTE: We don't use the localOnlyLengths for specific number types yet, since they aren't | ||||
|                 // used in the API and won't be until a method that assesses whether a number is possible | ||||
|                 // for a certain type or not is available. To ensure size is small, we don't set them | ||||
|                 // outside the general desc at this time. If we want this data later, the empty set here | ||||
|                 // should be replaced with the localOnlyLengths set above. | ||||
|                 self::setPossibleLengths($lengths, array(), $parentDesc, $numberDesc); | ||||
|             } | ||||
|  | ||||
|             $validPattern = $element->getElementsByTagName(self::NATIONAL_NUMBER_PATTERN); | ||||
|             if ($validPattern->length > 0) { | ||||
|                 $numberDesc->setNationalNumberPattern(self::validateRE($validPattern->item(0)->firstChild->nodeValue, true)); | ||||
|             } | ||||
|  | ||||
|             if (!$liteBuild) { | ||||
|                 $exampleNumber = $element->getElementsByTagName(self::EXAMPLE_NUMBER); | ||||
|                 if ($exampleNumber->length > 0) { | ||||
|                     $numberDesc->setExampleNumber($exampleNumber->item(0)->firstChild->nodeValue); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return $numberDesc; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @internal | ||||
|      * @param string $numberType | ||||
|      * @return bool | ||||
|      */ | ||||
|     public static function numberTypeShouldAlwaysBeFilledIn($numberType) | ||||
|     { | ||||
|         return $numberType == self::FIXED_LINE || $numberType == self::MOBILE || $numberType == self::GENERAL_DESC; | ||||
|     } | ||||
|  | ||||
|     private static function arePossibleLengthsEqual($possibleLengths, PhoneNumberDesc $desc) | ||||
|     { | ||||
|         $descPossibleLength = $desc->getPossibleLength(); | ||||
|         if (count($possibleLengths) != count($descPossibleLength)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Note that both should be sorted already, and we know they are the same length. | ||||
|         $i = 0; | ||||
|         foreach ($possibleLengths as $length) { | ||||
|             if ($length != $descPossibleLength[$i]) { | ||||
|                 return false; | ||||
|             } | ||||
|             $i++; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $metadataCollection PhoneMetadata[] | ||||
|      * @return array | ||||
|      */ | ||||
|     public static function buildCountryCodeToRegionCodeMap($metadataCollection) | ||||
|     { | ||||
|         $countryCodeToRegionCodeMap = array(); | ||||
|  | ||||
|         foreach ($metadataCollection as $metadata) { | ||||
|             $regionCode = $metadata->getId(); | ||||
|             $countryCode = $metadata->getCountryCode(); | ||||
|             if (array_key_exists($countryCode, $countryCodeToRegionCodeMap)) { | ||||
|                 if ($metadata->getMainCountryForCode()) { | ||||
|                     array_unshift($countryCodeToRegionCodeMap[$countryCode], $regionCode); | ||||
|                 } else { | ||||
|                     $countryCodeToRegionCodeMap[$countryCode][] = $regionCode; | ||||
|                 } | ||||
|             } else { | ||||
|                 // For most countries, there will be only one region code for the country calling code. | ||||
|                 $listWithRegionCode = array(); | ||||
|                 if ($regionCode != '') { // For alternate formats, there are no region codes at all. | ||||
|                     $listWithRegionCode[] = $regionCode; | ||||
|                 } | ||||
|                 $countryCodeToRegionCodeMap[$countryCode] = $listWithRegionCode; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $countryCodeToRegionCodeMap; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										122
									
								
								vendor/giggsey/libphonenumber-for-php/build/libphonenumber/buildtools/BuildMetadataPHPFromXml.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								vendor/giggsey/libphonenumber-for-php/build/libphonenumber/buildtools/BuildMetadataPHPFromXml.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| <?php | ||||
| namespace libphonenumber\buildtools; | ||||
|  | ||||
| use libphonenumber\PhoneMetadata; | ||||
|  | ||||
| /** | ||||
|  * Tool to convert phone number metadata from the XML format to protocol buffer format. | ||||
|  * | ||||
|  * @author Davide Mendolia | ||||
|  */ | ||||
| class BuildMetadataPHPFromXml | ||||
| { | ||||
|     const GENERATION_COMMENT = <<<EOT | ||||
| /** | ||||
|  * This file has been @generated by a phing task by {@link BuildMetadataPHPFromXml}. | ||||
|  * See [README.md](README.md#generating-data) for more information. | ||||
|  * | ||||
|  * Pull requests changing data in these files will not be accepted. See the | ||||
|  * [FAQ in the README](README.md#problems-with-invalid-numbers] on how to make | ||||
|  * metadata changes. | ||||
|  * | ||||
|  * Do not modify this file directly! | ||||
|  */ | ||||
|  | ||||
|  | ||||
| EOT; | ||||
|     const MAP_COMMENT = <<<EOT | ||||
|   // A mapping from a country code to the region codes which denote the | ||||
|   // country/region represented by that country code. In the case of multiple | ||||
|   // countries sharing a calling code, such as the NANPA countries, the one | ||||
|   // indicated with "isMainCountryForCode" in the metadata should be first. | ||||
|  | ||||
| EOT; | ||||
|     const COUNTRY_CODE_SET_COMMENT = | ||||
|         "  // A set of all country codes for which data is available.\n"; | ||||
|     const REGION_CODE_SET_COMMENT = | ||||
|         "  // A set of all region codes for which data is available.\n"; | ||||
|  | ||||
|     public function start($inputFile, $outputDir, $filePrefix, $mappingClass, $mappingClassLocation, $liteBuild) | ||||
|     { | ||||
|         $savePath = $outputDir . $filePrefix; | ||||
|  | ||||
|         $metadataCollection = BuildMetadataFromXml::buildPhoneMetadataCollection($inputFile, $liteBuild); | ||||
|         $this->writeMetadataToFile($metadataCollection, $savePath); | ||||
|  | ||||
|         $countryCodeToRegionCodeMap = BuildMetadataFromXml::buildCountryCodeToRegionCodeMap($metadataCollection); | ||||
|         // Sort $countryCodeToRegionCodeMap just to have the regions in order | ||||
|         ksort($countryCodeToRegionCodeMap); | ||||
|         $this->writeCountryCallingCodeMappingToFile($countryCodeToRegionCodeMap, $mappingClassLocation, $mappingClass); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param $metadataCollection PhoneMetadata[] | ||||
|      * @param $filePrefix | ||||
|      */ | ||||
|     private function writeMetadataToFile($metadataCollection, $filePrefix) | ||||
|     { | ||||
|         foreach ($metadataCollection as $metadata) { | ||||
|             /** @var $phoneMetadata PhoneMetadata */ | ||||
|             $regionCode = $metadata->getId(); | ||||
|             // For non-geographical country calling codes (e.g. +800), use the country calling codes | ||||
|             // instead of the region code to form the file name. | ||||
|             if ($regionCode === '001' || $regionCode == '') { | ||||
|                 $regionCode = $metadata->getCountryCode(); | ||||
|             } | ||||
|  | ||||
|             $data = '<?php' . PHP_EOL | ||||
|                 . self::GENERATION_COMMENT . PHP_EOL | ||||
|                 . 'return ' . var_export($metadata->toArray(), true) . ';' . PHP_EOL; | ||||
|  | ||||
|             file_put_contents($filePrefix . "_" . $regionCode . '.php', $data); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function writeCountryCallingCodeMappingToFile($countryCodeToRegionCodeMap, $outputDir, $mappingClass) | ||||
|     { | ||||
|         // Find out whether the countryCodeToRegionCodeMap has any region codes or country | ||||
|         // calling codes listed in it. | ||||
|         $hasRegionCodes = false; | ||||
|         foreach ($countryCodeToRegionCodeMap as $key => $listWithRegionCode) { | ||||
|             if (count($listWithRegionCode) > 0) { | ||||
|                 $hasRegionCodes = true; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $hasCountryCodes = (count($countryCodeToRegionCodeMap) > 1); | ||||
|  | ||||
|         $variableName = lcfirst($mappingClass); | ||||
|  | ||||
|         $data = '<?php' . PHP_EOL . | ||||
|             self::GENERATION_COMMENT . PHP_EOL . | ||||
|             "namespace libphonenumber;" . PHP_EOL . | ||||
|             "class {$mappingClass} {" . PHP_EOL . | ||||
|             PHP_EOL; | ||||
|  | ||||
|         if ($hasRegionCodes && $hasCountryCodes) { | ||||
|             $data .= self::MAP_COMMENT . PHP_EOL; | ||||
|             $data .= "   public static \${$variableName} = " . var_export( | ||||
|                     $countryCodeToRegionCodeMap, | ||||
|                     true | ||||
|                 ) . ";" . PHP_EOL; | ||||
|         } elseif ($hasCountryCodes) { | ||||
|             $data .= self::COUNTRY_CODE_SET_COMMENT . PHP_EOL; | ||||
|             $data .= "   public static \${$variableName} = " . var_export( | ||||
|                     array_keys($countryCodeToRegionCodeMap), | ||||
|                     true | ||||
|                 ) . ";" . PHP_EOL; | ||||
|         } else { | ||||
|             $data .= self::REGION_CODE_SET_COMMENT . PHP_EOL; | ||||
|             $data .= "   public static \${$variableName} = " . var_export( | ||||
|                     $countryCodeToRegionCodeMap[0], | ||||
|                     true | ||||
|                 ) . ";" . PHP_EOL; | ||||
|         } | ||||
|  | ||||
|         $data .= PHP_EOL . | ||||
|             "}" . PHP_EOL; | ||||
|  | ||||
|         file_put_contents($outputDir . $mappingClass . '.php', $data); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,41 @@ | ||||
| <?php | ||||
|  | ||||
| namespace libphonenumber\buildtools\Commands; | ||||
|  | ||||
| use libphonenumber\buildtools\BuildMetadataPHPFromXml; | ||||
| use Symfony\Component\Console\Command\Command; | ||||
| use Symfony\Component\Console\Input\InputArgument; | ||||
| use Symfony\Component\Console\Input\InputInterface; | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
|  | ||||
| class BuildMetadataPHPFromXMLCommand extends Command | ||||
| { | ||||
|     protected function configure() | ||||
|     { | ||||
|         $this->setName('BuildMetadataPHPFromXML'); | ||||
|         $this->setDescription('Generate phone metadata data files'); | ||||
|         $this->setDefinition( | ||||
|             array( | ||||
|                 new InputArgument('InputFile', InputArgument::REQUIRED, 'The input file containing phone number metadata in XML format.'), | ||||
|                 new InputArgument('OutputDirectory', InputArgument::REQUIRED, 'The output source directory to store phone number metadata (one file per region) and the country code to region code mapping file'), | ||||
|                 new InputArgument('DataPrefix', InputArgument::REQUIRED, 'The start of the filename to store the files (e.g. dataPrefix_GB.php'), | ||||
|                 new InputArgument('MappingClass', InputArgument::REQUIRED, 'The name of the mapping class generated'), | ||||
|                 new InputArgument('MappingClassLocation', InputArgument::REQUIRED, 'The directory where the mapping class is stored'), | ||||
|                 new InputArgument('LiteBuild', InputArgument::OPTIONAL, 'Whether to generate the lite-version of the metadata. When set to true, certain metadata will be omitted. AT this moment, example numbers information is omitted', false), | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     protected function execute(InputInterface $input, OutputInterface $output) | ||||
|     { | ||||
|         $build = new BuildMetadataPHPFromXml(); | ||||
|         $build->start( | ||||
|             $input->getArgument('InputFile'), | ||||
|             $input->getArgument('OutputDirectory'), | ||||
|             $input->getArgument('DataPrefix'), | ||||
|             $input->getArgument('MappingClass'), | ||||
|             $input->getArgument('MappingClassLocation'), | ||||
|             ($input->getArgument('LiteBuild') == 'true') ? true : false | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,34 @@ | ||||
| <?php | ||||
|  | ||||
| namespace libphonenumber\buildtools\Commands; | ||||
|  | ||||
| use libphonenumber\buildtools\GeneratePhonePrefixData; | ||||
| use Symfony\Component\Console\Command\Command; | ||||
| use Symfony\Component\Console\Input\InputArgument; | ||||
| use Symfony\Component\Console\Input\InputInterface; | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
|  | ||||
| class GeneratePhonePrefixDataCommand extends Command | ||||
| { | ||||
|     protected function configure() | ||||
|     { | ||||
|         $this->setName('GeneratePhonePrefixData'); | ||||
|         $this->setDescription('Generate phone prefix data files'); | ||||
|         $this->setDefinition( | ||||
|             array( | ||||
|                 new InputArgument('InputDirectory', InputArgument::REQUIRED, 'The input directory containing the locale/region.txt files'), | ||||
|                 new InputArgument('OutputDirectory', InputArgument::REQUIRED, 'The output source directory'), | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     protected function execute(InputInterface $input, OutputInterface $output) | ||||
|     { | ||||
|         $generatePhonePrefixData = new GeneratePhonePrefixData(); | ||||
|         $generatePhonePrefixData->start( | ||||
|             $input->getArgument('InputDirectory'), | ||||
|             $input->getArgument('OutputDirectory'), | ||||
|             $output | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| <?php | ||||
|  | ||||
| namespace libphonenumber\buildtools\Commands; | ||||
|  | ||||
| use libphonenumber\buildtools\GenerateTimeZonesMapData; | ||||
| use Symfony\Component\Console\Command\Command; | ||||
| use Symfony\Component\Console\Input\InputArgument; | ||||
| use Symfony\Component\Console\Input\InputInterface; | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
|  | ||||
| class GenerateTimeZonesMapDataCommand extends Command | ||||
| { | ||||
|     protected function configure() | ||||
|     { | ||||
|         $this->setName('GenerateTimeZonesMapData'); | ||||
|         $this->setDescription('Generate time zone data files'); | ||||
|         $this->setDefinition( | ||||
|             array( | ||||
|                 new InputArgument('InputFile', InputArgument::REQUIRED, 'The input file containing the timezone map data'), | ||||
|                 new InputArgument('OutputDirectory', InputArgument::REQUIRED, 'The output directory to save the file'), | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     protected function execute(InputInterface $input, OutputInterface $output) | ||||
|     { | ||||
|         new GenerateTimeZonesMapData($input->getArgument('InputFile'), $input->getArgument('OutputDirectory')); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										399
									
								
								vendor/giggsey/libphonenumber-for-php/build/libphonenumber/buildtools/GeneratePhonePrefixData.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								vendor/giggsey/libphonenumber-for-php/build/libphonenumber/buildtools/GeneratePhonePrefixData.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,399 @@ | ||||
| <?php | ||||
|  | ||||
| namespace libphonenumber\buildtools; | ||||
|  | ||||
| use Symfony\Component\Console\Helper\ProgressBar; | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
|  | ||||
| class GeneratePhonePrefixData | ||||
| { | ||||
|     const NANPA_COUNTRY_CODE = 1; | ||||
|     const DATA_FILE_EXTENSION = '.txt'; | ||||
|     const GENERATION_COMMENT = <<<'EOT' | ||||
| /** | ||||
|  * This file is automatically @generated by {@link GeneratePhonePrefixData}. | ||||
|  * Please don't modify it directly. | ||||
|  */ | ||||
|  | ||||
|  | ||||
| EOT; | ||||
|  | ||||
|     public $inputDir; | ||||
|     private $filesToIgnore = array('.', '..', '.svn', '.git'); | ||||
|     private $outputDir; | ||||
|     private $englishMaps = array(); | ||||
|  | ||||
|  | ||||
|     public function start($inputDir, $outputDir, OutputInterface $consoleOutput) | ||||
|     { | ||||
|         $this->inputDir = $inputDir; | ||||
|         $this->outputDir = $outputDir; | ||||
|  | ||||
|         $inputOutputMappings = $this->createInputOutputMappings(); | ||||
|         $availableDataFiles = array(); | ||||
|  | ||||
|         $progress = new ProgressBar($consoleOutput, count($inputOutputMappings)); | ||||
|  | ||||
|         $progress->start(); | ||||
|         foreach ($inputOutputMappings as $textFile => $outputFiles) { | ||||
|             $mappings = $this->readMappingsFromFile($textFile); | ||||
|  | ||||
|             $language = $this->getLanguageFromTextFile($textFile); | ||||
|  | ||||
|             $this->removeEmptyEnglishMappings($mappings, $language); | ||||
|             $this->makeDataFallbackToEnglish($textFile, $mappings); | ||||
|             $mappingForFiles = $this->splitMap($mappings, $outputFiles); | ||||
|  | ||||
|             foreach ($mappingForFiles as $outputFile => $value) { | ||||
|                 $this->writeMappingFile($language, $outputFile, $value); | ||||
|                 $this->addConfigurationMapping($availableDataFiles, $language, $outputFile); | ||||
|             } | ||||
|             $progress->advance(); | ||||
|         } | ||||
|  | ||||
|         $this->writeConfigMap($availableDataFiles); | ||||
|         $progress->finish(); | ||||
|     } | ||||
|  | ||||
|     private function createInputOutputMappings() | ||||
|     { | ||||
|         $topLevel = scandir($this->inputDir); | ||||
|  | ||||
|         $mappings = array(); | ||||
|  | ||||
|         foreach ($topLevel as $languageDirectory) { | ||||
|             if (in_array($languageDirectory, $this->filesToIgnore)) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             $fileLocation = $this->inputDir . DIRECTORY_SEPARATOR . $languageDirectory; | ||||
|  | ||||
|             if (is_dir($fileLocation)) { | ||||
|                 // Will contain files | ||||
|  | ||||
|                 $countryCodeFiles = scandir($fileLocation); | ||||
|  | ||||
|                 foreach ($countryCodeFiles as $countryCodeFileName) { | ||||
|                     if (in_array($countryCodeFileName, $this->filesToIgnore)) { | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|  | ||||
|                     $outputFiles = $this->createOutputFileNames( | ||||
|                         $countryCodeFileName, | ||||
|                         $this->getCountryCodeFromTextFileName($countryCodeFileName), | ||||
|                         $languageDirectory | ||||
|                     ); | ||||
|  | ||||
|                     $mappings[$languageDirectory . DIRECTORY_SEPARATOR . $countryCodeFileName] = $outputFiles; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $mappings; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method used by {@code #createInputOutputMappings()} to generate the list of output binary files | ||||
|      * from the provided input text file. For the data files expected to be large (currently only | ||||
|      * NANPA is supported), this method generates a list containing one output file for each area | ||||
|      * code. Otherwise, a single file is added to the list. | ||||
|      */ | ||||
|  | ||||
|     private function createOutputFileNames($file, $countryCode, $language) | ||||
|     { | ||||
|         $outputFiles = array(); | ||||
|  | ||||
|         if ($countryCode == self::NANPA_COUNTRY_CODE) { | ||||
|             // Fetch the 4-digit prefixes stored in the file. | ||||
|             $phonePrefixes = array(); | ||||
|  | ||||
|             $this->parseTextFile( | ||||
|                 $this->getFilePathFromLanguageAndCountryCode($language, $countryCode), | ||||
|                 function ($prefix, $location) use (&$phonePrefixes) { | ||||
|                     $shortPrefix = substr($prefix, 0, 4); | ||||
|                     if (!in_array($shortPrefix, $phonePrefixes)) { | ||||
|                         $phonePrefixes[] = $shortPrefix; | ||||
|                     } | ||||
|                 } | ||||
|             ); | ||||
|  | ||||
|             foreach ($phonePrefixes as $prefix) { | ||||
|                 $outputFiles[] = $this->generateFilename($prefix, $language); | ||||
|             } | ||||
|         } elseif ($countryCode == 86) { | ||||
|  | ||||
|             /* | ||||
|              * Reduce memory usage for China numbers | ||||
|              * @see https://github.com/giggsey/libphonenumber-for-php/issues/44 | ||||
|              */ | ||||
|  | ||||
|             // Fetch the 5-digit prefixes stored in the file. | ||||
|             $phonePrefixes = array(); | ||||
|  | ||||
|             $this->parseTextFile( | ||||
|                 $this->getFilePathFromLanguageAndCountryCode($language, $countryCode), | ||||
|                 function ($prefix, $location) use (&$phonePrefixes) { | ||||
|                     $shortPrefix = substr($prefix, 0, 5); | ||||
|                     if (!in_array($shortPrefix, $phonePrefixes)) { | ||||
|                         $phonePrefixes[] = $shortPrefix; | ||||
|                     } | ||||
|                 } | ||||
|             ); | ||||
|  | ||||
|             foreach ($phonePrefixes as $prefix) { | ||||
|                 $outputFiles[] = $this->generateFilename($prefix, $language); | ||||
|             } | ||||
|         } else { | ||||
|             $outputFiles[] = $this->generateFilename($countryCode, $language); | ||||
|         } | ||||
|  | ||||
|         return $outputFiles; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reads phone prefix data from the provides file path and invokes the given handler for each | ||||
|      * mapping read. | ||||
|      * | ||||
|      * @param $filePath | ||||
|      * @param $handler | ||||
|      * @return array | ||||
|      * @throws \InvalidArgumentException | ||||
|      */ | ||||
|     private function parseTextFile($filePath, \Closure $handler) | ||||
|     { | ||||
|         if (!file_exists($filePath) || !is_readable($filePath)) { | ||||
|             throw new \InvalidArgumentException("File '{$filePath}' does not exist"); | ||||
|         } | ||||
|  | ||||
|         $data = file($filePath); | ||||
|  | ||||
|         $countryData = array(); | ||||
|  | ||||
|         foreach ($data as $line) { | ||||
|             // Remove \n | ||||
|             $line = str_replace("\n", "", $line); | ||||
|             $line = str_replace("\r", "", $line); | ||||
|             $line = trim($line); | ||||
|  | ||||
|             if (strlen($line) == 0 || substr($line, 0, 1) == '#') { | ||||
|                 continue; | ||||
|             } | ||||
|             if (strpos($line, '|')) { | ||||
|                 // Valid line | ||||
|                 $parts = explode('|', $line); | ||||
|  | ||||
|  | ||||
|                 $prefix = $parts[0]; | ||||
|                 $location = $parts[1]; | ||||
|  | ||||
|                 $handler($prefix, $location); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $countryData; | ||||
|     } | ||||
|  | ||||
|     private function getFilePathFromLanguageAndCountryCode($language, $code) | ||||
|     { | ||||
|         return $this->getFilePath($language . DIRECTORY_SEPARATOR . $code . self::DATA_FILE_EXTENSION); | ||||
|     } | ||||
|  | ||||
|     private function getFilePath($fileName) | ||||
|     { | ||||
|         $path = $this->inputDir . $fileName; | ||||
|  | ||||
|         return $path; | ||||
|     } | ||||
|  | ||||
|     private function generateFilename($prefix, $language) | ||||
|     { | ||||
|         return $language . DIRECTORY_SEPARATOR . $prefix . self::DATA_FILE_EXTENSION; | ||||
|     } | ||||
|  | ||||
|     private function getCountryCodeFromTextFileName($countryCodeFileName) | ||||
|     { | ||||
|         return str_replace(self::DATA_FILE_EXTENSION, '', $countryCodeFileName); | ||||
|     } | ||||
|  | ||||
|     private function readMappingsFromFile($inputFile) | ||||
|     { | ||||
|         $areaCodeMap = array(); | ||||
|  | ||||
|         $this->parseTextFile( | ||||
|             $this->inputDir . $inputFile, | ||||
|             function ($prefix, $location) use (&$areaCodeMap) { | ||||
|                 $areaCodeMap[$prefix] = $location; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return $areaCodeMap; | ||||
|     } | ||||
|  | ||||
|     private function getLanguageFromTextFile($textFile) | ||||
|     { | ||||
|         $parts = explode(DIRECTORY_SEPARATOR, $textFile); | ||||
|  | ||||
|         return $parts[0]; | ||||
|     } | ||||
|  | ||||
|     private function removeEmptyEnglishMappings(&$mappings, $language) | ||||
|     { | ||||
|         if ($language != "en") { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         foreach ($mappings as $k => $v) { | ||||
|             if ($v == "") { | ||||
|                 unset($mappings[$k]); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Compress the provided mappings according to the English data file if any. | ||||
|      * @param string $textFile | ||||
|      * @param array $mappings | ||||
|      */ | ||||
|     private function makeDataFallbackToEnglish($textFile, &$mappings) | ||||
|     { | ||||
|         $englishPath = $this->getEnglishDataPath($textFile); | ||||
|  | ||||
|         if ($textFile == $englishPath || !file_exists($this->getFilePath($englishPath))) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $countryCode = substr($textFile, 3, 2); | ||||
|  | ||||
|         if (!array_key_exists($countryCode, $this->englishMaps)) { | ||||
|             $englishMap = $this->readMappingsFromFile($englishPath); | ||||
|  | ||||
|             $this->englishMaps[$countryCode] = $englishMap; | ||||
|         } | ||||
|  | ||||
|         $this->compressAccordingToEnglishData($this->englishMaps[$countryCode], $mappings); | ||||
|     } | ||||
|  | ||||
|     private function getEnglishDataPath($textFile) | ||||
|     { | ||||
|         return "en" . DIRECTORY_SEPARATOR . substr($textFile, 3, 2) . self::DATA_FILE_EXTENSION; | ||||
|     } | ||||
|  | ||||
|     private function compressAccordingToEnglishData($englishMap, &$nonEnglishMap) | ||||
|     { | ||||
|         foreach ($nonEnglishMap as $prefix => $value) { | ||||
|             if (array_key_exists($prefix, $englishMap)) { | ||||
|                 $englishDescription = $englishMap[$prefix]; | ||||
|                 if ($englishDescription == $value) { | ||||
|                     if (!$this->hasOverlappingPrefix($prefix, $nonEnglishMap)) { | ||||
|                         unset($nonEnglishMap[$prefix]); | ||||
|                     } else { | ||||
|                         $nonEnglishMap[$prefix] = ""; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function hasOverlappingPrefix($number, $mappings) | ||||
|     { | ||||
|         while (strlen($number) > 0) { | ||||
|             $number = substr($number, 0, -1); | ||||
|  | ||||
|             if (array_key_exists($number, $mappings)) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     private function splitMap($mappings, $outputFiles) | ||||
|     { | ||||
|         $mappingForFiles = array(); | ||||
|  | ||||
|         foreach ($mappings as $prefix => $location) { | ||||
|             $targetFile = null; | ||||
|  | ||||
|             foreach ($outputFiles as $k => $outputFile) { | ||||
|                 $outputFilePrefix = $this->getPhonePrefixLanguagePairFromFilename($outputFile)->prefix; | ||||
|                 if (self::startsWith($prefix, $outputFilePrefix)) { | ||||
|                     $targetFile = $outputFilePrefix; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|             if (!array_key_exists($targetFile, $mappingForFiles)) { | ||||
|                 $mappingForFiles[$targetFile] = array(); | ||||
|             } | ||||
|             $mappingForFiles[$targetFile][$prefix] = $location; | ||||
|         } | ||||
|  | ||||
|         return $mappingForFiles; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Extracts the phone prefix and the language code contained in the provided file name. | ||||
|      */ | ||||
|     private function getPhonePrefixLanguagePairFromFilename($outputFile) | ||||
|     { | ||||
|         $parts = explode(DIRECTORY_SEPARATOR, $outputFile); | ||||
|  | ||||
|         $returnObj = new \stdClass(); | ||||
|         $returnObj->language = $parts[0]; | ||||
|  | ||||
|         $returnObj->prefix = $this->getCountryCodeFromTextFileName($parts[1]); | ||||
|  | ||||
|         return $returnObj; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @link http://stackoverflow.com/a/834355/403165 | ||||
|      * @param $haystack | ||||
|      * @param $needle | ||||
|      * @return bool | ||||
|      */ | ||||
|     private static function startsWith($haystack, $needle) | ||||
|     { | ||||
|         return !strncmp($haystack, $needle, strlen($needle)); | ||||
|     } | ||||
|  | ||||
|     private function writeMappingFile($language, $outputFile, $data) | ||||
|     { | ||||
|         if (!file_exists($this->outputDir . $language)) { | ||||
|             mkdir($this->outputDir . $language); | ||||
|         } | ||||
|  | ||||
|         $phpSource = '<?php' . PHP_EOL | ||||
|             . self::GENERATION_COMMENT | ||||
|             . 'return ' . var_export($data, true) . ';' | ||||
|             . PHP_EOL; | ||||
|  | ||||
|         $outputPath = $this->outputDir . $language . DIRECTORY_SEPARATOR . $outputFile . '.php'; | ||||
|  | ||||
|         file_put_contents($outputPath, $phpSource); | ||||
|     } | ||||
|  | ||||
|     public function addConfigurationMapping(&$availableDataFiles, $language, $prefix) | ||||
|     { | ||||
|         if (!array_key_exists($language, $availableDataFiles)) { | ||||
|             $availableDataFiles[$language] = array(); | ||||
|         } | ||||
|  | ||||
|         $availableDataFiles[$language][] = $prefix; | ||||
|     } | ||||
|  | ||||
|     private function writeConfigMap($availableDataFiles) | ||||
|     { | ||||
|         $phpSource = '<?php' . PHP_EOL | ||||
|             . self::GENERATION_COMMENT | ||||
|             . 'return ' . var_export($availableDataFiles, true) . ';' | ||||
|             . PHP_EOL; | ||||
|  | ||||
|         $outputPath = $this->outputDir . 'Map.php'; | ||||
|  | ||||
|         file_put_contents($outputPath, $phpSource); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,76 @@ | ||||
| <?php | ||||
|  | ||||
| namespace libphonenumber\buildtools; | ||||
|  | ||||
| use libphonenumber\PhoneNumberToTimeZonesMapper; | ||||
|  | ||||
| class GenerateTimeZonesMapData | ||||
| { | ||||
|     const GENERATION_COMMENT = <<<'EOT' | ||||
| /** | ||||
|  * This file is automatically @generated by {@link GeneratePhonePrefixData}. | ||||
|  * Please don't modify it directly. | ||||
|  */ | ||||
|  | ||||
|  | ||||
| EOT; | ||||
|     private $inputTextFile; | ||||
|  | ||||
|     public function __construct($inputFile, $outputDir) | ||||
|     { | ||||
|         $this->inputTextFile = $inputFile; | ||||
|  | ||||
|         if (!is_readable($this->inputTextFile)) { | ||||
|             throw new \RuntimeException("The provided input text file does not exist."); | ||||
|         } | ||||
|  | ||||
|         $data = $this->parseTextFile(); | ||||
|         $this->writeMappingFile($outputDir, $data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reads phone prefix data from the provided input stream and returns a SortedMap with the | ||||
|      * prefix to time zones mappings. | ||||
|      */ | ||||
|     private function parseTextFile() | ||||
|     { | ||||
|         $data = file($this->inputTextFile); | ||||
|  | ||||
|         $timeZoneMap = array(); | ||||
|  | ||||
|         foreach ($data as $line) { | ||||
|             // Remove \n | ||||
|             $line = str_replace("\n", "", $line); | ||||
|             $line = str_replace("\r", "", $line); | ||||
|             $line = trim($line); | ||||
|  | ||||
|             if (strlen($line) == 0 || substr($line, 0, 1) == '#') { | ||||
|                 continue; | ||||
|             } | ||||
|             if (strpos($line, '|')) { | ||||
|                 // Valid line | ||||
|                 $parts = explode('|', $line); | ||||
|  | ||||
|  | ||||
|                 $prefix = $parts[0]; | ||||
|                 $timezone = $parts[1]; | ||||
|  | ||||
|                 $timeZoneMap[$prefix] = $timezone; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $timeZoneMap; | ||||
|     } | ||||
|  | ||||
|     private function writeMappingFile($outputFile, $data) | ||||
|     { | ||||
|         $phpSource = '<?php' . PHP_EOL | ||||
|             . self::GENERATION_COMMENT | ||||
|             . 'return ' . var_export($data, true) . ';' | ||||
|             . PHP_EOL; | ||||
|  | ||||
|         $outputPath = $outputFile . DIRECTORY_SEPARATOR . PhoneNumberToTimeZonesMapper::MAPPING_DATA_FILE_NAME; | ||||
|  | ||||
|         file_put_contents($outputPath, $phpSource); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Manish Verma
					Manish Verma