Passed
Pull Request — master (#1700)
by Struan
04:10
created

Postcode::postcodeFetchFromDb()   B

Complexity

Conditions 8
Paths 9

Size

Total Lines 23
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 8.0079

Importance

Changes 0
Metric Value
cc 8
eloc 20
c 0
b 0
f 0
nc 9
nop 1
dl 0
loc 23
rs 8.4444
ccs 19
cts 20
cp 0.95
crap 8.0079
1
<?php
2
namespace MySociety\TheyWorkForYou\Utility;
3
4
/**
5
 * Postcode Utilities
6
 *
7
 * Utility functions related to postcodes
8
 */
9
class Postcode
10
{
11
12
    /**
13
     * Postcode To Constituency
14
     */
15
16 4
    public static function postcodeToConstituency($postcode) {
17 4
        return self::postcodeToConstituenciesInternal($postcode, true);
18
    }
19
20
    /**
21
     * Postcode To Constituencies
22
     */
23
24 1
    public static function postcodeToConstituencies($postcode) {
25 1
        return self::postcodeToConstituenciesInternal($postcode, false);
26
    }
27
28
    /**
29
     * Postcode To Constituencies (Internal)
30
     *
31
     * @param boolean $mp_only
32
     */
33
34 5
    private static function postcodeToConstituenciesInternal($postcode, $mp_only) {
35 5
        global $last_postcode, $last_postcode_value;
36
37 5
        $postcode = preg_replace('#[^a-z0-9]#i', '', $postcode);
38 5
        $postcode = self::canonicalisePostcode($postcode);
39
40 5
        if ($last_postcode == $postcode) {
41 1
            $return_value = $mp_only ? $last_postcode_value['WMC'] : $last_postcode_value;
42 1
            twfy_debug ("TIME", "Postcode $postcode looked up last time, is " . ( is_array($return_value) ? implode(', ', $return_value) : $return_value ));
43 1
            return $return_value;
44
        }
45
46 5
        if (!validate_postcode($postcode)) {
47 1
            return '';
48
        }
49
50 4
        $ret = self::postcodeFetchFromDb($postcode);
51 4
        if (!$ret) {
52
            $ret = self::postcodeFetchFromMapit($postcode);
53
        }
54
55 4
        if (is_string($ret)) return $ret;
56
57 4
        $last_postcode = $postcode;
58 4
        $last_postcode_value = $ret;
59 4
        return $mp_only ? $ret['WMC'] : $ret;
60
    }
61
62
    /**
63
     * Fetch Postcode Information from DB
64
     *
65
     * @param string $postcode
66
     */
67
68 4
    private static function postcodeFetchFromDb($postcode) {
69 4
        $db = new \ParlDB;
70 4
        $q = $db->query('select name from postcode_lookup where postcode = :postcode', array(
71 4
            ':postcode' => $postcode
72 4
            ))->first();
73
74 4
        if ($q) {
75 4
            $name = $q['name'];
76 4
            $country = '';
77 4
            $parts = explode(';', $name);
78 4
            if (count($parts) > 1) {
79 2
                $country = $parts[0];
80 2
                $name = $parts[1];
81
            }
82 4
            $name = explode('|', $name);
83 4
            if ($country == 'W') {
84
                return array('WMC' => $name[0], 'WAC' => $name[1], 'WAE' => $name[2]);
85 4
            } elseif ($country == 'S' || count($name)==3) {
86 2
                return array('WMC' => $name[0], 'SPC' => $name[1], 'SPE' => $name[2]);
87 3
            } elseif ($country == 'N' || count($name)==2) {
88 2
                return array('WMC' => $name[0], 'NIE' => $name[1]);
89
            } else {
90 1
                return array('WMC' => $name[0]);
91
            }
92
        }
93
    }
94
95
    /**
96
     * Fetch Postcode Information from MapIt
97
     *
98
     * @param string $postcode
99
     */
100
101
    private static function postcodeFetchFromMapit($postcode) {
102
        if (!defined('OPTION_MAPIT_URL') || !OPTION_MAPIT_URL) {
103
            return '';
104
        }
105
        $filename = 'postcode/' . rawurlencode($postcode);
106
        $ch = curl_init(OPTION_MAPIT_URL . $filename);
107
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
108
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
109
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
110
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
111
        $file = curl_exec($ch);
112
        if (curl_errno($ch)) {
113
            $errno = curl_errno($ch);
114
            trigger_error("Postcode database: " . $errno . ' ' . curl_error($ch), E_USER_WARNING);
115
            return 'CONNECTION_TIMED_OUT';
116
        }
117
        curl_close($ch);
118
119
        $r = json_decode($file, true);
0 ignored issues
show
Bug introduced by
It seems like $file can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

119
        $r = json_decode(/** @scrutinizer ignore-type */ $file, true);
Loading history...
120
        if (!$r) {
121
            trigger_error("Postcode database is not working. Content:\n".$file.", request: ". $filename, E_USER_WARNING);
0 ignored issues
show
Bug introduced by
Are you sure $file of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

121
            trigger_error("Postcode database is not working. Content:\n"./** @scrutinizer ignore-type */ $file.", request: ". $filename, E_USER_WARNING);
Loading history...
122
            return '';
123
        }
124
        if (isset($r['error']) || !isset($r['areas'])) {
125
            return '';
126
        }
127
        $areas = array();
128
        foreach ($r['areas'] as $row) {
129
            if (in_array($row['type'], array('WMC', 'SPC', 'SPE', 'NIE', 'WAC', 'WAE')))
130
                $areas[$row['type']] = $row['name'];
131
        }
132
133
        if (!isset($areas['WMC'])) {
134
            return '';
135
        }
136
137
        # Normalise name - assume SP and NI are already so...
138
        $normalised = Constituencies::normaliseConstituencyName(strtolower($areas['WMC']));
139
        if ($normalised) {
140
            $areas['WMC'] = $normalised;
141
            if (isset($areas['SPC'])) {
142
                $serialized = "S;$areas[WMC]|$areas[SPC]|$areas[SPE]";
143
            } elseif (isset($areas['NIE'])) {
144
                $serialized = "N;$areas[WMC]|$areas[NIE]";
145
            } elseif (isset($areas['WAC'])) {
146
                $serialized = "W;$areas[WMC]|$areas[WAC]|$areas[WAE]";
147
            } else {
148
                $serialized = "E;$areas[WMC]";
149
            }
150
            $db = new \ParlDB;
151
            $db->query('replace into postcode_lookup values(:postcode, :serialized)',
152
                array(
153
                    ':postcode' => $postcode,
154
                    ':serialized' => $serialized
155
                ));
156
        } else {
157
            return '';
158
        }
159
160
        return $areas;
161
    }
162
163
    /**
164
     * Canonicalise Postcode
165
     *
166
     * Take a postcode and turn it into a tidied, uppercased canonical version.
167
     */
168
169 6
    public static function canonicalisePostcode($pc) {
170 6
        $pc = str_replace(' ', '', $pc);
171 6
        $pc = trim($pc);
172 6
        $pc = strtoupper($pc);
173 6
        $pc = preg_replace('#(\d[A-Z]{2})#', ' $1', $pc);
174 6
        return $pc;
175
    }
176
177
}
178
179