Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.83% covered (success)
95.83%
23 / 24
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
Country_Data
95.83% covered (success)
95.83%
23 / 24
50.00% covered (danger)
50.00%
1 / 2
15
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
6
 get_locations_for_postcode
93.75% covered (success)
93.75%
15 / 16
0.00% covered (danger)
0.00%
0 / 1
9.02
1<?php
2/**
3 * Object containing all postcode data for a specific country, with one method for querying.
4 *
5 * @package brianhenryie/bh-wc-postcode-address-autofill
6 */
7
8namespace BrianHenryIE\WC_Postcode_Address_Autofill\API;
9
10use stdClass;
11
12/**
13 * Construct via json object saved in /data/ folder, query with partial, exact or extended postcode.
14 */
15class Country_Data {
16
17    /**
18     * The max length that a match can be made for this country's postcode.
19     *
20     * E.g. US postcodes may be entered as nine characters but only match on the first five.
21     */
22    protected int $postcode_length;
23
24    /**
25     * A list, indexed by postcode, of state-city pairs in that postcode.
26     *
27     * @var array<string, array<Postcode_Location>>
28     */
29    protected array $postcode_locations = array();
30
31    /**
32     * Constructor
33     *
34     * @param stdClass $json JSON object from data directory.
35     */
36    public function __construct( stdClass $json ) {
37        if ( isset( $json->postcode_length ) ) {
38            $this->postcode_length = $json->postcode_length;
39        }
40        if ( isset( $json->postcode_locations ) ) {
41            foreach ( $json->postcode_locations as $postcode => $locations ) {
42                if ( ! isset( $this->postcode_locations[ $postcode ] ) ) {
43                    $this->postcode_locations[ (string) $postcode ] = array();
44                }
45                foreach ( $locations as $location ) {
46                    $this->postcode_locations[ (string) $location->postcode ][] = new Postcode_Location( $location );
47                }
48            }
49        }
50    }
51
52    /**
53     * Given a partial postcode, return all matches (e.g. 958xx),
54     * given a full postcode, return the match (e.g. 95819),
55     * given an overly specific postcode, droplast() until finding a match
56     *
57     * @param string $postcode The partial, complete, or extended postcode to match.
58     */
59    public function get_locations_for_postcode( string $postcode ): ?Postcode_Locations_Result {
60        if ( strlen( $postcode ) > $this->postcode_length ) {
61            $postcode = substr( $postcode, 0, $this->postcode_length );
62        }
63
64        $result = new Postcode_Locations_Result();
65
66        if ( isset( $this->postcode_locations[ $postcode ] ) ) {
67            foreach ( $this->postcode_locations[ $postcode ] as $location ) {
68                $result->append( $location );
69            }
70            return $result;
71        }
72
73        $match_length = strlen( $postcode ) - 1;
74        do {
75            $postcode = substr( $postcode, 0, $match_length );
76
77            foreach ( $this->postcode_locations as $postcode_index => $postcode_location ) {
78                if ( 0 === strpos( $postcode_index, $postcode ) ) {
79                    foreach ( $this->postcode_locations[ $postcode_index ] as $location ) {
80                        $result->append( $location );
81                    }
82                }
83            }
84            --$match_length;
85        } while ( 0 === $result->count() && $match_length > 1 );
86
87        return 0 !== $result->count() ? $result : null;
88    }
89}