Completed
Push — master ( 9ac2db...521986 )
by Yasir
02:54
created

GeoIPLocation::setMockHandler()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
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
124
        return new Client();
125
    }
126
127
    /**
128
     * Return request Url
129
     *
130
     * @param string $ipAddress Ip Address
131
     *
132
     * @return string
133
     */
134 5
    private function getRequestUrl(string $ipAddress): string
135
    {
136 5
        return \sprintf(
137 5
            '%s/%s/%s',
138 5
            ApiConfig::IP_API_BASE_URL,
139 5
            ApiConfig::RESPONSE_FORMAT,
140 5
            $ipAddress
141
        );
142
    }
143
144
    /**
145
     * Return options array for http request
146
     *
147
     * @return array
148
     */
149 5
    private function getRequestOptions(): array
150
    {
151
        return array(
152 5
            'timeout' => ApiConfig::TIMEOUT,
153 5
            'connect_timeout' => ApiConfig::CONNECTION_TIMEOUT,
154
            'headers' => null,
155
        );
156
    }
157
158
    /**
159
     * Return mapped Location Object
160
     *
161
     * @param ResponseInterface $response Response
162
     *
163
     * @return Location
164
     */
165 3
    private function getMappedLocation(ResponseInterface $response): Location
166
    {
167 3
        $data =  \json_decode($response->getBody());
168
169 3
        return (new Location())
170 3
            ->setStatus($this->getStatus($this->mapProperty($data, 'status')))
171 3
            ->setMessage($this->mapProperty($data, 'message'))
172 3
            ->setCity($this->mapProperty($data, 'city'))
173 3
            ->setCountry($this->mapProperty($data, 'country'))
174 3
            ->setCountryCode($this->mapProperty($data, 'countryCode'))
175 3
            ->setLatitude($this->mapProperty($data, 'lat'))
176 3
            ->setLongitude($this->mapProperty($data, 'lon'))
177 3
            ->setRegionCode($this->mapProperty($data, 'region'))
178 3
            ->setRegionName($this->mapProperty($data, 'regionName'))
179 3
            ->setTimezone($this->mapProperty($data, 'timezone'))
180 3
            ->setPostalCode($this->mapProperty($data, 'zip'));
181
    }
182
183
    /**
184
     * Return status
185
     *
186
     * @param string $status Status string
187
     *
188
     * @return bool
189
     */
190 3
    private function getStatus(string $status): bool
191
    {
192 3
        return ('success' === $status ? true : false);
193
    }
194
195
    /**
196
     * Return response phrase with corresponding http status code
197
     *
198
     * @param ResponseInterface $response Response
199
     *
200
     * @return string
201
     */
202 1
    private function getResponsePhrase(ResponseInterface $response): string
203
    {
204 1
        return \sprintf(
205 1
            'Request failed with response code: %d and response: %s',
206 1
            $response->getStatusCode(),
207 1
            $response->getReasonPhrase()
208
        );
209
    }
210
211
    /**
212
     * Handle error
213
     *
214
     * @param string $message Message
215
     *
216
     * @return Location
217
     */
218 2
    private function handleError(string $message): Location
219
    {
220 2
        return (new Location())
221 2
            ->setStatus(false)
222 2
            ->setMessage($message);
223
    }
224
225
    /**
226
     * Map object property
227
     *
228
     * @param \stdClass $object   Object
229
     * @param string $property Property
230
     *
231
     * @return null|string
232
     */
233 3
    private function mapProperty(\stdClass $object, string $property): ?string
234
    {
235 3
        if (isset($object->$property)) {
236 3
            return $object->$property;
237
        }
238
239 3
        return null;
240
    }
241
}
242