Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .phpactor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "/phpactor.schema.json",
"php_code_sniffer.enabled": true
}
51 changes: 51 additions & 0 deletions src/IPinfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,57 @@ public function getRequestDetails(string $ip_address)
return $raw_details;
}

/**
* Get residential proxy information for an IP address.
* @param string $ip_address IP address to look up.
* @return array Resproxy data containing ip, last_seen, percent_days_seen, service.
* @throws IPinfoException
*/
public function getResproxy(string $ip_address)
{
$cacheKey = "resproxy/$ip_address";

if ($this->cache != null) {
$cachedRes = $this->cache->get($this->cacheKey($cacheKey));
if ($cachedRes != null) {
// The cache may modify the 'ip' field for IPv6 normalization,
// but for resproxy the key contains a prefix, so restore original IP
$cachedRes['ip'] = $ip_address;
return $cachedRes;
}
}

$url = self::API_URL . "/resproxy/$ip_address";

try {
$response = $this->http_client->request('GET', $url);
} catch (GuzzleException $e) {
throw new IPinfoException($e->getMessage());
} catch (Exception $e) {
throw new IPinfoException($e->getMessage());
}

if ($response->getStatusCode() == self::STATUS_CODE_QUOTA_EXCEEDED) {
throw new IPinfoException('IPinfo request quota exceeded.');
} elseif ($response->getStatusCode() >= 400) {
throw new IPinfoException(
'Exception: ' .
json_encode([
'status' => $response->getStatusCode(),
'reason' => $response->getReasonPhrase(),
]),
);
}

$details = json_decode($response->getBody(), true);

if ($this->cache != null) {
$this->cache->set($this->cacheKey($cacheKey), $details);
}

return $details;
}

/**
* Gets a URL to a map on https://ipinfo.io/map given a list of IPs (max
* 500,000).
Expand Down
40 changes: 38 additions & 2 deletions tests/IPinfoTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,11 @@ public function testGuzzleOverride()
public function testGetMapURL()
{
$h = new IPinfo();
$url = $h->getMapUrl(file("tests/map-ips.txt"));
$url = $h->getMapUrl(file("tests/map-ips.txt", FILE_IGNORE_NEW_LINES));
if ($url === null) {
// The Map endpoint is heavily rate limited
$this->markTestSkipped("Map API rate limit exceeded");
}
$this->assertStringStartsWith("https://ipinfo.io/tools/map/", $url);
}

Expand Down Expand Up @@ -209,7 +213,7 @@ public function testGetBatchDetails()
$this->assertNotNull($ipV4['region']);
$this->assertNotNull($ipV4['country']);
$this->assertNotNull($ipV4['loc']);
$this->assertNull($ipV4['postal']);
$this->assertNotNull($ipV4['postal']);
$this->assertNotNull($ipV4['timezone']);
$this->assertEquals($ipV4['org'], 'AS3356 Level 3 Parent, LLC');
}
Expand Down Expand Up @@ -405,4 +409,36 @@ public function testIPv6NotationsCaching()
$normalized_ip = inet_ntop(inet_pton($standard_ip));
$h->getDetails($normalized_ip);
}

public function testResproxy()
{
$tok = getenv('IPINFO_TOKEN');
if (!$tok) {
$this->markTestSkipped('IPINFO_TOKEN env var required');
}

$h = new IPinfo($tok);
$ip = '175.107.211.204';

// test multiple times for cache hits
for ($i = 0; $i < 5; $i++) {
$res = $h->getResproxy($ip);
$this->assertEquals($res['ip'], $ip);
$this->assertNotNull($res['last_seen']);
$this->assertNotNull($res['percent_days_seen']);
$this->assertNotNull($res['service']);
}
}

public function testResproxyEmpty()
{
$tok = getenv("IPINFO_TOKEN");
if (!$tok) {
$this->markTestSkipped("IPINFO_TOKEN env var required");
}

$h = new IPinfo($tok);
$res = $h->getResproxy("8.8.8.8");
$this->assertEquals($res, []);
}
}