Smart, opinionated parsing of single‑line US postal addresses into structured components for Laravel.
Laravel Address Parser helps you take messy user‑submitted single line US addresses and split them into reliable fields: address1, address2 (unit), city, state, zip and optionally county. It applies pragmatic heuristics—street suffix detection, unit indicator extraction, normalization (whitespace + periods), and validation of state + ZIP formats—without depending on external APIs. Ideal for form ingestion, ETL pipelines, quick data cleanup, and pre‑validation before geocoding.
Not a USPS CASS certified normalizer. It doesn't validate that an address physically exists; it simply parses and validates format.
- Features
- Requirements
- Installation
- Quick Start
- Usage Examples
- Facade, DI & Helper Patterns
- Parsing Logic & Heuristics
- County Parsing
- Validation Utilities
- Formatting Addresses
- Configuration
- Error Handling
- Edge Cases & Limitations
- Testing
- Performance
- Roadmap
- Contributing
- Security
- Support Us
- Credits
- License
- ✅ Parse single‑line US addresses with or without commas
- ✅ Detect and extract unit indicators (
APT,STE,#12,FLOOR, etc.) - ✅ Supports ZIP (5‑digit) and ZIP+4 (extended) formats
- ✅ Validates US state abbreviations (full 50 + DC)
- ✅ Optional county parsing via comma‑separated form
- ✅ Normalizes periods after abbreviations (
St.→St), collapses excess whitespace - ✅ Graceful formatting back to a one‑line address
- ✅ Clear, typed exception (
AddressParsingException) for invalid cases - ✅ Simple, framework‑friendly (pure static methods), zero external API calls
- ✅ Battle‑tested with Pest test suite
| Component | Version | ||||
|---|---|---|---|---|---|
| PHP | 7.4 or ^8.2 | ||||
| Laravel (illuminate/contracts) | ^10 | ^11 | ^12 | ||
| Extension dependencies | None beyond standard PHP + mbstring |
Install via Composer:
composer require awaisjameel/laravel-address-parserThe package auto‑discovers. No manual provider registration required.
Currently the config file is a placeholder for future tuning (custom suffixes, unit indicators, etc.). You can publish it now:
php artisan vendor:publish --tag="laravel-address-parser-config"Published file at config/address-parser.php:
return [
// Reserved for future customization: e.g. 'extra_street_suffixes' => [],
// 'extra_unit_indicators' => [],
];No migrations or views are shipped (ignore earlier generic template tags).
use Awaisjameel\LaravelAddressParser\LaravelAddressParser;
$parsed = LaravelAddressParser::parseAddressString('500 Elm Avenue Apt 4B Metropolis NY 10001');
/* Result:
[
'address1' => '500 Elm Avenue',
'address2' => 'APT 4B',
'city' => 'Metropolis',
'state' => 'NY',
'zip' => '10001',
'county' => null,
]
*/$parsed = LaravelAddressParser::parseAddressString('77 Broadway St #12 Gotham NJ 07001');
// address2 is '#12'$parsed = LaravelAddressParser::parseAddressString('1600 Pennsylvania Avenue NW, Washington, DC 20500-0003');$parsed = LaravelAddressParser::parseAddressStringWithCounty('123 Main St, Springfield, Greene, MO 65804');
// county => 'Greene'$parsed = LaravelAddressParser::parseAddressStringWithCounty('1600 Pennsylvania Avenue NW, Washington, DC 20500');
// county => null$formatted = LaravelAddressParser::formatAddress([
'address1' => '123 Main St',
'address2' => 'APT 4B',
'city' => 'Springfield',
'state' => 'IL',
'zip' => '62704',
]);
// "123 Main St APT 4B, Springfield, IL 62704"LaravelAddressParser::isValidState('TX'); // true
LaravelAddressParser::isValidZipCode('12345-6789'); // true
LaravelAddressParser::getValidStates(); // [ 'AL', 'AK', ... ]Because all methods are static, you can call the class directly. A facade alias LaravelAddressParser is registered; if you prefer the facade style:
use LaravelAddressParser; // Facade alias
$parsed = LaravelAddressParser::parseAddressString('500 Elm Avenue Apt 4B Metropolis NY 10001');Dependency injection is not required, but you can wrap this in your own service if you want to enforce non‑static boundaries for test isolation.
- Normalize input: trim, collapse whitespace, remove trailing punctuation, strip periods after known abbreviations.
- Extract trailing
STATE ZIPusing regex. Validate both. - Before the state/ZIP segment:
- If no commas: locate rightmost known street suffix, everything after → city.
- If commas: last comma part → city; remaining → address lines.
- Unit detection:
- Detect explicit unit parts after street suffix (e.g.
Apt 4B,#12). - Handles units embedded at end of
address1or separated by comma.
- Detect explicit unit parts after street suffix (e.g.
- Normalizes unit casing (except leading
#). - Returns structured array including a nullable
countywhen parsed via county method.
Recognized street suffixes (subset): ST, AVE, RD, DR, LN, CT, CIR, BLVD, PKWY, TRAIL, HWY, WAY, PL, LOOP, TER, EXPY, etc.
Recognized unit indicators: APT, SUITE, STE, UNIT, FLOOR, ROOM, BLDG, #, LOT, SPACE, etc.
Use parseAddressStringWithCounty() for either format:
Street, City, County, ST ZIPStreet, City, ST ZIP(county omitted →county => null)
If fewer than 3 comma‑separated parts exist (excluding state/ZIP) an exception is thrown.
| Method | Purpose |
|---|---|
isValidZipCode(string $zip) |
5‑digit or ZIP+4 pattern 12345 / 12345-6789 |
isValidState(string $state) |
Valid two‑letter US state (inclusive of DC) |
getValidStates() |
Returns internal list of state abbreviations |
These do not consult external APIs; they are format checks only.
formatAddress(array $components): string builds a single line string from parsed parts. Missing address2 is skipped; missing address1 results in City, ST ZIP only.
$oneLine = LaravelAddressParser::formatAddress($parsed);Currently no runtime options are exposed. Future versions may allow:
- Extending street suffix list
- Extending unit indicator list
- Custom validation strategy or USPS API integration hooks
Feel free to open an issue with your use case.
Parsing failures throw Awaisjameel\LaravelAddressParser\AddressParsingException with a human‑readable message. Common triggers:
- Empty or "0" input
- Cannot locate valid trailing
STATE ZIP - Invalid state abbreviation (e.g.
ZZ) - Invalid ZIP format
- Missing city segment
- Cannot identify street suffix when required
Always wrap user‑submitted data:
try {
$parsed = LaravelAddressParser::parseAddressString($raw);
} catch (AddressParsingException $e) {
// Log, show validation error, fallback, etc.
}| Scenario | Behavior |
|---|---|
| Missing street suffix & no commas | Throws exception |
| Excess whitespace | Normalized |
Periods after abbreviations (St.) |
Removed |
Mixed case units (aPt 4b) |
Uppercased →APT 4B |
Standalone #12 unit |
Preserved exactly |
| Non‑US addresses | Likely rejected (state + ZIP fail) |
| Addresses without house number | Usually rejected unless suffix detection passes heuristics |
| PO Boxes | Parsed as street if suffix logic permits (e.g.PO BOX 123) |
Not a full canonicalizer: it won't expand NW to Northwest, or validate delivery points.
Run the full test suite (Pest):
composer testOr with coverage:
composer test-coverageStatic analysis:
composer analyseCode style (Laravel Pint):
composer formatAll operations are in‑memory string functions & a few regex matches. Suitable for real‑time form handling. For bulk ETL (hundreds of thousands of rows) you can batch process safely; memory footprint is minimal.
Micro‑optimizations (e.g. caching compiled regex) are intentionally deferred until a real hotspot is demonstrated.
- Optional configuration for custom suffix/unit lists
- USPS address standardization adapter
- Geocoding integration hooks
- Bulk parsing helper with per‑record error collection
- Locale expansion (Canadian, UK parsing) via strategy interfaces
Have a request? Open an issue.
Contributions are welcome! Please:
- Fork & create a feature branch
- Add/adjust tests for new behavior
- Run:
composer analyse,composer format,composer test - Open a PR describing rationale & trade‑offs
For architectural changes, open an issue first for discussion.
If you discover a security vulnerability (e.g. pathological regex input leading to DoS), please email the author or open a private advisory. Avoid posting exploits publicly until a fix is released.
This library does not execute external processes or perform network calls, and stores no secrets.
If you find this package useful, consider supporting its development make sure to give a star on GitHub!
- awaisjameel
- Inspired by pragmatic parsing approaches in many OSS data-cleanup tools
Released under the MIT License. See LICENSE.md.
No. It validates format only. For existence use USPS, Smarty, Google, etc.
Only single‑line strings. Pre‑join lines ("address1 address2") before parsing, or use the county method if needed.
Low ceremony; no state. You can wrap them if you prefer dependency injection.
It covers common US street suffixes & unit indicators. You can extend in the future via config once exposed.
Not yet. Planned via config.
try {
$parsed = LaravelAddressParser::parseAddressString(request('address_line'));
// Persist
CustomerAddress::create([
'address1' => $parsed['address1'],
'address2' => $parsed['address2'],
'city' => $parsed['city'],
'state' => $parsed['state'],
'zip' => $parsed['zip'],
'county' => $parsed['county'],
'raw' => request('address_line'),
]);
} catch (AddressParsingException $e) {
return back()->withErrors(['address_line' => $e->getMessage()]);
}Happy parsing! 📨