/** * Formats a phone number in the specified format using client-defined formatting rules. Note that * if the phone number has a country calling code of zero or an otherwise invalid country calling * code, we cannot work out things like whether there should be a national prefix applied, or how * to format extensions, 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 * @param array $userDefinedFormats formatting rules specified by clients * @return String the formatted phone number */ public function formatByPattern(PhoneNumber $number, $numberFormat, array $userDefinedFormats) { $countryCallingCode = $number->getCountryCode(); $nationalSignificantNumber = $this->getNationalSignificantNumber($number); if (!$this->hasValidCountryCallingCode($countryCallingCode)) { return $nationalSignificantNumber; } // 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 $metadata = $this->getMetadataForRegionOrCallingCode($countryCallingCode, $regionCode); $formattedNumber = ""; $formattingPattern = $this->chooseFormattingPatternForNumber($userDefinedFormats, $nationalSignificantNumber); if ($formattingPattern === null) { // If no pattern above is matched, we format the number as a whole. $formattedNumber .= $nationalSignificantNumber; } else { $numFormatCopy = new NumberFormat(); // Before we do a replacement of the national prefix pattern $NP with the national prefix, we // need to copy the rule so that subsequent replacements for different numbers have the // appropriate national prefix. $numFormatCopy->mergeFrom($formattingPattern); $nationalPrefixFormattingRule = $formattingPattern->getNationalPrefixFormattingRule(); if (mb_strlen($nationalPrefixFormattingRule) > 0) { $nationalPrefix = $metadata->getNationalPrefix(); if (mb_strlen($nationalPrefix) > 0) { // Replace $NP with national prefix and $FG with the first group ($1). $npPatternMatcher = new Matcher(self::NP_PATTERN, $nationalPrefixFormattingRule); $nationalPrefixFormattingRule = $npPatternMatcher->replaceFirst($nationalPrefix); $fgPatternMatcher = new Matcher(self::FG_PATTERN, $nationalPrefixFormattingRule); $nationalPrefixFormattingRule = $fgPatternMatcher->replaceFirst("\\\$1"); $numFormatCopy->setNationalPrefixFormattingRule($nationalPrefixFormattingRule); } else { // We don't want to have a rule for how to format the national prefix if there isn't one. $numFormatCopy->clearNationalPrefixFormattingRule(); } } $formattedNumber .= $this->formatNsnUsingPattern($nationalSignificantNumber, $numFormatCopy, $numberFormat); } $this->maybeAppendFormattedExtension($number, $metadata, $numberFormat, $formattedNumber); $this->prefixNumberWithCountryCallingCode($countryCallingCode, $numberFormat, $formattedNumber); return $formattedNumber; }
/** * Formats a phone number using the original phone number format that the number is parsed from. * The original format is embedded in the country_code_source field of the PhoneNumber object * passed in. If such information is missing, the number will be formatted into the NATIONAL * format by default. When the number contains a leading zero and this is unexpected for this * country, or we don't have a formatting pattern for the number, the method returns the raw input * when it is available. * * Note this method guarantees no digit will be inserted, removed or modified as a result of * formatting. * * @param PhoneNumber $number the phone number that needs to be formatted in its original number format * @param string $regionCallingFrom the region whose IDD needs to be prefixed if the original number * has one * @return string the formatted phone number in its original number format */ public function formatInOriginalFormat(PhoneNumber $number, $regionCallingFrom) { if ($number->hasRawInput() && ($this->hasUnexpectedItalianLeadingZero($number) || !$this->hasFormattingPatternForNumber($number))) { // We check if we have the formatting pattern because without that, we might format the number // as a group without national prefix. return $number->getRawInput(); } if (!$number->hasCountryCodeSource()) { return $this->format($number, PhoneNumberFormat::NATIONAL); } switch ($number->getCountryCodeSource()) { case CountryCodeSource::FROM_NUMBER_WITH_PLUS_SIGN: $formattedNumber = $this->format($number, PhoneNumberFormat::INTERNATIONAL); break; case CountryCodeSource::FROM_NUMBER_WITH_IDD: $formattedNumber = $this->formatOutOfCountryCallingNumber($number, $regionCallingFrom); break; case CountryCodeSource::FROM_NUMBER_WITHOUT_PLUS_SIGN: $formattedNumber = substr($this->format($number, PhoneNumberFormat::INTERNATIONAL), 1); break; case CountryCodeSource::FROM_DEFAULT_COUNTRY: // Fall-through to default case. // Fall-through to default case. default: $regionCode = $this->getRegionCodeForCountryCode($number->getCountryCode()); // We strip non-digits from the NDD here, and from the raw input later, so that we can // compare them easily. $nationalPrefix = $this->getNddPrefixForRegion($regionCode, true); $nationalFormat = $this->format($number, PhoneNumberFormat::NATIONAL); if ($nationalPrefix === null || strlen($nationalPrefix) == 0) { // If the region doesn't have a national prefix at all, we can safely return the national // format without worrying about a national prefix being added. $formattedNumber = $nationalFormat; break; } // Otherwise, we check if the original number was entered with a national prefix. if ($this->rawInputContainsNationalPrefix($number->getRawInput(), $nationalPrefix, $regionCode)) { // If so, we can safely return the national format. $formattedNumber = $nationalFormat; break; } // Metadata cannot be null here because getNddPrefixForRegion() (above) returns null if // there is no metadata for the region. $metadata = $this->getMetadataForRegion($regionCode); $nationalNumber = $this->getNationalSignificantNumber($number); $formatRule = $this->chooseFormattingPatternForNumber($metadata->numberFormats(), $nationalNumber); // The format rule could still be null here if the national number was 0 and there was no // raw input (this should not be possible for numbers generated by the phonenumber library // as they would also not have a country calling code and we would have exited earlier). if ($formatRule === NULL) { $formattedNumber = $nationalFormat; break; } // When the format we apply to this number doesn't contain national prefix, we can just // return the national format. // TODO: Refactor the code below with the code in isNationalPrefixPresentIfRequired. $candidateNationalPrefixRule = $formatRule->getNationalPrefixFormattingRule(); // We assume that the first-group symbol will never be _before_ the national prefix. $indexOfFirstGroup = strpos($candidateNationalPrefixRule, '$1'); if ($indexOfFirstGroup <= 0) { $formattedNumber = $nationalFormat; break; } $candidateNationalPrefixRule = substr($candidateNationalPrefixRule, 0, $indexOfFirstGroup); $candidateNationalPrefixRule = $this->normalizeDigitsOnly($candidateNationalPrefixRule); if (strlen($candidateNationalPrefixRule) == 0) { // National prefix not used when formatting this number. $formattedNumber = $nationalFormat; break; } // Otherwise, we need to remove the national prefix from our output. $numFormatCopy = new NumberFormat(); $numFormatCopy->mergeFrom($formatRule); $numFormatCopy->clearNationalPrefixFormattingRule(); $numberFormats = array(); $numberFormats[] = $numFormatCopy; $formattedNumber = $this->formatByPattern($number, PhoneNumberFormat::NATIONAL, $numberFormats); break; } $rawInput = $number->getRawInput(); // If no digit is inserted/removed/modified as a result of our formatting, we return the // formatted phone number; otherwise we return the raw input the user entered. if ($formattedNumber !== NULL && strlen($rawInput) > 0) { $normalizedFormattedNumber = $this->normalizeHelper($formattedNumber, self::$DIALLABLE_CHAR_MAPPINGS, TRUE); $normalizedRawInput = $this->normalizeHelper($rawInput, self::$DIALLABLE_CHAR_MAPPINGS, TRUE); if ($normalizedFormattedNumber != $normalizedRawInput) { $formattedNumber = $rawInput; } } return $formattedNumber; }