20steps/libphonenumber-for-php
PHP port/wrapper of Google’s libphonenumber, providing tools to parse, validate, and format international phone numbers, detect regions and number types, and handle country calling codes for consistent phone handling in your apps.
Installation:
composer require giggsey/libphonenumber-for-php
Add to composer.json if not auto-loaded:
"autoload": {
"psr-4": {
"App\\": "app/",
"libphonenumber\\": "vendor/giggsey/libphonenumber-for-php/src/"
}
}
Run composer dump-autoload.
First Use Case: Parse and format a phone number in a Laravel controller:
use libphonenumber\PhoneNumberUtil;
use libphonenumber\PhoneNumberFormat;
$phoneUtil = PhoneNumberUtil::getInstance();
$number = $phoneUtil->parse('+14155552671', 'US');
$formatted = $phoneUtil->format($number, PhoneNumberFormat::E164);
// Output: +14155552671
Key Classes to Know:
PhoneNumberUtil: Core utility for parsing, formatting, and validating.PhoneNumber: Represents a parsed phone number.PhoneNumberFormat: Constants for formatting styles (E164, INTERNATIONAL, etc.).// Validate and parse a user-submitted phone number
$phoneUtil = PhoneNumberUtil::getInstance();
try {
$number = $phoneUtil->parse($request->input('phone'), $request->input('country_code'));
if (!$phoneUtil->isValidNumber($number)) {
throw new \InvalidArgumentException('Invalid phone number');
}
} catch (\libphonenumber\NumberParseException $e) {
return back()->withErrors(['phone' => 'Invalid format']);
}
// Format for user-friendly display (e.g., in a view)
$formatted = $phoneUtil->format($number, PhoneNumberFormat::NATIONAL);
// Output: (415) 555-2671 (for US numbers)
// Get region code and country name
$regionCode = $phoneUtil->getRegionCodeForNumber($number);
$countryCode = $phoneUtil->getCountryCodeForNumber($number);
$countryName = $phoneUtil->getCountryNameForNumber($number);
// Process an array of phone numbers (e.g., from a CSV import)
$numbers = ['+14155552671', '+442071838750'];
$processed = [];
foreach ($numbers as $phone) {
$processed[] = $phoneUtil->parse($phone, null); // Auto-detect country
}
Create a custom rule:
use libphonenumber\PhoneNumberUtil;
use Illuminate\Contracts\Validation\Rule;
class ValidPhoneNumber implements Rule {
protected $phoneUtil;
public function __construct() {
$this->phoneUtil = PhoneNumberUtil::getInstance();
}
public function passes($attribute, $value) {
try {
$number = $this->phoneUtil->parse($value);
return $this->phoneUtil->isValidNumber($number);
} catch (\libphonenumber\NumberParseException $e) {
return false;
}
}
public function message() {
return 'The :attribute must be a valid phone number.';
}
}
Usage in a Form Request:
public function rules() {
return [
'phone' => ['required', new ValidPhoneNumber],
];
}
Store phone numbers in E164 format (e.g., +14155552671) in the database for consistency:
$e164Number = $phoneUtil->format($number, PhoneNumberFormat::E164);
$user->phone = $e164Number;
$user->save();
Register the PhoneNumberUtil as a singleton in AppServiceProvider:
public function register() {
$this->app->singleton(PhoneNumberUtil::class, function () {
return PhoneNumberUtil::getInstance();
});
}
Now inject it into controllers/services:
public function __construct(private PhoneNumberUtil $phoneUtil) {}
Since PhoneNumberUtil::getInstance() is thread-safe, cache it in Laravel’s cache:
$phoneUtil = Cache::remember('phone-number-util', 365 * 24 * 60, function () {
return PhoneNumberUtil::getInstance();
});
Country Code Ambiguity:
parse('02071838750')) may fail or return unexpected results.NumberParseException.Time Zone Data:
vendor directory is up-to-date (composer update).Short Numbers vs. Full E164:
5552671) may not parse correctly without a country code.+1 or the appropriate country code before parsing.Non-Standard Formats:
0 prefix in the UK).PhoneNumberFormat::NATIONAL for display, but store in E164.Performance with Large Datasets:
Check Parsing Errors:
try {
$number = $phoneUtil->parse($input);
} catch (\libphonenumber\NumberParseException $e) {
\Log::error("Phone parse error: " . $e->getMessage());
}
Validate Before Storing:
if (!$phoneUtil->isValidNumber($number)) {
throw new \RuntimeException("Invalid phone number: " . $phoneUtil->format($number));
}
Log Unexpected Formats:
$formats = [
PhoneNumberFormat::E164,
PhoneNumberFormat::INTERNATIONAL,
PhoneNumberFormat::NATIONAL,
];
foreach ($formats as $format) {
\Log::debug("Formatted ($format): " . $phoneUtil->format($number, $format));
}
Custom Formatters: Extend the library to add domain-specific formatting:
class CustomPhoneFormatter {
public static function formatForAPI($number) {
$util = PhoneNumberUtil::getInstance();
$formatted = $util->format($number, PhoneNumberFormat::E164);
return str_replace('+', '', $formatted); // Remove '+' for API consistency
}
}
Database Migration Helpers: Create a trait for phone number handling in Eloquent models:
trait HandlesPhoneNumbers {
public function getPhoneNumberAttribute($value) {
if (empty($value)) return null;
$util = PhoneNumberUtil::getInstance();
return $util->parse($value, null);
}
public function setPhoneNumberAttribute($value) {
$util = PhoneNumberUtil::getInstance();
$this->attributes['phone_number'] = $util->format($util->parse($value), PhoneNumberFormat::E164);
}
}
Testing:
Use libphonenumber\PhoneNumberUtil in PHPUnit tests:
public function testPhoneNumberValidation() {
$util = PhoneNumberUtil::getInstance();
$this->assertTrue($util->isValidNumber($util->parse('+14155552671')));
$this->assertFalse($util->isValidNumber($util->parse('invalid')));
}
Default Country Code: The library does not set a default country code. Always handle cases where parsing fails due to ambiguity.
Locale-Specific Formatting:
Use PhoneNumberFormat::RFC3966 for locale-agnostic formats (e.g., `tel:+1-415
How can I help you explore Laravel packages today?