misd/phone-number-bundle
Symfony bundle integrating Google’s libphonenumber via giggsey/libphonenumber-for-php. Provides services and helpers to parse, validate, format, and geocode phone numbers in Symfony 2–4 apps. Abandoned; use odolbeau/phone-number-bundle instead.
Since this bundle is Symfony-specific, direct integration into Laravel requires abstraction. Start by installing the underlying library (giggsey/libphonenumber-for-php) via Composer:
composer require giggsey/libphonenumber-for-php
First Use Case: Parsing and Validating a Phone Number
use libphonenumber\PhoneNumberUtil;
use libphonenumber\PhoneNumberFormat;
// Initialize the utility (Laravel service container)
$phoneUtil = PhoneNumberUtil::getInstance();
// Parse a raw string
$phoneNumber = $phoneUtil->parse('+442071838750', 'GB');
// Format for display
echo $phoneUtil->format($phoneNumber, PhoneNumberFormat::NATIONAL);
// Output: "020 7183 8750"
Key Laravel-Specific Setup:
Service Provider: Register PhoneNumberUtil in AppServiceProvider:
public function register()
{
$this->app->singleton(PhoneNumberUtil::class, function () {
return PhoneNumberUtil::getInstance();
});
}
Helper Methods: Create a facade or helper class to wrap common operations:
// app/Helpers/PhoneHelper.php
class PhoneHelper
{
public static function parse(string $number, string $region = 'GB'): \libphonenumber\PhoneNumber
{
return app(PhoneNumberUtil::class)->parse($number, $region);
}
}
"+1 (212) 555-1234" → +12125551234).$normalized = PhoneHelper::parse($rawInput, 'US')
->setNationalNumber($phoneUtil->getNationalNumber($normalized));
use libphonenumber\PhoneNumberType;
class PhoneValidator extends Validator
{
public function validateMobile(string $number, string $region = 'GB'): bool
{
$phone = PhoneHelper::parse($number, $region);
return PhoneNumberType::MOBILE === $phoneUtil->getNumberType($phone);
}
}
varchar(35)).Attribute or Cast):
// app/Models/User.php
use Illuminate\Database\Eloquent\Casts\Attribute;
class User extends Model
{
protected function phoneNumber(): Attribute
{
return Attribute::make(
get: fn ($value) => PhoneHelper::parse($value),
set: fn ($value) => $value->getE164Number()
);
}
}
FormRequest or use a custom rule:
use Illuminate\Validation\Rule;
$request->validate([
'phone' => [
'required',
Rule::custom(function ($attribute, $value, $fail) {
if (!PhoneHelper::validate($value, 'GB')) {
$fail('Invalid phone number.');
}
}),
],
]);
$carrier = app(\libphonenumber\PhoneNumberToCarrierMapper::class)
->getNumberTypeForValidNumber(PhoneHelper::parse($number));
Twig Alternative: Use Blade directives for formatting:
// app/Providers/AppServiceProvider.php
Blade::directive('phone', function ($expression) {
return "<?php echo app('phoneUtil')->format({$expression}, libphonenumber\PhoneNumberFormat::NATIONAL); ?>";
});
Usage:
@phone($user->phoneNumber)
Testing: Mock PhoneNumberUtil in tests:
$this->partialMock(PhoneNumberUtil::class, ['parse'])
->shouldReceive('parse')
->andReturn($mockPhoneNumber);
Performance: Cache parsed numbers if reused frequently:
Cache::remember("phone_{$number}", 3600, function () use ($number) {
return PhoneHelper::parse($number);
});
Region Ambiguity:
PhoneNumberUtil::UNKNOWN_REGION) may fail for ambiguous numbers (e.g., 123 could be US or UK).'GB' for UK-centric apps).Database Migration:
varchar columns may store malformed numbers (e.g., "0123456789" instead of "+44123456789").DB::table('users')->update([
'phone_number' => DB::raw("REPLACE(phone_number, '0', '+44')")
]);
Form Widget Quirks:
country_choice widget requires manual country code mapping (e.g., 'GB' → 'United Kingdom').resources/lang/en/validation.php:
'phone_number' => [
'country_GB' => 'United Kingdom',
],
Deprecated Features:
PhoneNumberFormatHelper). Laravel must replace these with custom logic.libphonenumber API.Edge Cases:
"0" or "123" may pass validation but are invalid.Rule::custom('phone', function ($attribute, $value) {
return strlen($value) > 3 && !str_starts_with($value, '0');
}),
Validation Errors:
PhoneNumberUtil::isValidNumber() to debug:
if (!$phoneUtil->isValidNumber($phoneNumber)) {
$errors = $phoneUtil->getErrorMessage($phoneNumber);
// Log or display $errors
}
Time Zone/Carrier Lookup:
libphonenumber >=5.8.8):
try {
$timeZones = app(\libphonenumber\PhoneNumberToTimeZonesMapper::class)
->getTimeZonesForValidNumber($phoneNumber);
} catch (\Exception $e) {
// Fallback logic
}
Performance Bottlenecks:
Cache::remember("parsed_{$number}", now()->addHours(1), function () use ($number) {
return PhoneHelper::parse($number);
});
Custom PhoneNumberType:
class UkMobileValidator extends PhoneValidator
{
public function validate(string $number): bool
{
$phone = PhoneHelper::parse($number, 'GB');
return $phoneUtil->getNumberType($phone) === PhoneNumberType::MOBILE
&& $phoneUtil->getRegionCodeForNumber($phone) === 'GB';
}
}
Geocoding Integration:
$geoData = app(\libphonenumber\PhoneNumberOfflineGeocoder::class)
->getRegionCodeForValidNumber($phoneNumber);
$location = GeocodingService::reverse($geoData);
Event Listeners:
event(new PhoneNumberUpdated($user, $oldNumber, $newNumber));
Testing Utilities:
How can I help you explore Laravel packages today?