# Propaganistas/Laravel-Phone: libphonenumber Validation and Casting for Laravel

> A Laravel package that wraps the PHP port of Google's libphonenumber to validate phone numbers by country and type, and to cast Eloquent attributes into PhoneNumber objects. It is small, opinionated and useful only if your input arrives as a phone number rather than as an SMS challenge.

**Propaganistas/Laravel-Phone** — Phone number functionality for Laravel

- Repository: https://github.com/Propaganistas/Laravel-Phone
- Website: https://laravel-phone.herokuapp.com/
- Stars: 3,038 · Forks: 234
- Language: PHP
- License: MIT
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/propaganistas-laravel-phone

## What Laravel-Phone actually solves

Phone validation by regex is a losing game. A rule like /^[0-9]{10}$/ accepts numbers that no carrier assigns and rejects perfectly good international formats. Laravel-Phone takes a different route: it delegates the decision to the PHP port of Google's libphonenumber, so a number is judged against the numbering plan of a real country rather than against a character count. The README describes the package as adding phone number functionality to Laravel based on that port.

The audience is narrow and clear. You are building a Laravel application that stores phone numbers for people in known countries, and you want the validator to reject junk before it reaches the database. The package gives you three things: a phone validation rule, Eloquent casts that turn a stored string into a PhoneNumber object, and a utility class for formatting and comparing numbers. Nothing here sends a message or contacts a carrier. If your requirement is proving that a human owns the number, this package is the wrong layer.

## How the validation rule decides a number is real

The rule is driven by parameters you pass after the colon. Writing phone:US,BE constrains the accepted originating countries to the United States and Belgium, and the README notes that country codes must be ISO 3166-1 alpha-2 compliant. The rule does not guess a country from the number alone unless you let it: the INTERNATIONAL parameter accepts any valid internationally formatted number alongside the whitelisted countries, which the README frames as useful when you expect locally formatted numbers from one country but still want to accept properly formatted foreign ones.

Type constraints are appended in the same parameter list. phone:mobile accepts only mobile numbers; phone:!mobile blacklists instead of whitelisting, and the README states plainly that you cannot mix whitelisted and blacklisted types in one rule. There is also a LENIENT parameter, and this is the one worth understanding before you enable it. With leniency on, only the length of the number is checked instead of actual carrier patterns. That is a real reduction in strictness, not a convenience flag, and it should be a deliberate choice rather than a default.

The expressive form mirrors the string form. The README shows (new Phone)->country(['US', 'BE']) as the equivalent of phone:US,BE, with type(), notType(), international() and lenient() covering the rest. The country can also come from another field: if you name the field my_input_country, the rule discovers it automatically, or you can pass a custom field name as a parameter, for example phone:custom_country_field.

## Installing Laravel-Phone and validating a first field

Installation is a single Composer command. The README states that the service provider is discovered automatically by Laravel, so there is no registration step.

```bash
composer require propaganistas/laravel-phone
```

After that command completes, the package is available to the validator. One manual step remains: the package does not ship a message for the rule. In every validation.php file in your languages directory you add a phone key, and the README gives the exact line to use.

```php
'phone' => 'The :attribute field must be a valid number.',
```

With the message in place, a first real rule looks like this. The example below accepts Belgian mobile numbers, or any other number written in full international format.

```php
'my_input' => 'phone:INTERNATIONAL,BE',
```

If you prefer the rule object, the README's equivalent is (new Phone)->international()->country('BE').

For storage, the package offers two casts. RawPhoneNumberCast keeps the raw input in the column; E164PhoneNumberCast writes a formatted E.164 value. Both accept a country hint either as a country code, as the name of another attribute, or through an attribute suffixed with _country that the cast detects on its own.

```php
public $casts = [
    'phone_1' => RawPhoneNumberCast::class.':country_field',
    'phone_2' => E164PhoneNumberCast::class.':BE',
];
```

Reading the attribute back gives a PhoneNumber object or null, which the README notes can then be formatted or compared using the utility class.

## The ordering trap in E164PhoneNumberCast

The sharpest constraint in the README is not about validation at all. It is about when Laravel applies casts. Laravel applies a cast the moment an attribute is set, so E164PhoneNumberCast needs the country value to already exist when the phone value arrives. If the number is not passed in international format and the country attribute is still empty, the cast throws an unexpected exception.

The README shows this directly with a fill() call. Putting phone before phone_country is marked wrong; putting phone_country first is marked correct. This is easy to miss because it looks like an array ordering detail rather than a behavioural requirement, and it will surface as a runtime exception in whichever code path happens to fill the model in the other order.

Both casts also expect valid phone numbers. The README states this explicitly and points back to the validation documentation, which means the cast is not a sanitizer. If you skip validation and feed the cast a malformed string, you get an exception rather than a cleaned-up value. Validation belongs before assignment, not after.

## Where the package stops, and what to use instead

Two boundaries are worth stating plainly. First, the README does not document rollback, migrations or any database schema opinion: the database considerations section exists in the table of contents, but the visible README text does not spell out column types or lengths, so the storage decision is yours. Second, and more important, there is no verification flow. Nothing in the README sends an SMS, generates a code or queries a carrier. A number that passes phone:mobile is syntactically and structurally plausible for that country; it is not proven to belong to anyone.

If you need proof of ownership, the alternative is not another validation package. It is a verification service or an SMS gateway that sends a one-time code and checks the response, with Laravel-Phone used as the input filter in front of it. The two are complementary rather than competing: Laravel-Phone decides whether the string is worth sending a message to, and the gateway decides whether the message arrives.

If instead your problem is formatting rather than validation, note that the utility class here is a thin layer over libphonenumber. Calling the underlying PHP port directly is a reasonable choice when you are outside Laravel or when you want the parser without the validator integration. The difference is integration cost, not capability: Laravel-Phone's value is that the rule, the casts and the container wiring already exist.

## Maintenance, licensing and upgrade cost

The repository is not archived, and its last push was on 2026-09-17. The most recent release listed is 6.0.3 from 2026-03-18, preceded by 6.0.2 in July 2025 and 6.0.0 in April 2025. The presence of a UPGRADING.md file at the repository root is the practical signal here: the maintainers keep upgrade notes, which matters because the 6.0.0 major release sits inside the recent history. Before moving a project from the 5.x line, read that file rather than the README, since the README documents current behaviour only.

Licensing is MIT, which is permissive and places few obligations on how you use the package in a Laravel application. The underlying libphonenumber port has its own licence and its own metadata update cadence, and that cadence is what determines whether newly assigned number ranges validate correctly. This article does not give legal advice; if you redistribute the package or bundle it into a product, review the licence files of both the package and the port it depends on.

Upgrade cost is low in normal use. The public surface is a validation rule, two cast classes and a utility class, so a major bump is unlikely to touch more than a handful of call sites. The real recurring cost is not upgrading the package; it is keeping the numbering metadata current, which is a dependency concern rather than a Laravel-Phone concern.

## Conclusion

Adopt Laravel-Phone when you already collect a phone number and a country, and you want Laravel's validator to reject numbers that do not match that country's real numbering plan. Do not adopt it as a verification system: the README documents no SMS, no one-time code and no carrier lookup, so a passing rule only means the string is plausible. Before wiring it into a model, check two things in your own code: that the country attribute is filled before the phone attribute, because E164PhoneNumberCast throws when it runs first, and that your validation.php language files carry a 'phone' entry, because the package does not add one for you.

## FAQ

### How do I install Laravel-Phone?

Run composer require propaganistas/laravel-phone. The README states the service provider is discovered automatically by Laravel, so no manual registration is needed. You still have to add a 'phone' entry to each validation.php language file.

### How does Laravel-Phone validate a phone number with a country code?

Pass ISO 3166-1 alpha-2 country codes as rule parameters, for example 'phone:US,BE', or use the rule object with (new Phone)->country(['US', 'BE']). The rule can also read the country from a field named after the phone field with _country appended, or from a custom field name you pass as a parameter.

### Can Laravel-Phone format a phone number before storing it?

Yes, through the E164PhoneNumberCast, which writes a formatted E.164 phone number to the database. The RawPhoneNumberCast is the alternative when you want to keep the raw input instead of a normalized value.

### Why does E164PhoneNumberCast throw an exception when I fill a model?

Laravel applies casts as soon as an attribute is set, so the country attribute must be set before the phone attribute. The README marks filling 'phone' before 'phone_country' as wrong and the reverse order as correct, because the cast needs a valid country when the number is not already in international format.

### Does Laravel-Phone verify that a phone number belongs to a real person?

No. The README documents validation, attribute casting and a utility class for formatting and comparison, but no SMS sending, one-time codes or carrier lookup. A number that passes the rule is structurally valid for its country, not proven to be owned by the person who entered it.

## Sources

- [License: MIT](https://github.com/Propaganistas/Laravel-Phone/blob/master/LICENSE)
- [Project website](https://laravel-phone.herokuapp.com/)
- [Propaganistas/Laravel-Phone on GitHub](https://github.com/Propaganistas/Laravel-Phone)
- [README](https://github.com/Propaganistas/Laravel-Phone/blob/master/README.md)
- [Releases](https://github.com/Propaganistas/Laravel-Phone/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/propaganistas-laravel-phone
