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
40 changes: 37 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,27 @@ $psr17Factory = new Psr17Factory();
$ipdata = new Ipdata('my_api_key', $httpClient, $psr17Factory);
```

### EU endpoint

To route requests through ipdata's EU servers (Paris, Ireland and Frankfurt) for GDPR compliance, pass the EU base URL:

```php
$ipdata = new Ipdata('my_api_key', $httpClient, $psr17Factory, Ipdata::EU_BASE_URL);
```

## How to use

To send a geocode request you simply need to provide the IP address you are interested in.
### Look up your own IP

Call `lookup()` with no arguments to get the location of the calling IP address.

```php
$data = $ipdata->lookup();
```

### Look up a specific IP

To send a geocode request you simply need to provide the IP address you are interested in.

```php
$data = $ipdata->lookup('69.78.70.144');
Expand All @@ -56,6 +74,12 @@ The output will be the response from the API server with one additional `status`
"flag": "https:\/\/ipdata.co\/flags\/us.png",
"emoji_flag": "\ud83c\uddfa\ud83c\uddf8",
"emoji_unicode": "U+1F1FA U+1F1F8",
"company": {
"name": "Verizon Wireless",
"domain": "verizonwireless.com",
"network": "69.78.0.0\/16",
"type": "isp"
},
"asn": {
"asn": "AS6167",
"name": "Cellco Partnership DBA Verizon Wireless",
Expand Down Expand Up @@ -90,12 +114,22 @@ The output will be the response from the API server with one additional `status`
},
"threat": {
"is_tor": false,
"is_vpn": false,
"is_icloud_relay": false,
"is_proxy": false,
"is_datacenter": false,
"is_anonymous": false,
"is_known_attacker": false,
"is_known_abuser": false,
"is_threat": false,
"is_bogon": false
"is_bogon": false,
"blocklists": [],
"scores": {
"vpn_score": 0,
"proxy_score": 0,
"threat_score": 0,
"trust_score": 0
}
},
"count": "6",
"status": 200
Expand Down Expand Up @@ -125,7 +159,7 @@ If you want to look up multiple IPs at the same time you may use the `bulkLookup


```php
$data = $ipdata->buildLookup(['1.1.1.1', '69.78.70.144'], ['longitude', 'latitude', 'country_name']);
$data = $ipdata->bulkLookup(['1.1.1.1', '69.78.70.144'], ['longitude', 'latitude', 'country_name']);
echo json_encode($data, JSON_PRETTY_PRINT);
```

Expand Down
4 changes: 1 addition & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@
"require-dev": {
"nyholm/psr7": "^1.2",
"php-http/discovery": "^1.7",
"php-http/httplug": "^2.0",
"php-http/mock-client": "^1.3",
"phpunit/phpunit": "^8.5",
"symfony/http-client": "^5.0"
"phpunit/phpunit": "^8.5"
},
"autoload": {
"psr-4": {
Expand Down
10 changes: 10 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ parameters:
count: 1
path: src/Ipdata.php

-
message: "#^Method Ipdata\\\\ApiClient\\\\Ipdata\\:\\:bulkLookup\\(\\) has parameter \\$ips with no value type specified in iterable type array\\.$#"
count: 1
path: src/Ipdata.php

-
message: "#^Method Ipdata\\\\ApiClient\\\\Ipdata\\:\\:bulkLookup\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Ipdata.php

-
message: "#^Method Ipdata\\\\ApiClient\\\\Ipdata\\:\\:buildLookup\\(\\) has parameter \\$ips with no value type specified in iterable type array\\.$#"
count: 1
Expand Down
36 changes: 29 additions & 7 deletions src/Ipdata.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
*/
class Ipdata
{
private const BASE_URL = 'https://api.ipdata.co';
private const DEFAULT_BASE_URL = 'https://api.ipdata.co';
public const EU_BASE_URL = 'https://eu-api.ipdata.co';

/**
* @var string|null
Expand All @@ -36,13 +37,18 @@ class Ipdata
*/
private $requestFactory;

/**
* @var string
*/
private $baseUrl;

/**
* Get an instance of the API client. Give it an API key, a PSR-18 client and a PSR-17 request factory.
*
* @param ClientInterface|null $httpClient if null, we will try to use php-http/discovery to find an installed client
* @param RequestFactoryInterface|null $requestFactory if null, we will try to use php-http/discovery to find an installed factory
*/
public function __construct(string $apiKey, ClientInterface $httpClient = null, RequestFactoryInterface $requestFactory = null)
public function __construct(string $apiKey, ClientInterface $httpClient = null, RequestFactoryInterface $requestFactory = null, string $baseUrl = self::DEFAULT_BASE_URL)
{
if (null === $httpClient) {
if (!class_exists(Psr18ClientDiscovery::class)) {
Expand Down Expand Up @@ -71,14 +77,15 @@ public function __construct(string $apiKey, ClientInterface $httpClient = null,
$this->httpClient = $httpClient;
$this->apiKey = $apiKey;
$this->requestFactory = $requestFactory;
$this->baseUrl = $baseUrl;
}

/**
* @param array<string> $fields
*
* @throws \Psr\Http\Client\ClientExceptionInterface
*/
public function lookup(string $ip, array $fields = []): array
public function lookup(string $ip = '', array $fields = []): array
{
$query = [
'api-key' => $this->apiKey,
Expand All @@ -88,7 +95,8 @@ public function lookup(string $ip, array $fields = []): array
$query['fields'] = implode(',', $fields);
}

$request = $this->requestFactory->createRequest('GET', sprintf('%s/%s?%s', self::BASE_URL, $ip, http_build_query($query)));
$url = $ip !== '' ? sprintf('%s/%s', $this->baseUrl, $ip) : $this->baseUrl;
$request = $this->requestFactory->createRequest('GET', sprintf('%s?%s', $url, http_build_query($query)));
$response = $this->httpClient->sendRequest($request);

return $this->parseResponse($response);
Expand All @@ -97,11 +105,12 @@ public function lookup(string $ip, array $fields = []): array
/**
* Bulk lookup, requires paid subscription.
*
* @param array<string> $ips
* @param array<string> $fields
*
* @throws \Psr\Http\Client\ClientExceptionInterface
*/
public function buildLookup(array $ips, array $fields = []): array
public function bulkLookup(array $ips, array $fields = []): array
{
$query = [
'api-key' => $this->apiKey,
Expand All @@ -111,14 +120,27 @@ public function buildLookup(array $ips, array $fields = []): array
$query['fields'] = implode(',', $fields);
}

$request = $this->requestFactory->createRequest('POST', sprintf('%s/bulk?%s', self::BASE_URL, http_build_query($query)));
$request = $this->requestFactory->createRequest('POST', sprintf('%s/bulk?%s', $this->baseUrl, http_build_query($query)));
$request->getBody()->write(json_encode($ips));
$request = $request->withAddedHeader('Content-Type', 'text/plain');
$request = $request->withHeader('Content-Type', 'application/json');
$response = $this->httpClient->sendRequest($request);

return $this->parseResponse($response);
}

/**
* @deprecated Use bulkLookup() instead.
*
* @param array<string> $ips
* @param array<string> $fields
*
* @throws \Psr\Http\Client\ClientExceptionInterface
*/
public function buildLookup(array $ips, array $fields = []): array
{
return $this->bulkLookup($ips, $fields);
}

private function parseResponse(ResponseInterface $response): array
{
$body = $response->getBody()->__toString();
Expand Down
72 changes: 69 additions & 3 deletions tests/IpdataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,60 @@ public function testParseInvalidJsonResponse()
$ipdata->lookup('69.78.70.144');
}

public function testConstructWithDiscovery()
public function testCallerIpLookup()
{
$ipdata = new Ipdata('secret_key');
$this->assertInstanceOf(Ipdata::class, $ipdata);
$httpClient = new MockClient();
$httpClient->addResponse($this->createResponse());
$ipdata = $this->createIpdata($httpClient);
$ipdata->lookup();

$request = $httpClient->getLastRequest();
$this->assertEquals('GET', $request->getMethod());
$this->assertEquals('', $request->getUri()->getPath());
$this->assertEquals('api-key=secret_key', $request->getUri()->getQuery());
}

public function testBulkLookup()
{
$httpClient = new MockClient();
$httpClient->addResponse($this->createResponse());
$ipdata = $this->createIpdata($httpClient);
$ipdata->bulkLookup(['8.8.8.8', '69.78.70.144']);

$request = $httpClient->getLastRequest();
$this->assertEquals('POST', $request->getMethod());
$this->assertEquals('/bulk', $request->getUri()->getPath());
$this->assertEquals('api-key=secret_key', $request->getUri()->getQuery());
$this->assertEquals('["8.8.8.8","69.78.70.144"]', $request->getBody()->__toString());
}

public function testBulkLookupContentType()
{
$httpClient = new MockClient();
$httpClient->addResponse($this->createResponse());
$ipdata = $this->createIpdata($httpClient);
$ipdata->bulkLookup(['8.8.8.8']);

$request = $httpClient->getLastRequest();
$this->assertEquals('application/json', $request->getHeaderLine('Content-Type'));
}

public function testCustomBaseUrl()
{
$httpClient = new MockClient();
$httpClient->addResponse($this->createResponse());
$ipdata = new Ipdata('secret_key', $httpClient, new Psr17Factory(), Ipdata::EU_BASE_URL);
$ipdata->lookup('8.8.8.8');

$request = $httpClient->getLastRequest();
$this->assertEquals('eu-api.ipdata.co', $request->getUri()->getHost());
}

public function testConstructWithDiscoveryThrowsWhenNoClientAvailable()
{
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Could not find any installed HTTP clients');
new Ipdata('secret_key');
}

private function createIpdata(ClientInterface $httpClient): Ipdata
Expand Down Expand Up @@ -166,6 +216,12 @@ private function createResponse(array $data = [], int $statusCode = 200): Respon
'flag' => 'https://ipdata.co/flags/us.png',
'emoji_flag' => "\ud83c\uddfa\ud83c\uddf8",
'emoji_unicode' => 'U+1F1FA U+1F1F8',
'company' => [
'name' => 'Verizon Wireless',
'domain' => 'verizonwireless.com',
'network' => '69.78.0.0/16',
'type' => 'isp',
],
'asn' => [
'asn' => 'AS6167',
'name' => 'Cellco Partnership DBA Verizon Wireless',
Expand Down Expand Up @@ -197,12 +253,22 @@ private function createResponse(array $data = [], int $statusCode = 200): Respon
],
'threat' => [
'is_tor' => false,
'is_vpn' => false,
'is_icloud_relay' => false,
'is_proxy' => false,
'is_datacenter' => false,
'is_anonymous' => false,
'is_known_attacker' => false,
'is_known_abuser' => false,
'is_threat' => false,
'is_bogon' => false,
'blocklists' => [],
'scores' => [
'vpn_score' => 0,
'proxy_score' => 0,
'threat_score' => 0,
'trust_score' => 0,
],
],
'count' => '3',
];
Expand Down