gepur-it/phone-number
Laravel/PHP package for handling phone numbers: parse, validate, format, and normalize international numbers with country-code support, plus convenient helpers and validation rules for cleaner user data.
The package is small (2 stars, no known dependents), so treat the README and src/ as your primary documentation — do not assume parity with more popular packages like propaganistas/laravel-phone.
Install and inspect.
composer require gepur-it/phone-number
Before writing any code, open vendor/gepur-it/phone-number/ and read:
composer.json — reveal the underlying dependency (likely giggsey/libphonenumber-for-php or brick/phonenumber) and the PHP/Laravel constraints.src/ — locate the service provider, the validation rule, and any value object / cast.config/ — check whether a config file is published and what defaults (default region, allowed number types) it exposes.Register anything that needs registering. If the README instructs publishing config or assets:
php artisan vendor:publish --provider="GepurIt\PhoneNumber\PhoneNumberServiceProvider"
Most packages of this shape auto-discover the service provider via composer.json extra.laravel.providers, so a manual registration is usually unnecessary.
First use case: validate a phone number on a registration form. The canonical entry point for a phone package is a validation rule. Wire it into a FormRequest:
public function rules(): array
{
return [
'phone' => ['required', 'string', new \GepurIt\PhoneNumber\Rules\PhoneNumber()],
];
}
If the package exposes a string alias instead (check the README), use 'phone' in the rule array. If it accepts a country argument, pass the context region (see Gotchas).
Persist in E.164. Whatever the rule accepts, normalize before saving — store +14155552671, not (415) 555-2671. If the package ships a cast or value object, this is its job; otherwise parse manually before $model->save().
Validation in FormRequests. The most common day-to-day use. Combine the package rule with required/nullable and your own messages() override so the error reads naturally:
public function messages(): array
{
return ['phone' => 'Enter a valid phone number, including country code.'];
}
Prefer a per-country form input or a dedicated "country" select paired with the phone field — relying on app()->getLocale() or a request header to infer region is fragile.
Model casting to a value object. If the package provides a PhoneNumber object or cast, use it on Eloquent models so formatting/parsing logic lives in one place:
protected $casts = [
'phone' => \GepurIt\PhoneNumber\Casts\PhoneNumberCast::class,
];
Then $user->phone->toE164(), ->toNational(), ->toInternational() at display points (Blade, mail templates, API resources). If no cast is shipped, wrap one yourself — the value of a phone package is exactly this normalization boundary.
Normalize at the edge. Accept whatever users type on the request layer, convert to E.164 immediately in a form request (prepareForValidation) or a dedicated NormalizePhoneNumber middleware, and let everything downstream — DB, jobs, SMS providers — work with a single canonical shape. This prevents the "two users, same number, different rows" bug.
Formatting for UI vs. storage. Store E.164 (varchar(16) minimum, room for a leading + and up to 15 digits). Render localized formats only at the presentation layer. For APIs, prefer E.164 in JSON and expose formatted as a separate, clearly non-canonical field.
Integration with SMS / verification flows. Twilio, Vonage, and similar all want E.164. Since the package normalizes to that, this is the happy path — pass $user->phone straight through. Validate again before sending (libphonenumber metadata can differ from carrier reality), and catch provider errors distinctly from validation errors.
Testing. Assert against both accepted and rejected fixtures: a real mobile in your target region, a landline, a short code, an invalid length, and a number without a country code. Because the package is niche, add your own feature test that hits the registration endpoint end-to-end — don't rely solely on the package's own tests.
Custom rule extension. If the shipped rule is too coarse, subclass or wrap it in your own Rule object and delegate parsing to the package, adding your own type restrictions (mobile-only, no premium-rate).
Region ambiguity is the #1 pitfall. A bare 555-2671 has no meaning without a country. Always supply a default region — via the package config, or by passing it to the rule/parser — and document that expectation. Do not let libphonenumber guess.
"Valid" ≠ "reachable". libphonenumber (if that's the backend) validates format and assignment metadata, not that the number is live. Pair validation with SMS/OTP verification for anything security-sensitive.
Leading zeros and E.164. Users in many countries type a leading 0 for national numbers; E.164 strips it. Make sure your normalization step handles that or you'll store numbers that look right but won't route.
Metadata staleness. If the package bundles libphonenumber metadata, it ages. Check the pinned version of the underlying library; if it's old, run composer update periodically, or the package will reject newly assigned ranges.
Package maturity. With 2 stars, 0 dependents, and no ecosystem — assume the README may be thin and the API surface may be undocumented. Read src/ before committing, pin the version in composer.json, and add a thin adapter layer (your own PhoneNumberService / custom rule) so you can swap to propaganistas/laravel-phone or brick/phonenumber later without rewriting call sites.
Check the underlying library first. The package is almost certainly a thin wrapper. If it exposes the underlying parser, you may not need the package at all — using giggsey/libphonenumber-for-php directly gives you a maintained, documented API with no third-party wrapper risk. Evaluate that trade-off before adoption.
Config quirks. If the config exposes "allowed types" (mobile/landline/toll-free), defaults usually permit everything. Narrow this deliberately for SMS flows — toll-free and premium numbers often validate but can't receive OTPs.
Column sizing. varchar(15) is a common but wrong choice — E.164 allows up to 15 digits plus the leading +. Use varchar(20) or larger to leave room for future extensions/tel: URIs.
Extension points. The usual seams — Laravel's Validator::extend, a custom Rule class, or a Macroable facade — let you add policy (e.g., "no premium numbers") without forking. Prefer these over editing vendor code.
Debugging. When a number fails validation, log the raw input and the region you passed — "invalid phone" alone is unhelpful. libphonenumber exposes a validation result code (too short, invalid country code, etc.); surface it if the package doesn't already.
How can I help you explore Laravel packages today?