Completed
Push — master ( 521986...ee997e )
by Yasir
02:26
created

GeoIPLocation::handleError()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace ipGeolocation;
4
5
use GuzzleHttp\Client;
6
use GuzzleHttp\Exception\RequestException;
7
use GuzzleHttp\Handler\MockHandler;
8
use Psr\Http\Message\ResponseInterface;
9
10
/**
11
 * This class gets geo location information from ip address
12
 *
13
 * PHP version 7
14
 *
15
 * @author yasir khurshid <[email protected]>
16
 */
17
class GeoIPLocation
18
{
19
    /**
20
     * @var MockHandler
21
     */
22
    private $mockHandler;
23
24
    /**
25
     * Set mock handler (Unit testing purpose)
26
     *
27
     * @param MockHandler $mockHandler Mock handler
28
     *
29
     * @return GeoIPLocation
30
     */
31 5
    public function setMockHandler(MockHandler $mockHandler): GeoIPLocation
32
    {
33 5
        $this->mockHandler = $mockHandler;
34
35 5
        return $this;
36
    }
37
38
    /**
39
     * Indices for getting user IP Address
40
     *
41
     * @var array
42
     */
43
    private $remotes = array(
44
        'REMOTE_ADDR',
45
        'HTTP_X_FORWARDED_FOR',
46
        'HTTP_CLIENT_IP',
47
        'HTTP_X_FORWARDED',
48
        'HTTP_FORWARDED_FOR',
49
        'HTTP_FORWARDED',
50
        'HTTP_X_CLUSTER_CLIENT_IP',
51
    );
52
53
    /**
54
     * Return geo location data based on the user's Ip Address
55
     *
56
     * Example: ip-address => {"status": "success", "country": "COUNTRY", "countryCode": "COUNTRY CODE", "region": "REGION CODE"}
57
     *
58
     * @return Location
59
     */
60 5
    public function getGeoLocation()
61
    {
62
        try {
63 5
            $response = $this->getHttpClient()->get(
64 5
                $this->getRequestUrl($this->getIpAddress()),
65 5
                $this->getRequestOptions()
66
            );
67
68 4
            if (\in_array($response->getStatusCode(), range(200, 299))) {
69
70 3
                return $this->getMappedLocation($response);
71
            }
72
73 1
            return $this->handleError($this->getResponsePhrase($response));
74
75 1
        } catch (RequestException $e) {
76
77 1
            return $this->handleError($e->getMessage());
78
        }
79
    }
80
81
    /**
82
     * Return Ip address of the user
83
     *
84
     * @return string
85
     */
86 5
    public function getIpAddress(): string
87
    {
88 5
        foreach ($this->remotes as $remote) {
89 5
            if (isset($_SERVER[$remote])) {
90 5
                foreach (explode(',', $_SERVER[$remote]) as $ipAddress) {
91 5
                    if ($this->isIpAddressValid($ipAddress)) {
92 5
                        return $ipAddress;
93
                    }
94
                }
95
            }
96
        }
97
98 4
        return ApiConfig::DEFAULT_IP_ADDRESS;
99
    }
100
101
    /**
102
     * Check if ip address is valid or not
103
     *
104
     * @param string $ipAddress Ip Address
105
     *
106
     * @return bool
107
     */
108 5
    public function isIpAddressValid(string $ipAddress): bool
109
    {
110 5
        return (bool) \filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
111
    }
112
113
    /**
114
     * Return http client
115
     *
116
     * @return Client
117
     */
118 5
    private function getHttpClient(): Client
119
    {
120 5
        if ($this->mockHandler) {
121 5
            return new Client(array('handler' => $this->mockHandler));
122
        }
123
        // @codeCoverageIgnoreStart
124
        return new Client();
125
        // @codeCoverageIgnoreEnd
126
    }
127
128
    /**
129
     * Return request Url
130
     *
131
     * @param string $ipAddress Ip Address
132
     *
133
     * @return string
134
     */
135 5
    private function getRequestUrl(string $ipAddress): string
136
    {
137 5
        return \sprintf(
138 5
            '%s/%s/%s',
139 5
            ApiConfig::IP_API_BASE_URL,
140 5
            ApiConfig::RESPONSE_FORMAT,
141 5
            $ipAddress
142
        );
143
    }
144
145
    /**
146
     * Return options array for http request
147
     *
148
     * @return array
149
     */
150 5
    private function getRequestOptions(): array
151
    {
152
        return array(
153 5
            'timeout' => ApiConfig::TIMEOUT,
154 5
            'connect_timeout' => ApiConfig::CONNECTION_TIMEOUT,
155
            'headers' => null,
156
        );
157
    }
158
159
    /**
160
     * Return mapped Location Object
161
     *
162
     * @param ResponseInterface $response Response
163
     *
164
     * @return Location
165
     */
166 3
    private function getMappedLocation(ResponseInterface $response): Location
167
    {
168 3
        $data =  \json_decode($response->getBody());
169
170 3
        return (new Location())
171 3
            ->setStatus($this->getStatus($this->mapProperty($data, 'status')))
172 3
            ->setMessage($this->mapProperty($data, 'message'))
173 3
            ->setCity($this->mapProperty($data, 'city'))
174 3
            ->setCountry($this->mapProperty($data, 'country'))
175 3
            ->setCountryCode($this->mapProperty($data, 'countryCode'))
176 3
            ->setLatitude($this->mapProperty($data, 'lat'))
177 3
            ->setLongitude($this->mapProperty($data, 'lon'))
178 3
            ->setRegionCode($this->mapProperty($data, 'region'))
179 3
            ->setRegionName($this->mapProperty($data, 'regionName'))
180 3
            ->setTimezone($this->mapProperty($data, 'timezone'))
181 3
            ->setPostalCode($this->mapProperty($data, 'zip'));
182
    }
183
184
    /**
185
     * Return status
186
     *
187
     * @param string $status Status string
188
     *
189
     * @return bool
190
     */
191 3
    private function getStatus(string $status): bool
192
    {
193 3
        return ('success' === $status ? true : false);
194
    }
195
196
    /**
197
     * Return response phrase with corresponding http status code
198
     *
199
     * @param ResponseInterface $response Response
200
     *
201
     * @return string
202
     */
203 1
    private function getResponsePhrase(ResponseInterface $response): string
204
    {
205 1
        return \sprintf(
206 1
            'Request failed with response code: %d and response: %s',
207 1
            $response->getStatusCode(),
208 1
            $response->getReasonPhrase()
209
        );
210
    }
211
212
    /**
213
     * Handle error
214
     *
215
     * @param string $message Message
216
     *
217
     * @return Location
218
     */
219 2
    private function handleError(string $message): Location
220
    {
221 2
        return (new Location())
222 2
            ->setStatus(false)
223 2
            ->setMessage($message);
224
    }
225
226
    /**
227
     * Map object property
228
     *
229
     * @param \stdClass $object   Object
230
     * @param string $property Property
231
     *
232
     * @return null|string
233
     */
234 3
    private function mapProperty(\stdClass $object, string $property): ?string
235
    {
236 3
        if (isset($object->$property)) {
237 3
            return $object->$property;
238
        }
239
240 3
        return null;
241
    }
242
}
243