Skip to content

Commit 480b2d5

Browse files
committed
Fix geocoder-php#267 - Geoips not working (api change)
1 parent d64808a commit 480b2d5

File tree

3 files changed

+238
-51
lines changed

3 files changed

+238
-51
lines changed

src/Geocoder/Provider/GeoIPsProvider.php

+71-12
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@
1313
use Geocoder\Exception\InvalidCredentialsException;
1414
use Geocoder\Exception\NoResultException;
1515
use Geocoder\Exception\UnsupportedException;
16+
use Geocoder\Exception\InvalidArgumentException;
17+
use Geocoder\Exception\QuotaExceededException;
1618
use Geocoder\HttpAdapter\HttpAdapterInterface;
1719

1820
/**
1921
* @author Andrea Cristaudo <[email protected]>
22+
* @author Arthur Bodera <[email protected]>
23+
* @link http://www.geoips.com/en/developer/api-guide
2024
*/
2125
class GeoIPsProvider extends AbstractProvider implements ProviderInterface
2226
{
@@ -25,6 +29,14 @@ class GeoIPsProvider extends AbstractProvider implements ProviderInterface
2529
*/
2630
const GEOCODE_ENDPOINT_URL = 'http://api.geoips.com/ip/%s/key/%s/output/json/timezone/true/';
2731

32+
const CODE_SUCCESS = '200_1'; // The following results has been returned.
33+
const CODE_NOT_FOUND = '200_2'; // No result set has been returned.
34+
const CODE_BAD_KEY = '400_1'; // Error in the URI - The API call should include a API key parameter.
35+
const CODE_BAD_IP = '400_2'; // Error in the URI - The API call should include a valid IP address.
36+
const CODE_NOT_AUTHORIZED = '403_1'; // The API key associated with your request was not recognized.
37+
const CODE_ACCOUNT_INACTIVE = '403_2'; // The API key has not been approved or has been disabled.
38+
const CODE_LIMIT_EXCEEDED = '403_3'; // The service you have requested is over capacity.
39+
2840
/**
2941
* @var string
3042
*/
@@ -87,36 +99,83 @@ public function getName()
8799
/**
88100
* @param string $query
89101
*
102+
* @throws QuotaExceededException
103+
* @throws NoResultException
104+
* @throws InvalidCredentialsException
105+
* @throws InvalidArgumentException
90106
* @return array
91107
*/
92108
protected function executeQuery($query)
93109
{
94110
$content = $this->getAdapter()->getContent($query);
95111

96112
if (null === $content || '' === $content) {
97-
throw new NoResultException(sprintf('Could not execute query %s', $query));
113+
throw new NoResultException(sprintf('Invalid response from GeoIPs server for query %s', $query));
98114
}
99115

100116
$json = json_decode($content, true);
101117

102-
$response = array_key_exists('response', $json) ? $json['response'] : $json;
103-
104-
if (!is_array($response) || !count($response)) {
105-
throw new NoResultException(sprintf('Could not execute query %s', $query));
118+
// Check if we've received an error:
119+
if (isset($json['error'])) {
120+
switch ($json['error']['code']) {
121+
case static::CODE_BAD_IP:
122+
throw new InvalidArgumentException('The API call should include a valid IP address.');
123+
case static::CODE_BAD_KEY:
124+
throw new InvalidCredentialsException('The API call should include a API key parameter.');
125+
case static::CODE_NOT_AUTHORIZED:
126+
throw new InvalidCredentialsException('The API key associated with your request was not recognized.');
127+
case static::CODE_ACCOUNT_INACTIVE:
128+
throw new InvalidCredentialsException('The API key has not been approved or has been disabled.');
129+
case static::CODE_LIMIT_EXCEEDED:
130+
throw new QuotaExceededException('The service you have requested is over capacity.');
131+
default:
132+
throw new NoResultException(sprintf(
133+
'GeoIPs error %s%s%s%s - query: %s',
134+
$json['error']['code'],
135+
isset($json['error']['status']) ? ', ' . $json['error']['status'] : '',
136+
isset($json['error']['message']) ? ', ' . $json['error']['message'] : '',
137+
isset($json['error']['notes']) ? ', ' . $json['error']['notes'] : '',
138+
$query
139+
));
140+
}
106141
}
107142

108-
if (!isset($response['status']) || 'Bad Request' == $response['status']) {
109-
throw new NoResultException(sprintf('Could not execute query %s', $query));
143+
// Check response format
144+
if (
145+
!is_array($json) ||
146+
empty($json) ||
147+
empty($json['response']) ||
148+
empty($json['response']['code'])
149+
) {
150+
throw new NoResultException(sprintf('Invalid response from GeoIPs server for query %s', $query));
110151
}
111152

112-
if ('Forbidden' === $response['status']) {
113-
if ('Limit Exceeded' === $response['message']) {
114-
throw new NoResultException(sprintf('Could not execute query %s', $query));
115-
}
153+
$response = $json['response'];
154+
155+
// Check response code
156+
switch ($response['code']) {
157+
case static::CODE_NOT_FOUND:
158+
throw new NoResultException();
159+
case static::CODE_SUCCESS;
160+
// everything is ok
161+
break;
162+
default:
163+
throw new NoResultException(sprintf(
164+
'GeoIPs returned unknown result code %s for query: %s',
165+
$response['code'],
166+
$query
167+
));
168+
break;
169+
}
116170

117-
throw new InvalidCredentialsException('API Key provided is not valid.');
171+
// Make sure that we do have proper result array
172+
if (empty($response['locations']) || !is_array($response['locations']) || empty($response['locations'][0])) {
173+
throw new NoResultException(sprintf('Invalid response from GeoIPs server for query %s', $query));
118174
}
119175

176+
// Pick the first location from the response
177+
$response = $response['locations'][0];
178+
120179
return array(array_merge($this->getDefaults(), array(
121180
'country' => '' === $response['country_name'] ? null : $response['country_name'],
122181
'countryCode' => '' === $response['country_code'] ? null : $response['country_code'],

tests/Geocoder/Tests/Provider/GeoIPsProviderTest.php

+164-39
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public function testGetGeocodedDataWithLocalhostIPv6()
8585

8686
/**
8787
* @expectedException \Geocoder\Exception\NoResultException
88-
* @expectedExceptionMessage Could not execute query http://api.geoips.com/ip/74.200.247.59/key/api_key/output/json/timezone/true/
88+
* @expectedExceptionMessage Invalid response from GeoIPs server for query http://api.geoips.com/ip/74.200.247.59/key/api_key/output/json/timezone/true/
8989
*/
9090
public function testGetGeocodedDataWithRealIPv4GetsNullContent()
9191
{
@@ -95,7 +95,7 @@ public function testGetGeocodedDataWithRealIPv4GetsNullContent()
9595

9696
/**
9797
* @expectedException \Geocoder\Exception\NoResultException
98-
* @expectedExceptionMessage Could not execute query http://api.geoips.com/ip/74.200.247.59/key/api_key/output/json/timezone/true/
98+
* @expectedExceptionMessage Invalid response from GeoIPs server for query http://api.geoips.com/ip/74.200.247.59/key/api_key/output/json/timezone/true/
9999
*/
100100
public function testGetGeocodedDataWithRealIPv4GetsEmptyContent()
101101
{
@@ -106,21 +106,29 @@ public function testGetGeocodedDataWithRealIPv4GetsEmptyContent()
106106
public function testGetGeocodedDataWithRealIPv4GetsFakeContentFormattedEmpty()
107107
{
108108
$json = '{"response":{
109-
"status" : "Success",
110-
"ip" : "66.147.244.214",
111-
"hostname" : "box714.bluehost.com",
112-
"owner" : "",
113-
"continent_name" : "",
114-
"continent_code" : "",
115-
"country_name" : "",
116-
"country_code" : "",
117-
"region_name" : "",
118-
"region_code" : "",
119-
"county_name" : "",
120-
"city_name" : "",
121-
"latitude" : "",
122-
"longitude" : "",
123-
"timezone" : ""
109+
"status": "Propper Request",
110+
"message": "Success",
111+
"notes": "The following results has been returned",
112+
"code": "200_1",
113+
"locations": [{
114+
"ip" : "66.147.244.214",
115+
"owner" : "",
116+
"continent_name" : "",
117+
"continent_code" : "",
118+
"country_name" : "",
119+
"country_code" : "",
120+
"region_name" : "",
121+
"region_code" : "",
122+
"county_name" : "",
123+
"city_name" : "",
124+
"latitude" : "",
125+
"longitude" : "",
126+
"timezone" : ""
127+
}],
128+
"unit_test": {
129+
"elapsed_time": "0.0676",
130+
"memory_usage": "2.2MB"
131+
}
124132
}}';
125133

126134
$provider = new GeoIPsProvider($this->getMockAdapterReturns($json), 'api_key');
@@ -144,21 +152,25 @@ public function testGetGeocodedDataWithRealIPv4GetsFakeContentFormattedEmpty()
144152
public function testGetGeocodedDataWithRealIPv4GetsFakeContent()
145153
{
146154
$json = '{"response":{
147-
"status" : "Success",
148-
"ip" : "66.147.244.214",
149-
"hostname" : "box714.bluehost.com",
150-
"owner" : "BLUEHOST INC.",
151-
"continent_name" : "NORTH AMERICA",
152-
"continent_code" : "NA",
153-
"country_name" : "UNITED STATES",
154-
"country_code" : "US",
155-
"region_name" : "UTAH",
156-
"region_code" : "UT",
157-
"county_name" : "UTAH",
158-
"city_name" : "PROVO",
159-
"latitude" : "40.3402",
160-
"longitude" : "-111.6073",
161-
"timezone" : "MST"
155+
"status": "Propper Request",
156+
"message": "Success",
157+
"notes": "The following results has been returned",
158+
"code": "200_1",
159+
"locations": [{
160+
"ip" : "66.147.244.214",
161+
"owner" : "BLUEHOST INC.",
162+
"continent_name" : "NORTH AMERICA",
163+
"continent_code" : "NA",
164+
"country_name" : "UNITED STATES",
165+
"country_code" : "US",
166+
"region_name" : "UTAH",
167+
"region_code" : "UT",
168+
"county_name" : "UTAH",
169+
"city_name" : "PROVO",
170+
"latitude" : "40.3402",
171+
"longitude" : "-111.6073",
172+
"timezone" : "MST"
173+
}]
162174
}}';
163175

164176
$provider = new GeoIPsProvider($this->getMockAdapterReturns($json), 'api_key');
@@ -184,31 +196,131 @@ public function testGetGeocodedDataWithRealIPv4GetsFakeContent()
184196

185197
/**
186198
* @expectedException \Geocoder\Exception\InvalidCredentialsException
187-
* @expectedExceptionMessage API Key provided is not valid.
199+
* @expectedExceptionMessage The API key associated with your request was not recognized.
188200
*/
189201
public function testGetGeocodedDataWithRealIPv4AndInvalidApiKeyGetsFakeContent()
190202
{
191-
$provider = new GeoIPsProvider($this->getMockAdapterReturns('{"response":{"status":"Forbidden", "message":"Not Authorized"}}'), 'api_key');
203+
$provider = new GeoIPsProvider(
204+
$this->getMockAdapterReturns(
205+
'{
206+
"error": {
207+
"status": "Forbidden",
208+
"message": "Not Authorized",
209+
"notes": "The API key associated with your request was not recognized",
210+
"code": "403_1",
211+
"unit_test": {
212+
"elapsed_time": "0.0474",
213+
"memory_usage": "2.2MB"
214+
}
215+
}
216+
}'
217+
),
218+
'api_key'
219+
);
192220
$provider->getGeocodedData('74.200.247.59');
193221
}
194222

195223
/**
196224
* @expectedException \Geocoder\Exception\InvalidCredentialsException
197-
* @expectedExceptionMessage API Key provided is not valid.
225+
* @expectedExceptionMessage The API key has not been approved or has been disabled.
198226
*/
199227
public function testGetGeocodedDataWithRealIPv4AndInvalidApiKeyGetsFakeContent2()
200228
{
201-
$provider = new GeoIPsProvider($this->getMockAdapterReturns('{"response":{"status":"Forbidden", "message":"Account Inactive"}}'), 'api_key');
229+
$provider = new GeoIPsProvider(
230+
$this->getMockAdapterReturns(
231+
'{
232+
"error": {
233+
"status": "Forbidden",
234+
"message": "Account Inactive",
235+
"notes": "The API key has not been approved or has been disabled.",
236+
"code": "403_2",
237+
"unit_test": {
238+
"elapsed_time": "0.0474",
239+
"memory_usage": "2.2MB"
240+
}
241+
}
242+
}'
243+
),
244+
'api_key'
245+
);
202246
$provider->getGeocodedData('74.200.247.59');
203247
}
204248

205249
/**
206-
* @expectedException \Geocoder\Exception\NoResultException
207-
* @expectedExceptionMessage Could not execute query http://api.geoips.com/ip/74.200.247.59/key/api_key/output/json/timezone/true/
250+
* @expectedException \Geocoder\Exception\QuotaExceededException
251+
* @expectedExceptionMessage The service you have requested is over capacity.
252+
*/
253+
public function testGetGeocodedDataWithRealIPv4AndQuotaExceeded()
254+
{
255+
$provider = new GeoIPsProvider(
256+
$this->getMockAdapterReturns(
257+
'{
258+
"error": {
259+
"status": "Forbidden",
260+
"message": "Limit Exceeded",
261+
"notes": "The service you have requested is over capacity.",
262+
"code": "403_3",
263+
"unit_test": {
264+
"elapsed_time": "0.0474",
265+
"memory_usage": "2.2MB"
266+
}
267+
}
268+
}'
269+
),
270+
'api_key'
271+
);
272+
$provider->getGeocodedData('74.200.247.59');
273+
}
274+
275+
/**
276+
* @expectedException \Geocoder\Exception\InvalidArgumentException
277+
* @expectedExceptionMessage The API call should include a valid IP address.
208278
*/
209279
public function testGetGeocodedDataGetsFakeContentWithIpNotFound()
210280
{
211-
$provider = new GeoIPsProvider($this->getMockAdapterReturns('{"response":{"status":"Bad Request", "message":"IP Not Found"}}'), 'api_key');
281+
$provider = new GeoIPsProvider(
282+
$this->getMockAdapterReturns(
283+
'{
284+
"error": {
285+
"status": "Bad Request",
286+
"message": "Error in the URI",
287+
"notes": "The API call should include a valid IP address.",
288+
"code": "400_2",
289+
"unit_test": {
290+
"elapsed_time": "0.0474",
291+
"memory_usage": "2.2MB"
292+
}
293+
}
294+
}'
295+
),
296+
'api_key'
297+
);
298+
$provider->getGeocodedData('74.200.247.59');
299+
}
300+
301+
/**
302+
* @expectedException \Geocoder\Exception\InvalidCredentialsException
303+
* @expectedExceptionMessage The API call should include a API key parameter.
304+
*/
305+
public function testGetGeocodedDataGetsFakeContentWithKeyNotFound()
306+
{
307+
$provider = new GeoIPsProvider(
308+
$this->getMockAdapterReturns(
309+
'{
310+
"error": {
311+
"status": "Bad Request",
312+
"message": "Error in the URI",
313+
"notes": "The API call should include a API key parameter.",
314+
"code": "400_1",
315+
"unit_test": {
316+
"elapsed_time": "0.0474",
317+
"memory_usage": "2.2MB"
318+
}
319+
}
320+
}'
321+
),
322+
'api_key'
323+
);
212324
$provider->getGeocodedData('74.200.247.59');
213325
}
214326

@@ -239,6 +351,19 @@ public function testGetGeocodedDataWithRealIPv4()
239351
$this->assertNull($result['streetName']);
240352
}
241353

354+
/**
355+
* @expectedException \Geocoder\Exception\NoResultException
356+
*/
357+
public function testGetGeocodedDataWithRealIPv4NoResults()
358+
{
359+
if (!isset($_SERVER['GEOIPS_API_KEY'])) {
360+
$this->markTestSkipped('You need to configure the GEOIPS_API_KEY value in phpunit.xml');
361+
}
362+
363+
$provider = new GeoIPsProvider($this->getAdapter(), $_SERVER['GEOIPS_API_KEY']);
364+
$result = $provider->getGeocodedData('255.255.150.96');
365+
}
366+
242367
/**
243368
* @expectedException \Geocoder\Exception\UnsupportedException
244369
* @expectedExceptionMessage The GeoIPsProvider is not able to do reverse geocoding.

0 commit comments

Comments
 (0)