A PHP 8.0+ addressing library, powered by CLDR and Google's address data.
Manipulates postal addresses, meant to identify a precise recipient location for shipping or billing purposes.
Features:
Address formats and subdivisions were initially generated from Google's Address Data Service, and are now owned and maintained by the library.
Also check out commerceguys/intl for CLDR-powered languages/currencies/number formatting.
The address interface represents a postal address, with getters for the following fields:
Field names follow the OASIS eXtensible Address Language (xAL) standard.
The interface makes no assumptions about mutability. The implementing application can extend the interface to provide setters, or implement a value object that uses either PSR-7 style with* mutators or relies on an AddressBuilder. A default address value object is provided that can be used as an example, or mapped by Doctrine (preferably as an embeddable).
The address format provides the following information:
The country provides the following information:
The subdivision provides the following information:
Subdivisions are hierarchical and can have up to three levels: Administrative Area -> Locality -> Dependent Locality.
use CommerceGuys\Addressing\AddressFormat\AddressFormatRepository;
use CommerceGuys\Addressing\Country\CountryRepository;
use CommerceGuys\Addressing\Subdivision\SubdivisionRepository;
$countryRepository = new CountryRepository();
$addressFormatRepository = new AddressFormatRepository();
$subdivisionRepository = new SubdivisionRepository();
// Get the country list (countryCode => name), in French.
$countryList = $countryRepository->getList('fr-FR');
// Get the country object for Brazil.
$brazil = $countryRepository->get('BR');
echo $brazil->getThreeLetterCode(); // BRA
echo $brazil->getName(); // Brazil
echo $brazil->getCurrencyCode(); // BRL
print_r($brazil->getTimezones());
// Get all country objects.
$countries = $countryRepository->getAll();
// Get the address format for Brazil.
$addressFormat = $addressFormatRepository->get('BR');
// Get the subdivisions for Brazil.
$states = $subdivisionRepository->getAll(['BR']);
foreach ($states as $state) {
$municipalities = $state->getChildren();
}
// Get the subdivisions for Brazilian state Ceará.
$municipalities = $subdivisionRepository->getAll(['BR', 'CE']);
foreach ($municipalities as $municipality) {
echo $municipality->getName();
}
Addresses are formatted according to the address format, in HTML or text.
Formats an address for display, always adds the localized country name.
use CommerceGuys\Addressing\Address;
use CommerceGuys\Addressing\Formatter\DefaultFormatter;
use CommerceGuys\Addressing\AddressFormat\AddressFormatRepository;
use CommerceGuys\Addressing\Country\CountryRepository;
use CommerceGuys\Addressing\Subdivision\SubdivisionRepository;
$addressFormatRepository = new AddressFormatRepository();
$countryRepository = new CountryRepository();
$subdivisionRepository = new SubdivisionRepository();
$formatter = new DefaultFormatter($addressFormatRepository, $countryRepository, $subdivisionRepository);
// Options passed to the constructor or format() allow turning off
// html rendering, customizing the wrapper element and its attributes.
$address = new Address();
$address = $address
->withCountryCode('US')
->withAdministrativeArea('CA')
->withLocality('Mountain View')
->withAddressLine1('1098 Alta Ave');
echo $formatter->format($address);
/** Output:
<p translate="no">
<span class="address-line1">1098 Alta Ave</span><br>
<span class="locality">Mountain View</span>, <span class="administrative-area">CA</span><br>
<span class="country">United States</span>
</p>
**/
Takes care of uppercasing fields where required by the format (to facilitate automated mail sorting).
Requires specifying the origin country code, allowing it to differentiate between domestic and international mail. In case of domestic mail, the country name is not displayed at all. In case of international mail:
use CommerceGuys\Addressing\Address;
use CommerceGuys\Addressing\Formatter\PostalLabelFormatter;
use CommerceGuys\Addressing\AddressFormat\AddressFormatRepository;
use CommerceGuys\Addressing\Country\CountryRepository;
use CommerceGuys\Addressing\Subdivision\SubdivisionRepository;
$addressFormatRepository = new AddressFormatRepository();
$countryRepository = new CountryRepository();
$subdivisionRepository = new SubdivisionRepository();
// Defaults to text rendering. Requires passing the "origin_country"
// (e.g. 'FR') to the constructor or to format().
$formatter = new PostalLabelFormatter($addressFormatRepository, $countryRepository, $subdivisionRepository, ['locale' => 'fr']);
$address = new Address();
$address = $address
->withCountryCode('US')
->withAdministrativeArea('CA')
->withLocality('Mountain View')
->withAddressLine1('1098 Alta Ave');
echo $formatter->format($address, ['origin_country' => 'FR']);
/** Output:
1098 Alta Ave
MOUNTAIN VIEW, CA 94043
ÉTATS-UNIS - UNITED STATES
**/
Address validation relies on the Symfony Validator library.
Checks performed:
use CommerceGuys\Addressing\Address;
use CommerceGuys\Addressing\Validator\Constraints\AddressFormatConstraint;
use CommerceGuys\Addressing\Validator\Constraints\CountryConstraint;
use Symfony\Component\Validator\Validation;
$address = new Address('FR');
$validator = Validation::createValidator();
// Validate the country code, then validate the rest of the address.
$violations = $validator->validate($address->getCountryCode(), new CountryConstraint());
if (!$violations->count()) {
$violations = $validator->validate($address, new AddressFormatConstraint());
}
Zones are territorial groupings often used for shipping or tax purposes. For example, a set of shipping rates associated with a zone where the rates become available only if the customer's address belongs to the zone.
A zone can match countries, subdivisions (states/provinces/municipalities), postal codes. Postal codes can also be expressed using ranges or regular expressions.
Examples of zones:
use CommerceGuys\Addressing\Address;
use CommerceGuys\Addressing\Zone\Zone;
// Create the German VAT zone (Germany and 4 Austrian postal codes).
$zone = new Zone([
'id' => 'german_vat',
'label' => 'German VAT',
'territories' => [
['country_code' => 'DE'],
['country_code' => 'AT', 'included_postal_codes' => '6691, 6991:6993'],
],
]);
// Check if the provided austrian address matches the German VAT zone.
$austrianAddress = new Address();
$austrianAddress = $austrianAddress
->withCountryCode('AT')
->withPostalCode('6992');
echo $zone->match($austrianAddress); // true