sirprize/postal-code-validator
A PHP library for validating postal codes against country-specific formats. Use it to check ZIP and postal codes in forms, APIs, and data imports, ensuring address data is correct. Lightweight, straightforward to integrate, and supports multiple countries.
Install the package and confirm the exact validator API in its README and src/Validator.php:
composer require sirprize/postal-code-validator
Typical usage is a country-code/postal-code check:
use Sirprize\PostalCodeValidator\Validator;
$validator = new Validator();
$validator->isValid('US', '90210'); // true
$validator->isValid('US', '9021'); // false
$validator->isValid('CA', 'K1A 0B1'); // true
There is no Laravel service provider or config file to publish. The first day-to-day use case is address validation in registration, checkout, or profile forms: validate the postal code only after the user has selected a country.
Start by wrapping it in a Laravel validation rule so the rest of the application does not depend directly on the package API.
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Sirprize\PostalCodeValidator\Validator;
class PostalCode implements ValidationRule
{
public function __construct(private ?string $country) {}
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$country = strtoupper(trim((string) $this->country));
$postalCode = trim((string) $value);
if ($country === '' || ! (new Validator())->isValid($country, $postalCode)) {
$fail('The :attribute is not valid for the selected country.');
}
}
}
Use it in a form request:
protected function prepareForValidation(): void
{
$this->merge([
'country' => strtoupper(trim((string) $this->country)),
'postal_code' => trim((string) $this->postal_code),
]);
}
public function rules(): array
{
return [
'country' => ['required', 'string', 'size:2'],
'postal_code' => [
'required',
'string',
'max:32',
new \App\Rules\PostalCode($this->input('country')),
],
];
}
Keep the package behind your own rule or service. This is a small package with a narrow API. A local PostalCode rule, PostalCodeValidator service, or value object lets you swap implementations later without touching every form request.
namespace App\Support;
use Sirprize\PostalCodeValidator\Validator;
class PostalCodeService
{
public function __construct(private Validator $validator) {}
public function isValid(?string $country, ?string $postalCode): bool
{
$country = strtoupper(trim((string) $country));
$postalCode = trim((string) $postalCode);
if ($country === '' || $postalCode === '') {
return false;
}
return $this->validator->isValid($country, $postalCode);
}
}
Bind it once if you want a shared validator instance:
// AppServiceProvider::register()
$this->app->singleton(\Sirprize\PostalCodeValidator\Validator::class);
$this->app->singleton(\App\Support\PostalCodeService::class);
Normalize country codes, not postal-code structure. Country input should be ISO 3166-1 alpha-2 and uppercase. Do not strip all spaces and hyphens from postal codes: many formats require them, for example Canadian K1A 0B1 and UK SW1A 1AA.
protected function prepareForValidation(): void
{
$this->merge([
'country' => strtoupper(trim((string) $this->country)),
'postal_code' => trim((string) $this->postal_code),
]);
}
Handle countries without postal codes explicitly. Do not make the postal-code field universally required. Make it conditionally required based on the selected country, and let the product decide whether the field is optional or hidden for countries that do not use postal codes.
'postal_code' => [
Rule::requiredIf(fn () => in_array($this->input('country'), ['US', 'CA', 'GB'], true)),
'nullable',
'string',
'max:32',
new PostalCode($this->input('country')),
],
Use it in bulk imports and data cleanup. Reuse one validator instance and collect invalid rows rather than failing on the first one.
$validator = app(\Sirprize\PostalCodeValidator\Validator::class);
$invalidRows = $rows->filter(function ($row) use ($validator) {
return ! $validator->isValid(
strtoupper(trim($row['country'])),
trim($row['postal_code'])
);
});
Pair it with address autocomplete, not as a replacement. Client-side address lookup can suggest valid addresses, but server-side postal-code validation should still run before saving. This package validates formatting, not deliverability.
Test the countries you actually support. Add a small dataset test for your target markets, including leading zeros, spaces, hyphens, lowercase input, and known invalid values.
it('validates postal codes by country', function (string $country, string $postalCode, bool $expected) {
expect((new \Sirprize\PostalCodeValidator\Validator())->isValid($country, $postalCode))
->toBe($expected);
})->with([
['US', '90210', true],
['US', '9021', false],
['CA', 'K1A 0B1', true],
['GB', 'SW1A 1AA', true],
]);
isValid() cannot tell whether a postal code actually exists or matches a real address. Treat it as a format check, not address verification.USA, United States, or lowercase us may not work. Normalize to uppercase ISO alpha-2 before calling the package, and validate the country field separately.null, empty string, and unsupported country values.How can I help you explore Laravel packages today?