# giggsey/libphonenumber-for-php: parsing, formatting and validating international numbers in PHP

> A PHP port of Google's libphonenumber that keeps Google's version numbers and ships the metadata as PHP source. It handles parsing, validation, formatting, geocoding and carrier lookup, and the lite variant drops everything except the core utility.

**giggsey/libphonenumber-for-php** — PHP version of Google's phone number handling library

- Repository: https://github.com/giggsey/libphonenumber-for-php
- Website: https://giggsey.com/libphonenumber/
- Stars: 5,063 · Forks: 480
- Language: PHP
- License: Apache-2.0
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/giggsey-libphonenumber-for-php

## The problem: phone numbers are not strings

A phone number typed by a user is a string. A phone number you can validate, format and dial is a structure with a country code, a national number, an optional extension and a flag for an Italian leading zero. The gap between the two is where most registration forms and CRM imports break.

The library exists to close that gap in PHP. It is a port of Google's libphonenumber, and the README describes it as a PHP library for parsing, formatting, storing and validating international phone numbers. The audience is PHP developers who accept numbers from more than one country, or who need to display them back in a format the user recognises rather than the one the user typed.

The port is not a reimplementation of the algorithms from a specification. It is tied to Google's data, and the versioning section says the library tries to follow the same version numbers as Google, with extra releases when a critical fix cannot wait. That is a deliberate coupling: metadata changes arrive on Google's schedule, not on a PHP release cycle.

## How the parsing and formatting pipeline works

The entry point is a singleton. PhoneNumberUtil::getInstance() returns the shared utility object, and parse() takes the raw string plus a default region code. The region matters only when the number is not written in international form; a Swiss national number parsed with a region of CH resolves to country code 41, and the README shows the resulting PhoneNumber object with its private fields: countryCode, nationalNumber, extension, italianLeadingZero, rawInput, countryCodeSource and preferredDomesticCarrierCode.

Validation is a separate call. isValidNumber() takes that parsed object and returns a boolean, so parsing and validating are not the same operation and the library does not fold them together. Formatting then takes the same object plus a PhoneNumberFormat constant: E164, NATIONAL or INTERNATIONAL. There is also formatOutOfCountryCallingNumber(), which needs the region the call is placed from, because the trunk prefix differs. The README's example formats the same Swiss number as 011 41 44 668 1800 from the United States and as 00 41 44 668 18 00 from Great Britain.

Around that core sit several optional components, each documented in its own file under docs/: PhoneNumberOfflineGeocoder for a place description, PhoneNumberToCarrierMapper, PhoneNumberToTimeZonesMapper, PhoneNumberMatcher for finding numbers inside free text, and AsYouTypeFormatter for incremental input. The geocoder returns descriptions in the language you ask for, which is why the same Swiss number comes back as Zurich, Zürich or Zurigo depending on the locale string passed in. That is a lookup against bundled metadata, not a network call.

## Installing it and parsing your first number

The README states that PHP versions 8.1 to 8.5 are supported and that the PECL mbstring extension is required. Composer is the recommended route:

```bash
composer require giggsey/libphonenumber-for-php
```

If you do not use Composer, the README says to use any PSR-4 compliant autoloader and to load dependencies such as giggsey/locale yourself. After the install, parsing a national number works like this:

```php
$swissNumberStr = "044 668 18 00";
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
try {
    $swissNumberProto = $phoneUtil->parse($swissNumberStr, "CH");
    var_dump($swissNumberProto);
} catch (\libphonenumber\NumberParseException $e) {
    var_dump($e);
}
```

What you should see is a PhoneNumber object carrying countryCode 41 and nationalNumber 446681800, with the remaining fields null. Note the exception type: parse failures are signalled with NumberParseException, not with a null return, so the try block is not optional in practice.

Validation and formatting are two further calls on the same object:

```php
$isValid = $phoneUtil->isValidNumber($swissNumberProto);
var_dump($isValid); // true

echo $phoneUtil->format($swissNumberProto, \libphonenumber\PhoneNumberFormat::E164);
// Produces "+41446681800"
echo $phoneUtil->format($swissNumberProto, \libphonenumber\PhoneNumberFormat::NATIONAL);
// Produces "044 668 18 00"
```

If you only need the core utility, the README points at giggsey/libphonenumber-for-php-lite, which it describes as offering a much smaller package size. The trade is that the geocoder, carrier mapper and timezone mapper live in the full package.

## Where the library will not save you

The FAQ in the README addresses problems with invalid numbers, and that framing is honest about the boundary: validation is a metadata question. A number can be well-formed and still be rejected because the range was not allocated, and a number can pass isValidNumber() and still be disconnected. The library tells you whether a number fits the numbering plan, not whether anyone answers it.

The metadata is also a snapshot. New ranges appear, countries change plans, and the bundled data only moves when you upgrade the package. A pinned version in composer.json is a pinned picture of the numbering world, and the README does not describe a mechanism for fetching updated metadata at runtime. If your application depends on freshly allocated ranges, the upgrade cadence is your problem, not the library's.

Geocoding has a similar ceiling. The descriptions are offline lookups, so they are as coarse as the underlying data: a country-level answer for a mobile number is normal, and the README's own examples show a city for a fixed line and a country name for a US number in one locale. Treating the geocoder as a precise location service will disappoint. It is a display aid.

Finally, the package is not small. The lite variant exists precisely because the full one carries the geocoder, carrier and timezone data. If your use case is a single-country form, a regular expression plus a length check is a fraction of the weight, and the library will not add much beyond what you already know about that one plan.

## How it differs from libphonenumber-js

The obvious alternative for a web stack is libphonenumber-js, which is the same idea in JavaScript rather than a port of the Java original. The difference is not only the language. In a PHP application, giggsey/libphonenumber-for-php runs on the server, so parsing and validation happen once, in the same process that writes to your database, and the browser never needs the metadata bundle. libphonenumber-js can run in the browser, which means the metadata travels to the client and the validation happens there.

That changes where the data lives. With the PHP library you can normalise every number to E164 before it reaches storage and keep one canonical column. With a client-side library you either duplicate the metadata or accept that the server still has to re-validate, because client-side checks are not a security boundary.

There is also the versioning question. The PHP port inherits Google's version numbers, so a bump from 9.0.38 to 9.0.39 tracks a Google release rather than a PHP API change. The README warns that this means the project may not follow Semantic Versioning and that major version jumps may not contain backwards incompatible changes, so release notes are the thing to read before upgrading. A JavaScript library with its own release cadence gives you a more conventional dependency story, at the cost of a separate metadata pipeline.

## Maintenance, licence and the cost of upgrading

The repository is not archived, and the last push was on 2026-09-10, the same day as the 9.0.39 release. Releases 9.0.38 and 9.0.37 landed on 2026-09-01 and 2026-08-15, so the cadence tracked over those three releases is roughly two to four weeks. That is consistent with a project whose data source ships on its own schedule.

Upgrade cost is dominated by the versioning policy rather than by API churn. Because version numbers follow Google, the README tells readers to check release notes for major jumps instead of assuming breakage. The PHP version policy is the part that can surprise you: the README states the library will be updated to use supported versions of PHP without major version bumps. A PHP upgrade in your infrastructure can therefore require a library upgrade that does not look like one.

The code is Apache-2.0, which is a permissive licence with an explicit patent grant and a requirement to preserve notices. The metadata is the part worth thinking about rather than the PHP source: it is derived from Google's data, and the README does not spell out the terms attached to the data files. If you redistribute the package or ship the metadata inside a product, that is the question to put to whoever handles licensing on your side. Nothing here is legal advice.

## Conclusion

Adopt it if you store or display phone numbers for more than one country and want Google's metadata without leaving PHP; the composer require giggsey/libphonenumber-for-php install and the lite package both keep the footprint manageable. Skip it if you only ever handle one domestic format, or if you need a licence check on the metadata rather than the code. Before shipping, verify that the version you pin matches the Google release you expect, since the library follows Google's numbering and not Semantic Versioning.

## FAQ

### How do I format a phone number in PHP with giggsey/libphonenumber-for-php?

Parse the string with PhoneNumberUtil::getInstance()->parse() and a default region, then call format() with a PhoneNumberFormat constant such as E164, NATIONAL or INTERNATIONAL. The README's Swiss example produces "+41446681800" for E164 and "044 668 18 00" for NATIONAL.

### What is giggsey/libphonenumber-for-php used for?

It parses, formats, stores and validates international phone numbers in PHP, and it is based on Google's libphonenumber. Optional components documented in docs/ add offline geocoding, carrier mapping, timezone mapping, number matching in text and as-you-type formatting.

### Who maintains giggsey/libphonenumber-for-php?

The repository is under the giggsey GitHub account and is not archived; the last push was on 2026-09-10, alongside the 9.0.39 release. The README does not name individual maintainers.

### Is giggsey/libphonenumber-for-php open source?

Yes. The repository is licensed Apache-2.0, and the README recommends installing it through Composer with the package name giggsey/libphonenumber-for-php.

## Sources

- [giggsey/libphonenumber-for-php on GitHub](https://github.com/giggsey/libphonenumber-for-php)
- [License: Apache-2.0](https://github.com/giggsey/libphonenumber-for-php/blob/master/LICENSE)
- [Project website](https://giggsey.com/libphonenumber/)
- [README](https://github.com/giggsey/libphonenumber-for-php/blob/master/README.md)
- [Releases](https://github.com/giggsey/libphonenumber-for-php/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/giggsey-libphonenumber-for-php
