Skip to content

Commit 8e81958

Browse files
authored
Merge pull request #104 from ipinfo/silvano/eng-642-add-resproxy-support-in-ipinfophp-library
Add Residential Proxy API support
2 parents e378e66 + 627f5ec commit 8e81958

3 files changed

Lines changed: 93 additions & 2 deletions

File tree

.phpactor.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"$schema": "/phpactor.schema.json",
3+
"php_code_sniffer.enabled": true
4+
}

src/IPinfo.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,57 @@ public function getRequestDetails(string $ip_address)
254254
return $raw_details;
255255
}
256256

257+
/**
258+
* Get residential proxy information for an IP address.
259+
* @param string $ip_address IP address to look up.
260+
* @return array Resproxy data containing ip, last_seen, percent_days_seen, service.
261+
* @throws IPinfoException
262+
*/
263+
public function getResproxy(string $ip_address)
264+
{
265+
$cacheKey = "resproxy/$ip_address";
266+
267+
if ($this->cache != null) {
268+
$cachedRes = $this->cache->get($this->cacheKey($cacheKey));
269+
if ($cachedRes != null) {
270+
// The cache may modify the 'ip' field for IPv6 normalization,
271+
// but for resproxy the key contains a prefix, so restore original IP
272+
$cachedRes['ip'] = $ip_address;
273+
return $cachedRes;
274+
}
275+
}
276+
277+
$url = self::API_URL . "/resproxy/$ip_address";
278+
279+
try {
280+
$response = $this->http_client->request('GET', $url);
281+
} catch (GuzzleException $e) {
282+
throw new IPinfoException($e->getMessage());
283+
} catch (Exception $e) {
284+
throw new IPinfoException($e->getMessage());
285+
}
286+
287+
if ($response->getStatusCode() == self::STATUS_CODE_QUOTA_EXCEEDED) {
288+
throw new IPinfoException('IPinfo request quota exceeded.');
289+
} elseif ($response->getStatusCode() >= 400) {
290+
throw new IPinfoException(
291+
'Exception: ' .
292+
json_encode([
293+
'status' => $response->getStatusCode(),
294+
'reason' => $response->getReasonPhrase(),
295+
]),
296+
);
297+
}
298+
299+
$details = json_decode($response->getBody(), true);
300+
301+
if ($this->cache != null) {
302+
$this->cache->set($this->cacheKey($cacheKey), $details);
303+
}
304+
305+
return $details;
306+
}
307+
257308
/**
258309
* Gets a URL to a map on https://ipinfo.io/map given a list of IPs (max
259310
* 500,000).

tests/IPinfoTest.php

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,11 @@ public function testGuzzleOverride()
160160
public function testGetMapURL()
161161
{
162162
$h = new IPinfo();
163-
$url = $h->getMapUrl(file("tests/map-ips.txt"));
163+
$url = $h->getMapUrl(file("tests/map-ips.txt", FILE_IGNORE_NEW_LINES));
164+
if ($url === null) {
165+
// The Map endpoint is heavily rate limited
166+
$this->markTestSkipped("Map API rate limit exceeded");
167+
}
164168
$this->assertStringStartsWith("https://ipinfo.io/tools/map/", $url);
165169
}
166170

@@ -209,7 +213,7 @@ public function testGetBatchDetails()
209213
$this->assertNotNull($ipV4['region']);
210214
$this->assertNotNull($ipV4['country']);
211215
$this->assertNotNull($ipV4['loc']);
212-
$this->assertNull($ipV4['postal']);
216+
$this->assertNotNull($ipV4['postal']);
213217
$this->assertNotNull($ipV4['timezone']);
214218
$this->assertEquals($ipV4['org'], 'AS3356 Level 3 Parent, LLC');
215219
}
@@ -405,4 +409,36 @@ public function testIPv6NotationsCaching()
405409
$normalized_ip = inet_ntop(inet_pton($standard_ip));
406410
$h->getDetails($normalized_ip);
407411
}
412+
413+
public function testResproxy()
414+
{
415+
$tok = getenv('IPINFO_TOKEN');
416+
if (!$tok) {
417+
$this->markTestSkipped('IPINFO_TOKEN env var required');
418+
}
419+
420+
$h = new IPinfo($tok);
421+
$ip = '175.107.211.204';
422+
423+
// test multiple times for cache hits
424+
for ($i = 0; $i < 5; $i++) {
425+
$res = $h->getResproxy($ip);
426+
$this->assertEquals($res['ip'], $ip);
427+
$this->assertNotNull($res['last_seen']);
428+
$this->assertNotNull($res['percent_days_seen']);
429+
$this->assertNotNull($res['service']);
430+
}
431+
}
432+
433+
public function testResproxyEmpty()
434+
{
435+
$tok = getenv("IPINFO_TOKEN");
436+
if (!$tok) {
437+
$this->markTestSkipped("IPINFO_TOKEN env var required");
438+
}
439+
440+
$h = new IPinfo($tok);
441+
$res = $h->getResproxy("8.8.8.8");
442+
$this->assertEquals($res, []);
443+
}
408444
}

0 commit comments

Comments
 (0)