Understanding the difference between “possible” and “valid” is crucial:
Possible
Valid
A possible number has the right length for its region:
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();$number = $phoneUtil->parse('2345678901', 'US');if ($phoneUtil->isPossibleNumber($number)) { echo "This number has a valid length";}
Checks:
Country code is valid
Number length is within acceptable range
No detailed pattern matching
A valid number matches known patterns:
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();$number = $phoneUtil->parse('650-253-0000', 'US');if ($phoneUtil->isValidNumber($number)) { echo "This number matches known US patterns";}
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();// Example 1: Valid number (both possible and valid)$validNumber = $phoneUtil->parse('650-253-0000', 'US');var_dump($phoneUtil->isPossibleNumber($validNumber)); // truevar_dump($phoneUtil->isValidNumber($validNumber)); // true// Example 2: Possible but not valid$possibleOnly = $phoneUtil->parse('2000000000', 'US');var_dump($phoneUtil->isPossibleNumber($possibleOnly)); // true (right length)var_dump($phoneUtil->isValidNumber($possibleOnly)); // false (invalid pattern)// Example 3: Neither possible nor valid$tooShort = $phoneUtil->parse('123', 'US');var_dump($phoneUtil->isPossibleNumber($tooShort)); // falsevar_dump($phoneUtil->isValidNumber($tooShort)); // false
The ValidationResult enum provides detailed information about why a number might not be possible:
enum ValidationResult: int{ case IS_POSSIBLE = 0; case INVALID_COUNTRY_CODE = 1; case TOO_SHORT = 2; case TOO_LONG = 3; case IS_POSSIBLE_LOCAL_ONLY = 4; case INVALID_LENGTH = 5;}
Value: 1The country calling code is not recognized.
// +999 is not a valid country codetry { $number = $phoneUtil->parse('+999 1234567890', null); $result = $phoneUtil->isPossibleNumberWithReason($number);} catch (\libphonenumber\NumberParseException $e) { echo "Invalid country code";}
TOO_SHORT
Value: 2The number is shorter than all valid numbers for this region.
$number = $phoneUtil->parse('123', 'US');$result = $phoneUtil->isPossibleNumberWithReason($number);if ($result === \libphonenumber\ValidationResult::TOO_SHORT) { echo "Number is too short for US";}
TOO_LONG
Value: 3The number is longer than all valid numbers for this region.
$number = $phoneUtil->parse('12345678901234567890', 'US');$result = $phoneUtil->isPossibleNumberWithReason($number);if ($result === \libphonenumber\ValidationResult::TOO_LONG) { echo "Number is too long for US";}
IS_POSSIBLE_LOCAL_ONLY
Value: 4The number matches local-only patterns (may be dialable within an area but not nationally).
$localNumber = $phoneUtil->parse('496 0123', 'GB');$result = $phoneUtil->isPossibleNumberWithReason($localNumber);if ($result === \libphonenumber\ValidationResult::IS_POSSIBLE_LOCAL_ONLY) { echo "This is a local-only number";}
INVALID_LENGTH
Value: 5The number length doesn’t match any valid numbers for this region, but is between shortest and longest valid lengths.
Returns a ValidationResult with detailed information:
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();$number = $phoneUtil->parse('123', 'US');$result = $phoneUtil->isPossibleNumberWithReason($number);switch ($result) { case \libphonenumber\ValidationResult::IS_POSSIBLE: echo "Number is possible"; break; case \libphonenumber\ValidationResult::TOO_SHORT: echo "Number is too short"; break; case \libphonenumber\ValidationResult::TOO_LONG: echo "Number is too long"; break; case \libphonenumber\ValidationResult::INVALID_COUNTRY_CODE: echo "Invalid country code"; break; case \libphonenumber\ValidationResult::INVALID_LENGTH: echo "Invalid length"; break; case \libphonenumber\ValidationResult::IS_POSSIBLE_LOCAL_ONLY: echo "Local-only number"; break;}
Check if a number is possible for a specific phone number type:
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();$number = $phoneUtil->parse('7400 123456', 'GB');// Check if it's possible as a mobile number$result = $phoneUtil->isPossibleNumberForTypeWithReason( $number, \libphonenumber\PhoneNumberType::MOBILE);if ($result === \libphonenumber\ValidationResult::IS_POSSIBLE) { echo "Could be a mobile number";}// Check if it's possible as a fixed-line number$result = $phoneUtil->isPossibleNumberForTypeWithReason( $number, \libphonenumber\PhoneNumberType::FIXED_LINE);if ($result !== \libphonenumber\ValidationResult::IS_POSSIBLE) { echo "Not possible as a fixed-line number";}
Returns a boolean indicating if a number matches known patterns:
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();$number = $phoneUtil->parse('650-253-0000', 'US');if ($phoneUtil->isValidNumber($number)) { echo "Number is valid"; // Safe to store, display, or use} else { echo "Number is not valid"; // Show error to user}
isValidNumber() does NOT verify:
If the number is currently in service
If the number is assigned to a subscriber
If the number can receive calls or SMS
If the number belongs to a specific person or business
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();try { // Step 1: Parse the number $number = $phoneUtil->parse($userInput, $defaultRegion); // Step 2: Check if it's possible if (!$phoneUtil->isPossibleNumber($number)) { throw new Exception("Invalid phone number format"); } // Step 3: Check if it's valid if (!$phoneUtil->isValidNumber($number)) { throw new Exception("Phone number is not valid"); } // Step 4: Number is good to use $e164 = $phoneUtil->format($number, \libphonenumber\PhoneNumberFormat::E164); saveToDatabase($e164);} catch (\libphonenumber\NumberParseException $e) { echo "Cannot parse phone number: " . $e->getMessage();} catch (Exception $e) { echo $e->getMessage();}
The library cannot verify if a number is currently active:
$number = $phoneUtil->parse('+1-555-0100', 'US');if ($phoneUtil->isValidNumber($number)) { // ✅ Number matches US patterns // ❌ Does NOT mean the number can receive calls // ❌ Does NOT mean the number is assigned // ❌ Does NOT mean it's not disconnected}
To verify a number is in service, you need:
SMS verification
Voice call verification
Third-party number validation API
Number can receive SMS
Even valid mobile numbers may not accept SMS:
$number = $phoneUtil->parse('+1-650-253-0000', 'US');$type = $phoneUtil->getNumberType($number);if ($type === \libphonenumber\PhoneNumberType::MOBILE) { // ✅ Pattern matches mobile numbers // ❌ Does NOT guarantee SMS capability // ❌ Could be a data-only SIM // ❌ Could have SMS disabled}
Number ownership
Validation cannot determine who owns a number:
// These numbers might be valid but belong to:// - A different person// - A business// - A VoIP service// - A disconnected line// The library cannot tell
Reachability from your location
A valid number may not be dialable from your location:
$auNumber = $phoneUtil->parse('1300123456', 'AU');if ($phoneUtil->isValidNumber($auNumber)) { // ✅ Valid Australian number // ❌ Cannot be called from outside Australia // Use formatNumberForMobileDialing() to check dialability}
For critical applications, combine pattern validation with:
// 1. Pattern validation (libphonenumber)if (!$phoneUtil->isValidNumber($number)) { return "Invalid number format";}// 2. SMS verificationsendVerificationCode($number);// 3. User confirms they received the codeif (!verifyCode($number, $userCode)) { return "Number cannot receive SMS";}// 4. Optional: HLR lookup (via third-party service)// Checks if number is active on mobile network
// ❌ Don't validate raw stringsif (preg_match('/^\+[0-9]+$/', $userInput)) { // This is insufficient}// ✅ Always parse then validatetry { $number = $phoneUtil->parse($userInput, $region); if ($phoneUtil->isValidNumber($number)) { // Now we know it's valid }} catch (\libphonenumber\NumberParseException $e) { // Handle invalid input}
// For user input: use isValidNumberif ($phoneUtil->isValidNumber($number)) { saveNumber($number);}// For quick checks: use isPossibleNumberif ($phoneUtil->isPossibleNumber($number)) { // Might be valid, queue for later validation}// For detailed feedback: use isPossibleNumberWithReason$result = $phoneUtil->isPossibleNumberWithReason($number);// Show specific error to user
function validateAndFormat(string $input, string $region): ?string{ $phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance(); try { $number = $phoneUtil->parse($input, $region); if ($phoneUtil->isValidNumber($number)) { return $phoneUtil->format( $number, \libphonenumber\PhoneNumberFormat::E164 ); } } catch (\libphonenumber\NumberParseException $e) { // Log for debugging, but don't expose to user error_log("Phone parse error: " . $e->getMessage()); } return null;}
For production applications, always combine pattern validation with verification (SMS/voice) to ensure the number is reachable.