Skip to content

Commit 422923a

Browse files
committed
Update client to match current ipdata API
Add caller IP lookup (lookup with no args), EU endpoint support via configurable base URL and Ipdata::EU_BASE_URL constant, and properly named bulkLookup() method with buildLookup() kept as deprecated alias. Fix bulk request Content-Type from text/plain to application/json. https://claude.ai/code/session_017km7GVfNGXQe2UGceb34hf
1 parent bddeadc commit 422923a

4 files changed

Lines changed: 141 additions & 10 deletions

File tree

README.md

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,27 @@ $psr17Factory = new Psr17Factory();
2727
$ipdata = new Ipdata('my_api_key', $httpClient, $psr17Factory);
2828
```
2929

30+
### EU endpoint
31+
32+
To route requests through ipdata's EU servers (Paris, Ireland and Frankfurt) for GDPR compliance, pass the EU base URL:
33+
34+
```php
35+
$ipdata = new Ipdata('my_api_key', $httpClient, $psr17Factory, Ipdata::EU_BASE_URL);
36+
```
37+
3038
## How to use
3139

32-
To send a geocode request you simply need to provide the IP address you are interested in.
40+
### Look up your own IP
41+
42+
Call `lookup()` with no arguments to get the location of the calling IP address.
43+
44+
```php
45+
$data = $ipdata->lookup();
46+
```
47+
48+
### Look up a specific IP
49+
50+
To send a geocode request you simply need to provide the IP address you are interested in.
3351

3452
```php
3553
$data = $ipdata->lookup('69.78.70.144');
@@ -56,6 +74,12 @@ The output will be the response from the API server with one additional `status`
5674
"flag": "https:\/\/ipdata.co\/flags\/us.png",
5775
"emoji_flag": "\ud83c\uddfa\ud83c\uddf8",
5876
"emoji_unicode": "U+1F1FA U+1F1F8",
77+
"company": {
78+
"name": "Verizon Wireless",
79+
"domain": "verizonwireless.com",
80+
"network": "69.78.0.0\/16",
81+
"type": "isp"
82+
},
5983
"asn": {
6084
"asn": "AS6167",
6185
"name": "Cellco Partnership DBA Verizon Wireless",
@@ -90,12 +114,22 @@ The output will be the response from the API server with one additional `status`
90114
},
91115
"threat": {
92116
"is_tor": false,
117+
"is_vpn": false,
118+
"is_icloud_relay": false,
93119
"is_proxy": false,
120+
"is_datacenter": false,
94121
"is_anonymous": false,
95122
"is_known_attacker": false,
96123
"is_known_abuser": false,
97124
"is_threat": false,
98-
"is_bogon": false
125+
"is_bogon": false,
126+
"blocklists": [],
127+
"scores": {
128+
"vpn_score": 0,
129+
"proxy_score": 0,
130+
"threat_score": 0,
131+
"trust_score": 0
132+
}
99133
},
100134
"count": "6",
101135
"status": 200
@@ -125,7 +159,7 @@ If you want to look up multiple IPs at the same time you may use the `bulkLookup
125159

126160

127161
```php
128-
$data = $ipdata->buildLookup(['1.1.1.1', '69.78.70.144'], ['longitude', 'latitude', 'country_name']);
162+
$data = $ipdata->bulkLookup(['1.1.1.1', '69.78.70.144'], ['longitude', 'latitude', 'country_name']);
129163
echo json_encode($data, JSON_PRETTY_PRINT);
130164
```
131165

phpstan-baseline.neon

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ parameters:
55
count: 1
66
path: src/Ipdata.php
77

8+
-
9+
message: "#^Method Ipdata\\\\ApiClient\\\\Ipdata\\:\\:bulkLookup\\(\\) has parameter \\$ips with no value type specified in iterable type array\\.$#"
10+
count: 1
11+
path: src/Ipdata.php
12+
13+
-
14+
message: "#^Method Ipdata\\\\ApiClient\\\\Ipdata\\:\\:bulkLookup\\(\\) return type has no value type specified in iterable type array\\.$#"
15+
count: 1
16+
path: src/Ipdata.php
17+
818
-
919
message: "#^Method Ipdata\\\\ApiClient\\\\Ipdata\\:\\:buildLookup\\(\\) has parameter \\$ips with no value type specified in iterable type array\\.$#"
1020
count: 1

src/Ipdata.php

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
*/
2020
class Ipdata
2121
{
22-
private const BASE_URL = 'https://api.ipdata.co';
22+
private const DEFAULT_BASE_URL = 'https://api.ipdata.co';
23+
public const EU_BASE_URL = 'https://eu-api.ipdata.co';
2324

2425
/**
2526
* @var string|null
@@ -36,13 +37,18 @@ class Ipdata
3637
*/
3738
private $requestFactory;
3839

40+
/**
41+
* @var string
42+
*/
43+
private $baseUrl;
44+
3945
/**
4046
* Get an instance of the API client. Give it an API key, a PSR-18 client and a PSR-17 request factory.
4147
*
4248
* @param ClientInterface|null $httpClient if null, we will try to use php-http/discovery to find an installed client
4349
* @param RequestFactoryInterface|null $requestFactory if null, we will try to use php-http/discovery to find an installed factory
4450
*/
45-
public function __construct(string $apiKey, ClientInterface $httpClient = null, RequestFactoryInterface $requestFactory = null)
51+
public function __construct(string $apiKey, ClientInterface $httpClient = null, RequestFactoryInterface $requestFactory = null, string $baseUrl = self::DEFAULT_BASE_URL)
4652
{
4753
if (null === $httpClient) {
4854
if (!class_exists(Psr18ClientDiscovery::class)) {
@@ -71,14 +77,15 @@ public function __construct(string $apiKey, ClientInterface $httpClient = null,
7177
$this->httpClient = $httpClient;
7278
$this->apiKey = $apiKey;
7379
$this->requestFactory = $requestFactory;
80+
$this->baseUrl = $baseUrl;
7481
}
7582

7683
/**
7784
* @param array<string> $fields
7885
*
7986
* @throws \Psr\Http\Client\ClientExceptionInterface
8087
*/
81-
public function lookup(string $ip, array $fields = []): array
88+
public function lookup(string $ip = '', array $fields = []): array
8289
{
8390
$query = [
8491
'api-key' => $this->apiKey,
@@ -88,7 +95,8 @@ public function lookup(string $ip, array $fields = []): array
8895
$query['fields'] = implode(',', $fields);
8996
}
9097

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

94102
return $this->parseResponse($response);
@@ -97,11 +105,12 @@ public function lookup(string $ip, array $fields = []): array
97105
/**
98106
* Bulk lookup, requires paid subscription.
99107
*
108+
* @param array<string> $ips
100109
* @param array<string> $fields
101110
*
102111
* @throws \Psr\Http\Client\ClientExceptionInterface
103112
*/
104-
public function buildLookup(array $ips, array $fields = []): array
113+
public function bulkLookup(array $ips, array $fields = []): array
105114
{
106115
$query = [
107116
'api-key' => $this->apiKey,
@@ -111,14 +120,27 @@ public function buildLookup(array $ips, array $fields = []): array
111120
$query['fields'] = implode(',', $fields);
112121
}
113122

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

119128
return $this->parseResponse($response);
120129
}
121130

131+
/**
132+
* @deprecated Use bulkLookup() instead.
133+
*
134+
* @param array<string> $ips
135+
* @param array<string> $fields
136+
*
137+
* @throws \Psr\Http\Client\ClientExceptionInterface
138+
*/
139+
public function buildLookup(array $ips, array $fields = []): array
140+
{
141+
return $this->bulkLookup($ips, $fields);
142+
}
143+
122144
private function parseResponse(ResponseInterface $response): array
123145
{
124146
$body = $response->getBody()->__toString();

tests/IpdataTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,55 @@ public function testParseInvalidJsonResponse()
135135
$ipdata->lookup('69.78.70.144');
136136
}
137137

138+
public function testCallerIpLookup()
139+
{
140+
$httpClient = new MockClient();
141+
$httpClient->addResponse($this->createResponse());
142+
$ipdata = $this->createIpdata($httpClient);
143+
$ipdata->lookup();
144+
145+
$request = $httpClient->getLastRequest();
146+
$this->assertEquals('GET', $request->getMethod());
147+
$this->assertEquals('', $request->getUri()->getPath());
148+
$this->assertEquals('api-key=secret_key', $request->getUri()->getQuery());
149+
}
150+
151+
public function testBulkLookup()
152+
{
153+
$httpClient = new MockClient();
154+
$httpClient->addResponse($this->createResponse());
155+
$ipdata = $this->createIpdata($httpClient);
156+
$ipdata->bulkLookup(['8.8.8.8', '69.78.70.144']);
157+
158+
$request = $httpClient->getLastRequest();
159+
$this->assertEquals('POST', $request->getMethod());
160+
$this->assertEquals('/bulk', $request->getUri()->getPath());
161+
$this->assertEquals('api-key=secret_key', $request->getUri()->getQuery());
162+
$this->assertEquals('["8.8.8.8","69.78.70.144"]', $request->getBody()->__toString());
163+
}
164+
165+
public function testBulkLookupContentType()
166+
{
167+
$httpClient = new MockClient();
168+
$httpClient->addResponse($this->createResponse());
169+
$ipdata = $this->createIpdata($httpClient);
170+
$ipdata->bulkLookup(['8.8.8.8']);
171+
172+
$request = $httpClient->getLastRequest();
173+
$this->assertEquals('application/json', $request->getHeaderLine('Content-Type'));
174+
}
175+
176+
public function testCustomBaseUrl()
177+
{
178+
$httpClient = new MockClient();
179+
$httpClient->addResponse($this->createResponse());
180+
$ipdata = new Ipdata('secret_key', $httpClient, new Psr17Factory(), Ipdata::EU_BASE_URL);
181+
$ipdata->lookup('8.8.8.8');
182+
183+
$request = $httpClient->getLastRequest();
184+
$this->assertEquals('eu-api.ipdata.co', $request->getUri()->getHost());
185+
}
186+
138187
public function testConstructWithDiscovery()
139188
{
140189
$ipdata = new Ipdata('secret_key');
@@ -166,6 +215,12 @@ private function createResponse(array $data = [], int $statusCode = 200): Respon
166215
'flag' => 'https://ipdata.co/flags/us.png',
167216
'emoji_flag' => "\ud83c\uddfa\ud83c\uddf8",
168217
'emoji_unicode' => 'U+1F1FA U+1F1F8',
218+
'company' => [
219+
'name' => 'Verizon Wireless',
220+
'domain' => 'verizonwireless.com',
221+
'network' => '69.78.0.0/16',
222+
'type' => 'isp',
223+
],
169224
'asn' => [
170225
'asn' => 'AS6167',
171226
'name' => 'Cellco Partnership DBA Verizon Wireless',
@@ -197,12 +252,22 @@ private function createResponse(array $data = [], int $statusCode = 200): Respon
197252
],
198253
'threat' => [
199254
'is_tor' => false,
255+
'is_vpn' => false,
256+
'is_icloud_relay' => false,
200257
'is_proxy' => false,
258+
'is_datacenter' => false,
201259
'is_anonymous' => false,
202260
'is_known_attacker' => false,
203261
'is_known_abuser' => false,
204262
'is_threat' => false,
205263
'is_bogon' => false,
264+
'blocklists' => [],
265+
'scores' => [
266+
'vpn_score' => 0,
267+
'proxy_score' => 0,
268+
'threat_score' => 0,
269+
'trust_score' => 0,
270+
],
206271
],
207272
'count' => '3',
208273
];

0 commit comments

Comments
 (0)