wal3fo / phone-country
Resolve country code from phone number for Laravel, with AI-powered normalization, disambiguation, fraud detection, and metadata enrichment.
Fund package maintenance!
Requires
- php: ^8.0
- illuminate/support: ^8.0|^9.0|^10.0|^11.0|^12.0|^13.0
README
Resolve ISO 3166-1 alpha-2 country codes from phone numbers.
Rich metadata Β· Flag & geography Β· Number formatting Β· HTML card output Β· Zero dependencies.
composer require wal3fo/phone-country
Features
| Feature | Method | Description |
|---|---|---|
| π Country resolution | resolveCountryCode() |
Returns the ISO alpha-2 code from any phone format |
| π Rich analysis | analyze() |
Full country metadata, number type, formatting, and more |
| π³οΈ Flag & geography | analyze() |
Emoji flag, region, continent, and capital city |
| π± Currency & language | analyze() |
ISO 4217 currency, currency name, and primary language |
| π Timezone | analyze() |
IANA timezone for 180+ countries |
| π Formatting | analyze() |
National and international number formats |
| πΌοΈ HTML card | toHtml() |
Self-contained, styled HTML card β no external dependencies |
| β Validation rule | PhoneCountryRule |
Laravel validation rule with allowlist and strict mode |
Quick start
use Wal3fo\PhoneCountry\PhoneCountryService; $analysis = PhoneCountryService::analyze('+212612345678'); // ββ Country & identity ββββββββββββββββββββββββββββββββββββ $analysis->countryCode; // 'MA' $analysis->countryName; // 'Morocco' $analysis->flag; // 'π²π¦' $analysis->dialingCode; // '212' $analysis->e164; // '+212612345678' // ββ Geography βββββββββββββββββββββββββββββββββββββββββββββ $analysis->region; // 'Northern Africa' $analysis->continent; // 'Africa' $analysis->capital; // 'Rabat' // ββ Cultural metadata βββββββββββββββββββββββββββββββββββββ $analysis->timezone; // 'Africa/Casablanca' $analysis->currency; // 'MAD' $analysis->currencyName; // 'Moroccan Dirham' $analysis->language; // 'Arabic' // ββ Number analysis βββββββββββββββββββββββββββββββββββββββ $analysis->nationalNumber; // '612345678' $analysis->numberType; // 'MOBILE' $analysis->isValid; // true $analysis->isPossible; // true $analysis->digitCount; // 9 // ββ Formatting ββββββββββββββββββββββββββββββββββββββββββββ $analysis->formatNational; // '61 23 45 67 8' $analysis->formatInternational; // '+212 612 345 678' // ββ Input metadata ββββββββββββββββββββββββββββββββββββββββ $analysis->raw; // '+212612345678' $analysis->normalized; // '212612345678' $analysis->inputFormat; // 'E164' $analysis->usedFallback; // false $analysis->resolvedAt; // '2025-01-01T00:00:00+00:00' // ββ Output helpers ββββββββββββββββββββββββββββββββββββββββ $analysis->explanation; // HTML <ul> summary card $analysis->toArray(); // full associative array $analysis->toJson(); // JSON string $analysis->toHtml(); // self-contained HTML card
Core methods
resolveCountryCode()
Resolves a phone number and returns the ISO 3166-1 alpha-2 country code.
PhoneCountryService::resolveCountryCode('+212612345678'); // 'MA' PhoneCountryService::resolveCountryCode('00212612345678'); // 'MA' PhoneCountryService::resolveCountryCode('0612345678', 'MA'); // 'MA' β local fallback PhoneCountryService::resolveCountryCode('0612345678'); // 'XX' β unresolvable
Returns 'XX' when the number cannot be resolved to any country.
analyze()
Analyzes a phone number and returns a rich PhoneAnalysis value object.
$analysis = PhoneCountryService::analyze('+33612345678'); $analysis->countryCode; // 'FR' $analysis->countryName; // 'France' $analysis->flag; // 'π«π·' $analysis->timezone; // 'Europe/Paris' $analysis->currency; // 'EUR' $analysis->language; // 'French' $analysis->numberType; // 'MOBILE' $analysis->isValid; // true $analysis->toHtml(); // styled HTML card
With a local number and fallback country:
$analysis = PhoneCountryService::analyze('0612345678', 'MA'); $analysis->countryCode; // 'MA' $analysis->e164; // '+212612345678' $analysis->inputFormat; // 'LOCAL' $analysis->usedFallback; // true
Validation rule
use Wal3fo\PhoneCountry\Rules\PhoneCountryRule; // Any valid international number 'phone' => ['required', new PhoneCountryRule()] // With Morocco as local fallback 'phone' => ['required', new PhoneCountryRule('MA')] // Restrict to a country allowlist 'phone' => ['required', new PhoneCountryRule(['MA', 'FR', 'ES'])] // Strict mode β reject local 0xxx format 'phone' => ['required', new PhoneCountryRule('MA', strict: true)]
Example in a Form Request:
use Wal3fo\PhoneCountry\Rules\PhoneCountryRule; class RegisterRequest extends FormRequest { public function rules(): array { return [ 'name' => ['required', 'string'], 'phone' => ['required', 'string', new PhoneCountryRule('MA')], ]; } }
Dependency injection
class UserProfileController extends Controller { public function __construct( protected PhoneCountryService $phoneService ) {} public function update(Request $request) { $analysis = $this->phoneService->analyze($request->phone_number); $user->update([ 'phone' => $analysis->e164, 'country_code' => $analysis->countryCode, ]); } }
analyze() β rich pipeline
π Country metadata
Every resolved number returns full geographic and cultural data:
$a = PhoneCountryService::analyze('+966512345678'); // Saudi Arabia $a->countryCode; // 'SA' $a->countryName; // 'Saudi Arabia' $a->flag; // 'πΈπ¦' $a->region; // 'Western Asia' $a->continent; // 'Asia' $a->capital; // 'Riyadh' $a->currency; // 'SAR' $a->currencyName; // 'Saudi Riyal' $a->language; // 'Arabic' $a->timezone; // 'Asia/Riyadh'
Covers 180+ countries with IANA timezone Β· ISO 4217 currency Β· capital city Β· world region.
π Number formatting
analyze() produces both national and international formatted strings:
$a = PhoneCountryService::analyze('+212612345678'); $a->formatNational; // '61 23 45 67 8' $a->formatInternational; // '+212 612 345 678' $a->nationalNumber; // '612345678' $a->digitCount; // 9 $b = PhoneCountryService::analyze('+33612345678'); $b->formatNational; // '61 23 45 67' $b->formatInternational; // '+33 612 345 678'
π’ Number type & validity
$a = PhoneCountryService::analyze('+212612345678'); $a->numberType; // 'MOBILE' $a->isValid; // true β passes length check for MA (exactly 9 digits) $a->isPossible; // true β within Β±1 of expected range $b = PhoneCountryService::analyze('+18005551234'); $b->numberType; // 'TOLL_FREE' $c = PhoneCountryService::analyze('+19005551234'); $c->numberType; // 'PREMIUM'
Number types: MOBILE Β· FIXED_LINE Β· TOLL_FREE Β· PREMIUM Β· UNKNOWN
πΌοΈ HTML card output
toHtml() returns a fully self-contained, styled card with no external dependencies:
$html = PhoneCountryService::analyze('+212612345678')->toHtml(); // Returns a <div class="pcr-card"> with inline CSS, flag, badges, and a data table
Ideal for debug views, Blade partials, or API responses that render in a browser.
The explanation magic property returns an HTML <ul> list summary:
echo PhoneCountryService::analyze('+33612345678')->explanation; // <ul> // <li><strong>Number:</strong> +33612345678</li> // <li><strong>Country:</strong> π«π· France (+33)</li> // ... // </ul>
π Input format detection
analyze() recognizes and normalizes multiple input formats:
PhoneCountryService::analyze('+212612345678')->inputFormat; // 'E164' PhoneCountryService::analyze('00212612345678')->inputFormat; // 'DOUBLE_ZERO' PhoneCountryService::analyze('212612345678')->inputFormat; // 'NUMERIC' PhoneCountryService::analyze('0612345678', 'MA')->inputFormat; // 'LOCAL'
PhoneAnalysis reference
Properties
| Property | Type | Description |
|---|---|---|
raw |
string |
Original input as-is |
normalized |
string |
Cleaned digits (no +, spaces, or dashes) |
e164 |
string |
Canonical E.164 format, e.g. +212612345678 |
nationalNumber |
string |
Number without the country calling code |
dialingCode |
string |
Numeric calling code, e.g. 212 |
countryCode |
string |
ISO 3166-1 alpha-2, e.g. MA |
countryName |
string |
Full English name, e.g. Morocco |
flag |
string |
Emoji flag, e.g. π²π¦ |
region |
string |
World region, e.g. Northern Africa |
continent |
string |
Continent, e.g. Africa |
capital |
string |
Capital city, e.g. Rabat |
currency |
string |
ISO 4217 code, e.g. MAD |
currencyName |
string |
Currency name, e.g. Moroccan Dirham |
language |
string |
Primary language, e.g. Arabic |
timezone |
string |
IANA timezone, e.g. Africa/Casablanca |
numberType |
string |
MOBILE / FIXED_LINE / TOLL_FREE / PREMIUM / UNKNOWN |
isValid |
bool |
Passes basic length check for the country |
isPossible |
bool |
Within Β±1 digit of expected length |
digitCount |
int |
Total digit count of nationalNumber |
formatNational |
string |
National format, e.g. 06 12 34 56 78 |
formatInternational |
string |
International format, e.g. +212 612 345 678 |
inputFormat |
string |
E164 / DOUBLE_ZERO / NUMERIC / LOCAL |
usedFallback |
bool |
true if localCountryCode fallback was applied |
resolvedAt |
string |
ISO 8601 UTC timestamp of resolution |
Methods & magic properties
| Name | Description |
|---|---|
toArray() |
Full associative array β ready for JSON responses |
toJson(int $flags) |
JSON string (default: JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) |
toHtml() |
Self-contained HTML card with inline CSS |
toUl() |
HTML <ul> summary list |
$analysis->explanation |
Magic alias for toUl() |
Examples
| Input | Country | Name | Format |
|---|---|---|---|
+212612345678 |
MA |
Morocco | E164 |
00212612345678 |
MA |
Morocco | DOUBLE_ZERO |
212612345678 |
MA |
Morocco | NUMERIC |
0612345678 + 'MA' |
MA |
Morocco | LOCAL |
+33612345678 |
FR |
France | E164 |
+18005551234 |
US |
United States | E164 (toll-free) |
+966512345678 |
SA |
Saudi Arabia | E164 |
0612345678 |
XX |
Unknown | LOCAL |
Installation & configuration
composer require wal3fo/phone-country
# Optionally publish the config
php artisan vendor:publish --tag=phone-country-config
// config/phone-country.php return [ 'default_country' => env('PHONE_COUNTRY_DEFAULT', 'XX'), 'unknown_code' => 'XX', ];
PHONE_COUNTRY_DEFAULT=MA
Performance β for high-volume workloads, cache analyze() results:
$analysis = Cache::remember("phone_analysis:{$phone}", 3600, fn () => PhoneCountryService::analyze($phone) );
File structure
src/
βββ PhoneCountryService.php β Main service β resolveCountryCode() and analyze()
βββ PhoneCountryServiceProvider.php β Laravel service provider
βββ PhoneAnalysis.php β Rich value object returned by analyze()
βββ PhoneResult.php β Lightweight value object
βββ CountryMetadata.php β Static map: flag, region, capital, currency, language, timezone
βββ Rules/
β βββ PhoneCountryRule.php β Laravel validation rule
βββ AI/
βββ PhoneNormalizer.php β Input cleaning & repair
βββ NanpDisambiguator.php β +1 area-code map (25+ NANP countries)
βββ FraudDetector.php β Toll-free / premium / VOIP signal detection
βββ MetadataEnricher.php β Extended metadata enrichment
βββ PhoneExplainer.php β Plain-language explanation generator
config/
βββ phone-country.php
Changelog
v1.0.0
- Initial release with
resolveCountryCode()and longest-prefix matching - Added
analyze()returningPhoneAnalysisvalue object - Added
PhoneAnalysiswith flag, region, continent, capital, currency name, language, timezone - Added national and international number formatting (
formatNational,formatInternational) - Added
isPossibleanddigitCountfor length plausibility checks - Added input format detection (
E164,DOUBLE_ZERO,NUMERIC,LOCAL) - Added
usedFallbackandresolvedAtmetadata - Added
toHtml()β self-contained HTML card with inline CSS - Added
toJson()andtoArray()for API responses - Added
explanationmagic property returning an HTML<ul>summary - Added
CountryMetadataβ static map for 180+ countries - Added
PhoneCountryRuleβ Laravel validation rule with allowlist and strict mode - Added
AI/β PhoneNormalizer, NanpDisambiguator, FraudDetector, MetadataEnricher, PhoneExplainer
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Commit your changes:
git commit -m 'Add your feature' - Push to the branch:
git push origin feature/your-feature - Open a Pull Request
License
The MIT License (MIT). See LICENSE for full details.