Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Phone Validator Laravel Package

stuyam/laravel-phone-validator

A Laravel validation package for phone numbers, adding a simple rule to verify formats and improve form validation for user-submitted contact details.

View on GitHub
Deep Wiki
Context7

Getting Started

Pull the package in and wire up credentials before writing any validation code:

composer require stuyam/laravel-phone-validator
php artisan vendor:publish --provider="Stuyam\LaravelPhoneValidator\PhoneValidatorServiceProvider"

The package auto-registers its service provider on Laravel 5.5+, so there is nothing to add to config/app.php. Publishing gives you a config file (check the README for the exact tag/path — typically config/phone-validator.php) where the Twilio credentials live. Grab a free Twilio account SID and auth token, drop them into .env, and never hard-code them:

TWILIO_ACCOUNT_SID=ACxxxxxxxx
TWILIO_AUTH_TOKEN=xxxxxxxx

Where to look first: the published config file, the Phone rule class (Stuyam\LaravelPhoneValidator\Rules\Phone), and the README's usage block. That's the whole package — there is very little surface area, so reading the rule class tells you exactly what request it makes to Twilio.

First use case: replace a fragile regex on a registration form. The rule works in a FormRequest like any other Laravel rule:

use Stuyam\LaravelPhoneValidator\Rules\Phone;

public function rules(): array
{
    return [
        'name'  => ['required', 'string', 'max:255'],
        'phone' => ['required', 'string', new Phone],
    ];
}

The rule calls Twilio's free Lookup API to confirm the number is a real, parseable phone number — meaning it catches obvious typos and garbage strings that regex:/^\+?[0-9]{10,15}$/ happily lets through.

Implementation Patterns

Use it as a second gate, not the only gate. The idiomatic workflow is: normalize → cheap regex → Twilio lookup. Normalize to E.164 on the way in so the rule gets a consistent input and you store a consistent value:

protected function prepareForValidation(): void
{
    $this->merge([
        'phone' => $this->normalizePhone($this->input('phone')),
    ]);
}

Then chain it with the built-in checks so you fail fast on obvious junk before paying for a network round-trip:

'phone' => ['required', 'string', 'regex:/^\+[1-9]\d{6,14}$/', new Phone],

Wrap the rule in your own rule object. This is the cleanest day-to-day pattern and gives you a single seam for caching, logging, and error messages:

class ValidPhone implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $key = 'phone-valid:'.hash('sha256', $value);

        $valid = Cache::remember($key, now()->addDays(30), function () use ($value) {
            try {
                return (new Phone)->passes($attribute, $value);
            } catch (\Throwable $e) {
                report($e);
                return true; // fail open, see Gotchas
            }
        });

        if (! $valid) {
            $fail('The :attribute is not a valid phone number.');
        }
    }
}

Cache aggressively. The number-to-validity mapping is immutable, so there is no reason to hit Twilio twice for the same string. A Cache::remember with a long TTL drops your API usage to near-zero after the first few thousand signups and removes the network call from the hot path on repeat submissions.

Keep lookup and formatting separate. Use the rule for validity at the request boundary; if you also need country code, carrier, or line type, call the Twilio Lookup SDK directly in a service class rather than trying to coerce the rule into returning metadata. The rule is a boolean oracle — treat it as such.

Testing pattern: never let tests hit Twilio. Swap the rule out via a test service provider, or bind a fake implementation if the package exposes a contract:

// tests/TestCase.php
$this->app->bind(Phone::class, fn () => new class extends Phone {
    public function passes($attribute, $value): bool { return true; }
});

If the rule is registered under a string alias (e.g. 'phone'), use Validator::extend('phone', fn () => true) in the test bootstrap instead. Assert your validation flow in feature tests and cover the Twilio edge cases in one focused unit test with an HTTP client mock.

Where it fits well: registration, profile updates, lead capture forms, outbound SMS gating. Where it fits poorly: batch imports of tens of thousands of rows (sync HTTP becomes a bottleneck — queue it), and any field where a user might paste a number without a country code.

Gotchas and Tips

The validation call is a blocking HTTP request. Every validation adds 100–500ms to the request. On a synchronous registration endpoint this is noticeable. Cache the result, keep the request timeout tight (set Http::timeout(3) if the package uses the Laravel HTTP client — otherwise adjust at the Guzzle level), and consider deferring non-critical validation to a queued job.

Fail open, not closed, on Twilio outages. If Twilio is down or you've hit a rate limit, the rule throwing will block every user from registering. Catch exceptions in your own wrapper, log them, and let the user through (you can always re-verify later). A validator that breaks signups during a vendor incident is worse than one that misses a bad number.

This package is from September 2020. Before you ship, read its composer.json and confirm the illuminate/* constraints against your Laravel version. On Laravel 9+ you may need --with-all-dependencies when requiring, and the class may reference deprecated HTTP client or container APIs. Check whether the Twilio SDK it depends on is still on the Lookup API v1 — Twilio moved Lookup to v2 (with a different pricing model for line-type intelligence), and old SDK calls may return 404s or unexpected payloads. If the constraint fight gets ugly, the fallback is a 30-line custom rule that calls https://lookups.twilio.com/v1/PhoneNumbers/{number} directly.

Twilio's "free" lookup is not unconditionally free. Twilio has changed Lookup pricing over the years; carrier and line-type add-ons are billable. Budget-check with Twilio's current docs before you point bulk traffic at it. This is the single biggest operational surprise with this package.

Config caching bites you. If you set the env vars but ran php artisan config:cache before, the rule gets null credentials and throws an opaque auth error. Re-run php artisan config:clear / config:cache after changing .env.

Number format matters more than you think. Twilio validates a number as valid for the region it's parsed against. If your app is US-only and users type local numbers without +1, the lookup may fail for the wrong reason. Normalize to E.164 yourself or set the default country in the config if the package exposes one.

Custom error messages go in the rule's $messages / message() method or your FormRequest's messages() array — don't leave users staring at "The phone is invalid" when you can say "Please include your country code (e.g. +1 for the US)."

Extension points are thin. The practical seams are: (1) wrap the rule in your own ValidationRule, (2) bind a fake in the container for testing, (3) swap the service provider in config/app.php if you need to override registration. There is no event fired on validation and no result DTO, so don't architect around one existing.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums