NetAcuity::_query()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 1
1
<?php
2
namespace DominionEnterprises\NetAcuity;
3
4
use DominionEnterprises\Util;
5
use Socket\Raw\Socket;
6
7
/**
8
 * A client to access a NetAcuity server for geoip lookup.
9
 */
10
final class NetAcuity
11
{
12
    /** @type \Socket\Raw\Socket The NetAcuity socket. */
13
    private $_socket;
14
15
    /** @type int The API Id identifying your client. */
16
    private $_apiId;
17
18
    /**
19
     * Create the NetAcuity client.
20
     *
21
     * @param \Socket\Raw\Socket $socket The NetAcuity socket.
22
     * @param int $apiId The API Id identifying your client.
23
     */
24
    public function __construct(Socket $socket, $apiId)
25
    {
26
        Util::throwIfNotType(['int' => $apiId]);
27
28
        $this->_socket = $socket;
29
        $this->_apiId = $apiId;
30
    }
31
32
    /**
33
     * Gets the geo data for the given IP.
34
     *
35
     * Failures will be raised as exceptions.
36
     *
37
     * @param string $ip The ip address to lookup
38
     *
39
     * @return array {
40
     *     @type string $country
41
     *     @type string $region
42
     *     @type string $city
43
     *     @type string $conn-speed
44
     *     @type string $metro-code
45
     *     @type string $latitude
46
     *     @type string $longitude
47
     *     @type string $postal-code
48
     *     @type string $country-code
49
     *     @type string $region-code
50
     *     @type string $city-code
51
     *     @type string $continent-code
52
     *     @type string $two-letter-country
53
     *     @type string $internal-code
54
     *     @type string $area-code
55
     *     @type string $country-conf
56
     *     @type string $region-conf
57
     *     @type string $city-conf
58
     *     @type string $postal-conf
59
     *     @type string $gmt-offset
60
     *     @type string $in-dist
61
     *     @type string $timezone-name
62
     * }
63
     */
64
    public function getGeo($ip)
65
    {
66
        Util::throwIfNotType(['string' => $ip], true);
67
68
        $response = $this->_query($this->_buildQuery(4, $ip));
69
        return $this->_parseResponse(
70
            $response,
71
            [
72
                'country',
73
                'region',
74
                'city',
75
                'conn-speed',
76
                'metro-code',
77
                'latitude',
78
                'longitude',
79
                'zip-code',
80
                'country-code',
81
                'region-code',
82
                'city-code',
83
                'continent-code',
84
                'two-letter-country',
85
                'internal-code',
86
                'area-code',
87
                'country-conf',
88
                'region-conf',
89
                'city-conf',
90
                'postal-conf',
91
                'gmt-offset',
92
                'in-dist',
93
                'timezone-name',
94
            ]
95
        );
96
    }
97
98
    /**
99
     * Builds the query to NetAcuity.
100
     *
101
     * @param int $databaseId The database id to query.
102
     * @param string $ip The ip address to lookup.
103
     *
104
     * @return string The formatted query string.
105
     */
106
    private function _buildQuery($databaseId, $ip)
107
    {
108
        Util::throwIfNotType(['string' => $ip, 'int' => $databaseId], true);
109
110
        return sprintf("%d;%d;%s\r\n", $databaseId, $this->_apiId, $ip);
111
    }
112
113
    /**
114
     * Executes the query against NetAcuity and returns the unformatted response.
115
     *
116
     * Failures will be raised as exceptions.
117
     *
118
     * @param string $query The formatted query string.
119
     *
120
     * @return string The response from NetAcuity.
121
     */
122
    private function _query($query)
123
    {
124
        Util::throwIfNotType(['string' => $query], true);
125
126
        $this->_socket->write($query);
127
128
        // Remove the first 4 bytes (size data) and the last 3 bytes (standard footer)
129
        $response = substr($this->_socket->read(1024), 4, -3);
130
131
        return $response;
132
    }
133
134
    /**
135
     * Parses the response into an array using the field definition.
136
     *
137
     * @param string $response The response from NetAcuity.
138
     * @param array $fields The expected fields in the response.
139
     *
140
     * @return array The response where the keys are from $fields and the values are from the $response.
141
     */
142
    private function _parseResponse($response, array $fields)
143
    {
144
        Util::throwIfNotType(['string' => $response], true);
145
        Util::throwIfNotType(['string' => $fields], true);
146
147
        $numberOfExpectedFields = count($fields);
148
149
        $responseData = explode(';', $response);
150
        $numberOfResponseFields = count($responseData);
151
        //Netacuity has a history of adding fields without notice
152
        //Ensure that there are at least numberOfFields values returned.
153
        Util::ensure(
154
            true,
155
            $numberOfResponseFields >= $numberOfExpectedFields,
156
            '\\UnexpectedValueException',
157
            ["Net acuity returned less than {$numberOfExpectedFields} fields"]
158
        );
159
160
        return array_combine($fields, array_slice($responseData, 0, $numberOfExpectedFields));
161
    }
162
}
163