/**
* Takes two phone numbers and compares them for equality.
*
* <p>Returns EXACT_MATCH if the country_code, NSN, presence of a leading zero
* for Italian numbers and any extension present are the same. Returns NSN_MATCH
* if either or both has no region specified, and the NSNs and extensions are
* the same. Returns SHORT_NSN_MATCH if either or both has no region specified,
* or the region specified is the same, and one NSN could be a shorter version
* of the other number. This includes the case where one has an extension
* specified, and the other does not. Returns NO_MATCH otherwise. For example,
* the numbers +1 345 657 1234 and 657 1234 are a SHORT_NSN_MATCH. The numbers
* +1 345 657 1234 and 345 657 are a NO_MATCH.
*
* @param $firstNumberIn PhoneNumber|string First number to compare. If it is a
* string it can contain formatting, and can have country calling code specified
* with + at the start.
* @param $secondNumberIn PhoneNumber|string Second number to compare. If it is a
* string it can contain formatting, and can have country calling code specified
* with + at the start.
* @throws \InvalidArgumentException
* @return int {MatchType} NOT_A_NUMBER, NO_MATCH,
*/
public function isNumberMatch($firstNumberIn, $secondNumberIn)
{
if (is_string($firstNumberIn) && is_string($secondNumberIn)) {
try {
$firstNumberAsProto = $this->parse($firstNumberIn, self::UNKNOWN_REGION);
return $this->isNumberMatch($firstNumberAsProto, $secondNumberIn);
} catch (NumberParseException $e) {
if ($e->getErrorType() === NumberParseException::INVALID_COUNTRY_CODE) {
try {
$secondNumberAsProto = $this->parse($secondNumberIn, self::UNKNOWN_REGION);
return $this->isNumberMatch($secondNumberAsProto, $firstNumberIn);
} catch (NumberParseException $e2) {
if ($e2->getErrorType() === NumberParseException::INVALID_COUNTRY_CODE) {
try {
$firstNumberProto = new PhoneNumber();
$secondNumberProto = new PhoneNumber();
$this->parseHelper($firstNumberIn, null, false, false, $firstNumberProto);
$this->parseHelper($secondNumberIn, null, false, false, $secondNumberProto);
return $this->isNumberMatch($firstNumberProto, $secondNumberProto);
} catch (NumberParseException $e3) {
// Fall through and return MatchType::NOT_A_NUMBER
}
}
}
}
}
return MatchType::NOT_A_NUMBER;
}
if ($firstNumberIn instanceof PhoneNumber && is_string($secondNumberIn)) {
// First see if the second number has an implicit country calling code, by attempting to parse
// it.
try {
$secondNumberAsProto = $this->parse($secondNumberIn, self::UNKNOWN_REGION);
return $this->isNumberMatch($firstNumberIn, $secondNumberAsProto);
} catch (NumberParseException $e) {
if ($e->getErrorType() === NumberParseException::INVALID_COUNTRY_CODE) {
// The second number has no country calling code. EXACT_MATCH is no longer possible.
// We parse it as if the region was the same as that for the first number, and if
// EXACT_MATCH is returned, we replace this with NSN_MATCH.
$firstNumberRegion = $this->getRegionCodeForCountryCode($firstNumberIn->getCountryCode());
try {
if ($firstNumberRegion != self::UNKNOWN_REGION) {
$secondNumberWithFirstNumberRegion = $this->parse($secondNumberIn, $firstNumberRegion);
$match = $this->isNumberMatch($firstNumberIn, $secondNumberWithFirstNumberRegion);
if ($match === MatchType::EXACT_MATCH) {
return MatchType::NSN_MATCH;
}
return $match;
} else {
// If the first number didn't have a valid country calling code, then we parse the
// second number without one as well.
$secondNumberProto = new PhoneNumber();
$this->parseHelper($secondNumberIn, null, false, false, $secondNumberProto);
return $this->isNumberMatch($firstNumberIn, $secondNumberProto);
}
} catch (NumberParseException $e2) {
// Fall-through to return NOT_A_NUMBER.
}
}
}
}
if ($firstNumberIn instanceof PhoneNumber && $secondNumberIn instanceof PhoneNumber) {
// Make copies of the phone number so that the numbers passed in are not edited.
$firstNumber = new PhoneNumber();
$firstNumber->mergeFrom($firstNumberIn);
$secondNumber = new PhoneNumber();
$secondNumber->mergeFrom($secondNumberIn);
// First clear raw_input, country_code_source and preferred_domestic_carrier_code fields and any
// empty-string extensions so that we can use the proto-buffer equality method.
$firstNumber->clearRawInput();
$firstNumber->clearCountryCodeSource();
$firstNumber->clearPreferredDomesticCarrierCode();
$secondNumber->clearRawInput();
$secondNumber->clearCountryCodeSource();
$secondNumber->clearPreferredDomesticCarrierCode();
if ($firstNumber->hasExtension() && mb_strlen($firstNumber->getExtension()) === 0) {
$firstNumber->clearExtension();
}
if ($secondNumber->hasExtension() && mb_strlen($secondNumber->getExtension()) === 0) {
$secondNumber->clearExtension();
}
// Early exit if both had extensions and these are different.
if ($firstNumber->hasExtension() && $secondNumber->hasExtension() && $firstNumber->getExtension() != $secondNumber->getExtension()) {
return MatchType::NO_MATCH;
}
$firstNumberCountryCode = $firstNumber->getCountryCode();
$secondNumberCountryCode = $secondNumber->getCountryCode();
// Both had country_code specified.
if ($firstNumberCountryCode != 0 && $secondNumberCountryCode != 0) {
if ($firstNumber->equals($secondNumber)) {
return MatchType::EXACT_MATCH;
} elseif ($firstNumberCountryCode == $secondNumberCountryCode && $this->isNationalNumberSuffixOfTheOther($firstNumber, $secondNumber)) {
// A SHORT_NSN_MATCH occurs if there is a difference because of the presence or absence of
// an 'Italian leading zero', the presence or absence of an extension, or one NSN being a
// shorter variant of the other.
return MatchType::SHORT_NSN_MATCH;
}
// This is not a match.
return MatchType::NO_MATCH;
}
// Checks cases where one or both country_code fields were not specified. To make equality
// checks easier, we first set the country_code fields to be equal.
$firstNumber->setCountryCode($secondNumberCountryCode);
// If all else was the same, then this is an NSN_MATCH.
if ($firstNumber->equals($secondNumber)) {
return MatchType::NSN_MATCH;
}
if ($this->isNationalNumberSuffixOfTheOther($firstNumber, $secondNumber)) {
return MatchType::SHORT_NSN_MATCH;
}
return MatchType::NO_MATCH;
}
return MatchType::NOT_A_NUMBER;
}