Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dec84fa
update phpunit tests so they run correctly
matthewmcvickar Sep 20, 2026
7222fd2
migrate outdated XML configuration
matthewmcvickar Sep 21, 2026
299497c
ignore phpunit cache directory
matthewmcvickar Sep 21, 2026
ac98229
update to latest version of PHPUnit
matthewmcvickar Sep 21, 2026
54529fa
fix Unit regex to handle multi-word Places
matthewmcvickar Sep 21, 2026
14f554c
write tests to support Unit regex updates
matthewmcvickar Sep 21, 2026
f95013b
fix erroneous usage of 'Place' instead of 'City'
matthewmcvickar Sep 21, 2026
dc82658
add tests for when the Unit has a number and not
matthewmcvickar Sep 21, 2026
2d70080
expand test to cover and explain edge cases
matthewmcvickar Sep 21, 2026
4d20ab1
consistent capitalization
matthewmcvickar Sep 21, 2026
890831d
use DataProviders for PHPUnit tests
matthewmcvickar Sep 21, 2026
ad88d43
remove and ignore composer.lock file
matthewmcvickar Sep 21, 2026
c276f24
Merge branch 'main' of github.com:matthewmcvickar/address-normalization
matthewmcvickar Sep 21, 2026
41db98c
update GitHub Action
matthewmcvickar Sep 21, 2026
899da9a
remove unnecessary `composer update` flag
matthewmcvickar Sep 21, 2026
3e328f9
improve comments
matthewmcvickar Sep 21, 2026
789b69c
also remove `autoload-dev` from Composer config
matthewmcvickar Sep 21, 2026
1c37060
more useful to _not_ fail fast
matthewmcvickar Sep 21, 2026
f53b1be
validate Composer configuration
matthewmcvickar Sep 21, 2026
8e00e7c
user setup-php's `tools` support for Composer
matthewmcvickar Sep 21, 2026
a108af4
improve comments
matthewmcvickar Sep 21, 2026
534e910
support PHP >=8.2
matthewmcvickar Sep 21, 2026
62dcfe2
restore perfectly good existing code
matthewmcvickar Sep 21, 2026
c23724c
use `composer install`, not `composer update`
matthewmcvickar Sep 21, 2026
116abf6
use assoc arrays for DataProviders
matthewmcvickar Sep 21, 2026
c0d838f
consistent code style
matthewmcvickar Sep 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 13 additions & 18 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,38 @@ name: PHP Composer

on:
push:
branches: ["main"]
branches: ['main']
pull_request:
branches: ["main"]
branches: ['main']

permissions:
contents: read

jobs:
build:
name: PHP ${{ matrix.php-version }}
runs-on: ubuntu-latest

strategy:
fail-fast: true
fail-fast: false
matrix:
php: [8.0, 8.1, 8.2]
php-version: ['8.2', '8.3', '8.4', '8.5']

steps:
- uses: actions/checkout@v3
- name: Check out repository
uses: actions/checkout@v7

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
php-version: ${{ matrix.php-version }}
tools: composer

- name: Validate composer.json and composer.lock
- name: Validate Composer configuration
run: composer validate --strict

- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v3
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-

- name: Install dependencies
run: composer install --prefer-dist --no-progress
run: composer install --no-progress --no-interaction

- name: Run test suite
run: composer run-script test
8 changes: 5 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.idea/*
.DS_Store
/nbproject/
.idea/*
.phpunit.cache
.phpunit.result.cache
vendor
/nbproject/
composer.lock
vendor
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Basic Address Normalizer

[![Build Status](https://travis-ci.org/zerodahero/address-normalization.svg?branch=master)](https://travis-ci.org/zerodahero/address-normalization)

## Purpose
Expand Down Expand Up @@ -108,6 +109,7 @@ $address->toArray();
```

### Hashing

If you only need to make use of a consistent way of hashing (e.g. if you're starting with a dependable 5-part address, such as from a 3rd party service), you can build a `SimpleAddress`.

```php
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
}
},
"require": {
"php": ">=8.0"
"php": ">=8.2"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
"phpunit/phpunit": "^11|^12|^13"
},
"scripts": {
"test": "phpunit"
Expand Down
22 changes: 6 additions & 16 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
convertDeprecationsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Normalizer">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<phpunit backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<testsuites>
<testsuite name="Normalizer">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
</phpunit>
24 changes: 23 additions & 1 deletion src/Normalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,29 @@ private function setupRegularExpressions()

$this->zip_regexp = '(\d{5})(?:-?(\d{4})?)';
$this->corner_regexp = '(?:\band\b|\bat\b|&|\@)';
$this->unit_regexp = '(?:(su?i?te|p\W*[om]\W*b(?:ox)?|dept|apt|apartment|ro*m|fl|unit|box)\W+|\#\W*|)([\w-]+)';

/*
* Unit pattern. A 'unit' is an apartment, suite, etc.
*
* There are two capture groups: the unit prefix and the unit value.
*
* The unit prefix can be:
* 1. A word from a set of acceptable words (e.g., 'Apt' or 'Apartment'
* or 'Ste') followed by whitespace or punctuation.
* 2. A pound sign (#) followed by whitespace or punctuation.
* 3. Missing (no prefix), but only if the unit value contains a number.
*
* The requirement for a number in the unit value allows us to detect
* the end of the street address and the beginning of the city when the
* user doesn't add commas between address segments or when the city is
* a multi-word name like 'Los Angeles.'
*/
$this->unit_regexp = '(?:'
. '(su?i?te|p\W*[om]\W*b(?:ox)?|dept|apt|apartment|ro*m|fl|unit|box)\W+'
. '|\#\W*'
. '|(?=[\w-]*\d)'
. ')([\w-]+)';

$this->street_regexp =
'(?:'
. '(?:(' . $this->direct_regexp . ')\W+'
Expand Down
149 changes: 115 additions & 34 deletions tests/NormalizerTest.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
<?php

/*
NOTE:
This file use both the docblock syntax *and* attribute syntax for DataProviders.
Because this repository supports PHP >=8.2, it supports PHPUnit 11, 12, and 13.
The DataProviders syntax was changed in v12. (Using both causes no problems, as
the docblock syntax is ignored as of v12.)
*/

namespace Tests;

use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\DataProvider;
use ZeroDaHero\Normalizer;
use ZeroDaHero\Address;
use ZeroDaHero\SimpleAddress;
Expand Down Expand Up @@ -45,20 +54,19 @@ public function testReturnsFalseOnBadFivePartStrict()
$this->assertFalse($address);
}

public function normalizesAddressesDataProvider()
{
public static function normalizesAddressesDataProvider() {
return [
[
'1234 Main St. SE, Minneapolis, MN 55401',
'1234 Main Street Southeast, Minneapolis, MN 55401'
'1234 Main Street Southeast, Minneapolis, MN 55401',
'1234 Main St. SE, Minneapolis, MN 55401'
],
[
'1234 Main St. SE, Minneapolis, MN 55401',
'1234 Main St SE, Minneapolis, Minnesota 55401'
'1234 Main St SE, Minneapolis, Minnesota 55401',
'1234 Main St. SE, Minneapolis, MN 55401'
],
[
'1234 Main St. SE, Minneapolis, MN 55401',
'1234 Main St southeast, Minneapolis, Minnesota 55401'
'1234 Main St southeast, Minneapolis, Minnesota 55401',
'1234 Main St. SE, Minneapolis, MN 55401'
],
];
}
Expand All @@ -67,18 +75,18 @@ public function normalizesAddressesDataProvider()
* @test
* @dataProvider normalizesAddressesDataProvider
Comment thread
matthewmcvickar marked this conversation as resolved.
*/
public function testNormalizesAddresses($firstAddress, $secondAddress)
#[DataProvider('normalizesAddressesDataProvider')]
public function testNormalizesAddresses($test, $expected_result)
{
$normalizer = new Normalizer();

$this->assertEquals(
(string)$normalizer->parse($firstAddress),
(string)$normalizer->parse($secondAddress)
(string)$normalizer->parse($expected_result),
(string)$normalizer->parse($test)
);
}

public function badAddressesDataProvider()
{
public static function badAddressesDataProvider() {
return [
'double unit no commas' => ['1234 Main St. SE Unit 101 Unit 101'],
'double unit mismatch comma' => ['1234 Main St. SE, Unit 101 Apt 101, Minneapolis, MN 55555'],
Expand All @@ -91,54 +99,127 @@ public function badAddressesDataProvider()
* @test
* @dataProvider badAddressesDataProvider
*/
public function testFailsOnBadAddresses($badAddress)
#[DataProvider('badAddressesDataProvider')]
public function testFailsOnBadAddresses($address)
{
$normalizer = new Normalizer();

$this->assertFalse($normalizer->parse($badAddress));
$this->assertFalse($normalizer->parse($address));
}

/** @test */
public function testHandlesAddressWithoutUnitPrefix()
{
$normalizer = new Normalizer();

$addresses = [
[ // Test without unit prefix
public static function addressesWithoutUnitPrefixDataProvider() {
return [
'Test without unit prefix' => [
'test' => '1234 W Main Avenue 1W, Chicago, IL, 60647',
'expected_result' => '1234 W Main Ave #1W, Chicago, IL 60647'
],
[ // Regression test with unit prefix
'Regression test with "Unit" unit prefix' => [
'test' => '1234 W Main Avenue Unit 1W, Chicago, IL, 60647',
'expected_result' => '1234 W Main Ave Unit 1W, Chicago, IL 60647'
],
[ // Regression test with unit prefix
'Regression test with "Apartment" unit prefix' => [
'test' => '1234 W Main Avenue Apartment 1W, Chicago, IL, 60647',
'expected_result' => '1234 W Main Ave Apartment 1W, Chicago, IL 60647'
],
[ // Regression test with unit prefix
'Regression test with "#" unit prefix' => [
'test' => '1234 W Main Avenue #1W, Chicago, IL, 60647',
'expected_result' => '1234 W Main Ave #1W, Chicago, IL 60647'
],
[ // Regression test with unit prefix
'Regression test with "Room" unit prefix' => [
'test' => '1234 W Main Avenue Room 1, Chicago, IL, 60647',
'expected_result' => '1234 W Main Ave Room 1, Chicago, IL 60647'
],
[ // Regression test with unit prefix
'Regression test with "Apt" unit prefix' => [
'test' => '1234 W Main Avenue Apt 1W, Chicago, IL, 60647',
'expected_result' => '1234 W Main Ave Apt 1W, Chicago, IL 60647'
],
[ // Regression test without any unit
'Regression test without any unit' => [
'test' => '1234 W Main Street, Chicago, IL, 60647',
'expected_result' => '1234 W Main St, Chicago, IL 60647'
],
];
}

/**
* @test
* @dataProvider addressesWithoutUnitPrefixDataProvider
*/
#[DataProvider('addressesWithoutUnitPrefixDataProvider')]
public function testHandlesAddressWithoutUnitPrefix($test, $expected_result)
{
$normalizer = new Normalizer();

foreach ($addresses as $address) {
$this->assertEquals(
$address['expected_result'],
(string)$normalizer->parse($address['test'])
);
}
$this->assertEquals(
$expected_result,
(string)$normalizer->parse($test)
);
}

public static function addressesWithMultiWordCityDataProvider() {
return [
'Two-word city; without unit; with commas' => [
'test' => '123 Main Street, Los Angeles, CA 90012',
'expected_result' => '123 Main St, Los Angeles, CA 90012',
],
'Three-word city; without unit; with commas' => [
'test' => '123 Main Street, San Luis Obispo, CA 93405',
'expected_result' => '123 Main St, San Luis Obispo, CA 93405',
],
'Multi-word city; without unit; without commas' => [
'test' => '123 Main Street Los Angeles CA 90012',
'expected_result' => '123 Main St, Los Angeles, CA 90012',
],
'Multi-word city; with unit prefix; with commas' => [
'test' => '123 Main Street Apt 14A, Los Angeles, CA 90012',
'expected_result' => '123 Main St Apt 14A, Los Angeles, CA 90012',
],
'Multi-word city; without unit prefix; with commas' => [
'test' => '123 Main Street 1A, Los Angeles, CA 90012',
'expected_result' => '123 Main St #1A, Los Angeles, CA 90012',
],
'Multi-word city; with unit prefix; without commas' => [
'test' => '123 Main Street Apt 14, Los Angeles, CA 90012',
'expected_result' => '123 Main St Apt 14, Los Angeles, CA 90012',
],
'Multi-word city; without unit prefix; without commas' => [
'test' => '123 Main Street 1A Los Angeles CA 90012',
'expected_result' => '123 Main St #1A, Los Angeles, CA 90012',
],
'Multi-word city; without unit prefix; unit is only a number; without commas' => [
'test' => '123 Main Street 1 Los Angeles CA 90012',
'expected_result' => '123 Main St #1, Los Angeles, CA 90012',
],

// This is an edge case where we can't tell whether the 'A' is part
// of the street name or a unit. Since there's a comma, we assume
// it's part of the street.
'Multi-word city; without unit prefix; unit is only a letter; with commas' => [
'test' => '123 Main Street A, Los Angeles, CA 90012',
'expected_result' => '123 Main Street A, Los Angeles, CA 90012',
],

// This is an edge case where we can't tell whether the 'A' is part
// of the street or the city. Since there's no comma and we've
// found 'street,' we assume it's part of the city.
'Multi-word city; without unit prefix; unit is only a letter; without commas' => [
'test' => '123 Main Street A Los Angeles CA 90012',
'expected_result' => '123 Main St, A Los Angeles, CA 90012',
],
];
}

/**
* @test
* @dataProvider addressesWithMultiWordCityDataProvider
*/
#[DataProvider('addressesWithMultiWordCityDataProvider')]
public function testHandlesAddressWithMultiWordCity($test, $expected_result)
{
$normalizer = new Normalizer();

$this->assertEquals(
$expected_result,
(string)$normalizer->parse($test)
);
}
}
Loading