/** * Formats a phone number for out-of-country dialing purposes. * * Note that in this version, if the number was entered originally using alpha characters and * this version of the number is stored in raw_input, this representation of the number will be * used rather than the digit representation. Grouping information, as specified by characters * such as "-" and " ", will be retained. * * <p><b>Caveats:</b></p> * <ul> * <li> This will not produce good results if the country calling code is both present in the raw * input _and_ is the start of the national number. This is not a problem in the regions * which typically use alpha numbers. * <li> This will also not produce good results if the raw input has any grouping information * within the first three digits of the national number, and if the function needs to strip * preceding digits/words in the raw input before these digits. Normally people group the * first three digits together so this is not a huge problem - and will be fixed if it * proves to be so. * </ul> * * @param PhoneNumber $number the phone number that needs to be formatted * @param String $regionCallingFrom the region where the call is being placed * @return String the formatted phone number */ public function formatOutOfCountryKeepingAlphaChars(PhoneNumber $number, $regionCallingFrom) { $rawInput = $number->getRawInput(); // If there is no raw input, then we can't keep alpha characters because there aren't any. // In this case, we return formatOutOfCountryCallingNumber. if (mb_strlen($rawInput) == 0) { return $this->formatOutOfCountryCallingNumber($number, $regionCallingFrom); } $countryCode = $number->getCountryCode(); if (!$this->hasValidCountryCallingCode($countryCode)) { return $rawInput; } // Strip any prefix such as country calling code, IDD, that was present. We do this by comparing // the number in raw_input with the parsed number. // To do this, first we normalize punctuation. We retain number grouping symbols such as " " // only. $rawInput = $this->normalizeHelper($rawInput, self::$ALL_PLUS_NUMBER_GROUPING_SYMBOLS, true); // Now we trim everything before the first three digits in the parsed number. We choose three // because all valid alpha numbers have 3 digits at the start - if it does not, then we don't // trim anything at all. Similarly, if the national number was less than three digits, we don't // trim anything at all. $nationalNumber = $this->getNationalSignificantNumber($number); if (mb_strlen($nationalNumber) > 3) { $firstNationalNumberDigit = strpos($rawInput, substr($nationalNumber, 0, 3)); if ($firstNationalNumberDigit !== false) { $rawInput = substr($rawInput, $firstNationalNumberDigit); } } $metadataForRegionCallingFrom = $this->getMetadataForRegion($regionCallingFrom); if ($countryCode == self::NANPA_COUNTRY_CODE) { if ($this->isNANPACountry($regionCallingFrom)) { return $countryCode . " " . $rawInput; } } else { if ($metadataForRegionCallingFrom !== null && $countryCode == $this->getCountryCodeForValidRegion($regionCallingFrom)) { $formattingPattern = $this->chooseFormattingPatternForNumber($metadataForRegionCallingFrom->numberFormats(), $nationalNumber); if ($formattingPattern === null) { // If no pattern above is matched, we format the original input. return $rawInput; } $newFormat = new NumberFormat(); $newFormat->mergeFrom($formattingPattern); // The first group is the first group of digits that the user wrote together. $newFormat->setPattern("(\\d+)(.*)"); // Here we just concatenate them back together after the national prefix has been fixed. $newFormat->setFormat("\$1\$2"); // Now we format using this pattern instead of the default pattern, but with the national // prefix prefixed if necessary. // This will not work in the cases where the pattern (and not the leading digits) decide // whether a national prefix needs to be used, since we have overridden the pattern to match // anything, but that is not the case in the metadata to date. return $this->formatNsnUsingPattern($rawInput, $newFormat, PhoneNumberFormat::NATIONAL); } } $internationalPrefixForFormatting = ""; // If an unsupported region-calling-from is entered, or a country with multiple international // prefixes, the international format of the number is returned, unless there is a preferred // international prefix. if ($metadataForRegionCallingFrom !== null) { $internationalPrefix = $metadataForRegionCallingFrom->getInternationalPrefix(); $uniqueInternationalPrefixMatcher = new Matcher(self::UNIQUE_INTERNATIONAL_PREFIX, $internationalPrefix); $internationalPrefixForFormatting = $uniqueInternationalPrefixMatcher->matches() ? $internationalPrefix : $metadataForRegionCallingFrom->getPreferredInternationalPrefix(); } $formattedNumber = $rawInput; $regionCode = $this->getRegionCodeForCountryCode($countryCode); // Metadata cannot be null because the country calling code is valid. $metadataForRegion = $this->getMetadataForRegionOrCallingCode($countryCode, $regionCode); $this->maybeAppendFormattedExtension($number, $metadataForRegion, PhoneNumberFormat::INTERNATIONAL, $formattedNumber); if (mb_strlen($internationalPrefixForFormatting) > 0) { $formattedNumber = $internationalPrefixForFormatting . " " . $countryCode . " " . $formattedNumber; } else { // Invalid region entered as country-calling-from (so no metadata was found for it) or the // region chosen has multiple international dialling prefixes. $this->prefixNumberWithCountryCallingCode($countryCode, PhoneNumberFormat::INTERNATIONAL, $formattedNumber); } return $formattedNumber; }
/** * Formats a phone number in the specified format using default rules. Note that this does not * promise to produce a phone number that the user can dial from where they are - although we do * format in either 'national' or 'international' format depending on what the client asks for, we * do not currently support a more abbreviated format, such as for users in the same "area" who * could potentially dial the number without area code. Note that if the phone number has a * country calling code of 0 or an otherwise invalid country calling code, we cannot work out * which formatting rules to apply so we return the national significant number with no formatting * applied. * * @param PhoneNumber $number the phone number to be formatted * @param int $numberFormat the format the phone number should be formatted into * @return string the formatted phone number */ public function format(PhoneNumber $number, $numberFormat) { if ($number->getNationalNumber() == 0 && $number->hasRawInput()) { // Unparseable numbers that kept their raw input just use that. // This is the only case where a number can be formatted as E164 without a // leading '+' symbol (but the original number wasn't parseable anyway). // TODO: Consider removing the 'if' above so that unparseable // strings without raw input format to the empty string instead of "+00" $rawInput = $number->getRawInput(); if (strlen($rawInput) > 0) { return $rawInput; } } $metadata = NULL; $formattedNumber = ""; $countryCallingCode = $number->getCountryCode(); $nationalSignificantNumber = $this->getNationalSignificantNumber($number); if ($numberFormat == PhoneNumberFormat::E164) { // Early exit for E164 case (even if the country calling code is invalid) since no formatting // of the national number needs to be applied. Extensions are not formatted. $formattedNumber .= $nationalSignificantNumber; $this->prefixNumberWithCountryCallingCode($countryCallingCode, PhoneNumberFormat::E164, $formattedNumber); } elseif (!$this->hasValidCountryCallingCode($countryCallingCode)) { $formattedNumber .= $nationalSignificantNumber; } else { // Note getRegionCodeForCountryCode() is used because formatting information for regions which // share a country calling code is contained by only one region for performance reasons. For // example, for NANPA regions it will be contained in the metadata for US. $regionCode = $this->getRegionCodeForCountryCode($countryCallingCode); // Metadata cannot be null because the country calling code is valid (which means that the // region code cannot be ZZ and must be one of our supported region codes). $metadata = $this->getMetadataForRegionOrCallingCode($countryCallingCode, $regionCode); $formattedNumber .= $this->formatNsn($nationalSignificantNumber, $metadata, $numberFormat); $this->prefixNumberWithCountryCallingCode($countryCallingCode, $numberFormat, $formattedNumber); } $this->maybeAppendFormattedExtension($number, $metadata, $numberFormat, $formattedNumber); return $formattedNumber; }
/** * Merges the information from another phone number into this phone number. * * @param PhoneNumber $other The phone number to copy. * * @return PhoneNumber This PhoneNumber instance, for chaining method calls. */ public function mergeFrom(PhoneNumber $other) { if ($other->hasCountryCode()) { $this->setCountryCode($other->getCountryCode()); } if ($other->hasNationalNumber()) { $this->setNationalNumber($other->getNationalNumber()); } if ($other->hasExtension()) { $this->setExtension($other->getExtension()); } if ($other->hasItalianLeadingZero()) { $this->setItalianLeadingZero($other->isItalianLeadingZero()); } if ($other->hasNumberOfLeadingZeros()) { $this->setNumberOfLeadingZeros($other->getNumberOfLeadingZeros()); } if ($other->hasRawInput()) { $this->setRawInput($other->getRawInput()); } if ($other->hasCountryCodeSource()) { $this->setCountryCodeSource($other->getCountryCodeSource()); } if ($other->hasPreferredDomesticCarrierCode()) { $this->setPreferredDomesticCarrierCode($other->getPreferredDomesticCarrierCode()); } return $this; }
/** * Formats a phone number in the specified format using default rules. Note that this does not * promise to produce a phone number that the user can dial from where they are - although we do * format in either 'national' or 'international' format depending on what the client asks for, we * do not currently support a more abbreviated format, such as for users in the same "area" who * could potentially dial the number without area code. Note that if the phone number has a * country calling code of 0 or an otherwise invalid country calling code, we cannot work out * which formatting rules to apply so we return the national significant number with no formatting * applied. * * @param PhoneNumber $number the phone number to be formatted * @param int $numberFormat the format the phone number should be formatted into * @return string the formatted phone number */ public function format(PhoneNumber $number, $numberFormat) { if ($number->getNationalNumber() == 0 && $number->hasRawInput()) { $rawInput = $number->getRawInput(); if (strlen($rawInput) > 0) { return $rawInput; } } $formattedNumber = ""; $countryCallingCode = $number->getCountryCode(); $nationalSignificantNumber = $this->getNationalSignificantNumber($number); if ($numberFormat == PhoneNumberFormat::E164) { // Early exit for E164 case since no formatting of the national number needs to be applied. // Extensions are not formatted. $formattedNumber .= $nationalSignificantNumber; $this->prefixNumberWithCountryCallingCode($countryCallingCode, PhoneNumberFormat::E164, $formattedNumber); return $formattedNumber; } // Note getRegionCodeForCountryCode() is used because formatting information for regions which // share a country calling code is contained by only one region for performance reasons. For // example, for NANPA regions it will be contained in the metadata for US. $regionCode = $this->getRegionCodeForCountryCode($countryCallingCode); if (!$this->hasValidCountryCallingCode($countryCallingCode)) { $formattedNumber .= $nationalSignificantNumber; return $formattedNumber; } $metadata = $this->getMetadataForRegionOrCallingCode($countryCallingCode, $regionCode); $formattedNumber .= $this->formatNsn($nationalSignificantNumber, $metadata, $numberFormat); $this->maybeAppendFormattedExtension($number, $metadata, $numberFormat, $formattedNumber); $this->prefixNumberWithCountryCallingCode($countryCallingCode, $numberFormat, $formattedNumber); return $formattedNumber; }