Skip to content

Commit ee46b69

Browse files
oschwaldclaude
andcommitted
Add anonymizer and IP risk data to Insights web service
This commit adds support for the new anonymizer object and IP risk snapshot field in the GeoIP2 Insights web service response. Key changes: - Add new Anonymizer record class with VPN detection fields (confidence, provider name, network last seen, and anonymity flags) - Add anonymizer property to Insights model - Add ipRiskSnapshot field to Traits record for static IP risk scoring - Deprecate anonymous IP flags in Traits (isAnonymous, isAnonymousVpn, isHostingProvider, isPublicProxy, isResidentialProxy, isTorExitNode) in favor of the new anonymizer object - Update tests to cover new fields - Update CHANGELOG for version 3.3.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 144d04e commit ee46b69

File tree

5 files changed

+301
-2
lines changed

5 files changed

+301
-2
lines changed

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
CHANGELOG
22
=========
33

4+
3.3.0 (unreleased)
5+
------------------
6+
7+
* A new `anonymizer` property has been added to `GeoIp2\Model\Insights`.
8+
This property is an instance of `GeoIp2\Record\Anonymizer` and provides
9+
information about whether the IP address belongs to an anonymous network,
10+
VPN provider details (including `confidence`, `providerName`, and
11+
`networkLastSeen`), and various anonymity flags. This data is available
12+
from the GeoIP2 Insights web service.
13+
* A new `ipRiskSnapshot` property has been added to `GeoIp2\Record\Traits`.
14+
This property provides a risk score from 0.01 to 99.99 indicating the risk
15+
associated with the IP address. Higher values indicate higher risk. This is
16+
a static snapshot that is less dynamic than minFraud risk scoring. This
17+
attribute is only available from the GeoIP2 Insights web service.
18+
* The `isAnonymous`, `isAnonymousVpn`, `isHostingProvider`, `isPublicProxy`,
19+
`isResidentialProxy`, and `isTorExitNode` properties in
20+
`GeoIp2\Record\Traits` have been deprecated. Please use the corresponding
21+
properties in the new `anonymizer` object in the Insights response instead.
22+
423
3.2.0 (2025-05-05)
524
------------------
625

src/Model/Insights.php

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,48 @@
44

55
namespace GeoIp2\Model;
66

7+
use GeoIp2\Record\Anonymizer;
8+
79
/**
810
* Model class for the data returned by GeoIP2 Insights web service.
911
*
1012
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for
1113
* more details.
1214
*/
13-
// phpcs:disable
14-
class Insights extends City {}
15+
class Insights extends City
16+
{
17+
/**
18+
* @var Anonymizer Anonymizer data for the requested IP address. This
19+
* includes information about whether the IP belongs to an anonymous
20+
* network, VPN provider details, and confidence scores.
21+
*/
22+
public readonly Anonymizer $anonymizer;
23+
24+
/**
25+
* @ignore
26+
*
27+
* @param array<string, mixed> $raw
28+
* @param list<string> $locales
29+
*/
30+
public function __construct(array $raw, array $locales = ['en'])
31+
{
32+
parent::__construct($raw, $locales);
33+
34+
$this->anonymizer = new Anonymizer($raw['anonymizer'] ?? []);
35+
}
36+
37+
/**
38+
* @return array<string, mixed>|null
39+
*/
40+
public function jsonSerialize(): ?array
41+
{
42+
$js = parent::jsonSerialize();
43+
44+
$anonymizer = $this->anonymizer->jsonSerialize();
45+
if (!empty($anonymizer)) {
46+
$js['anonymizer'] = $anonymizer;
47+
}
48+
49+
return $js;
50+
}
51+
}

src/Record/Anonymizer.php

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace GeoIp2\Record;
6+
7+
/**
8+
* Contains data for the anonymizer record associated with an IP address.
9+
*
10+
* This record is returned by the GeoIP2 Insights web service.
11+
*/
12+
class Anonymizer implements \JsonSerializable
13+
{
14+
/**
15+
* @var int|null A confidence score from 1-99 indicating our confidence that the
16+
* IP address is a VPN. Currently, this is either 30 or 99. This attribute
17+
* is only available from the GeoIP2 Insights web service.
18+
*/
19+
public readonly ?int $confidence;
20+
21+
/**
22+
* @var bool This is true if the IP address belongs to
23+
* any sort of anonymous network. This attribute is only available from the
24+
* GeoIP2 Insights web service.
25+
*/
26+
public readonly bool $isAnonymous;
27+
28+
/**
29+
* @var bool This is true if the IP address is
30+
* registered to an anonymous VPN provider. If a VPN provider does not register
31+
* subnets under names associated with them, we will likely only flag their IP
32+
* ranges using the isHostingProvider property. This attribute is only available
33+
* from the GeoIP2 Insights web service.
34+
*/
35+
public readonly bool $isAnonymousVpn;
36+
37+
/**
38+
* @var bool This is true if the IP address belongs
39+
* to a hosting or VPN provider (see description of isAnonymousVpn property).
40+
* This attribute is only available from the GeoIP2 Insights web service.
41+
*/
42+
public readonly bool $isHostingProvider;
43+
44+
/**
45+
* @var bool This is true if the IP address belongs to
46+
* a public proxy. This attribute is only available from the GeoIP2 Insights
47+
* web service.
48+
*/
49+
public readonly bool $isPublicProxy;
50+
51+
/**
52+
* @var bool This is true if the IP address is
53+
* on a suspected anonymizing network and belongs to a residential ISP. This
54+
* attribute is only available from the GeoIP2 Insights web service.
55+
*/
56+
public readonly bool $isResidentialProxy;
57+
58+
/**
59+
* @var bool This is true if the IP address is a Tor
60+
* exit node. This attribute is only available from the GeoIP2 Insights
61+
* web service.
62+
*/
63+
public readonly bool $isTorExitNode;
64+
65+
/**
66+
* @var string|null The date the anonymizer network was last seen in
67+
* YYYY-MM-DD format. This attribute is only available from the GeoIP2
68+
* Insights web service.
69+
*/
70+
public readonly ?string $networkLastSeen;
71+
72+
/**
73+
* @var string|null The name of the VPN provider, for example, NordVPN or
74+
* SurfShark. This attribute is only available from the GeoIP2 Insights
75+
* web service.
76+
*/
77+
public readonly ?string $providerName;
78+
79+
/**
80+
* @ignore
81+
*
82+
* @param array<string, mixed> $record
83+
*/
84+
public function __construct(array $record)
85+
{
86+
$this->confidence = $record['confidence'] ?? null;
87+
$this->isAnonymous = $record['is_anonymous'] ?? false;
88+
$this->isAnonymousVpn = $record['is_anonymous_vpn'] ?? false;
89+
$this->isHostingProvider = $record['is_hosting_provider'] ?? false;
90+
$this->isPublicProxy = $record['is_public_proxy'] ?? false;
91+
$this->isResidentialProxy = $record['is_residential_proxy'] ?? false;
92+
$this->isTorExitNode = $record['is_tor_exit_node'] ?? false;
93+
$this->networkLastSeen = $record['network_last_seen'] ?? null;
94+
$this->providerName = $record['provider_name'] ?? null;
95+
}
96+
97+
/**
98+
* @return array<string, mixed>
99+
*/
100+
public function jsonSerialize(): array
101+
{
102+
$js = [];
103+
104+
if ($this->confidence !== null) {
105+
$js['confidence'] = $this->confidence;
106+
}
107+
if ($this->isAnonymous !== false) {
108+
$js['is_anonymous'] = $this->isAnonymous;
109+
}
110+
if ($this->isAnonymousVpn !== false) {
111+
$js['is_anonymous_vpn'] = $this->isAnonymousVpn;
112+
}
113+
if ($this->isHostingProvider !== false) {
114+
$js['is_hosting_provider'] = $this->isHostingProvider;
115+
}
116+
if ($this->isPublicProxy !== false) {
117+
$js['is_public_proxy'] = $this->isPublicProxy;
118+
}
119+
if ($this->isResidentialProxy !== false) {
120+
$js['is_residential_proxy'] = $this->isResidentialProxy;
121+
}
122+
if ($this->isTorExitNode !== false) {
123+
$js['is_tor_exit_node'] = $this->isTorExitNode;
124+
}
125+
if ($this->networkLastSeen !== null) {
126+
$js['network_last_seen'] = $this->networkLastSeen;
127+
}
128+
if ($this->providerName !== null) {
129+
$js['provider_name'] = $this->providerName;
130+
}
131+
132+
return $js;
133+
}
134+
}

src/Record/Traits.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ class Traits implements \JsonSerializable
6363
* @var bool This is true if the IP address belongs to
6464
* any sort of anonymous network. This property is only available from GeoIP2
6565
* Insights.
66+
*
67+
* @deprecated use $anonymizer->isAnonymous in the Insights response instead
6668
*/
6769
public readonly bool $isAnonymous;
6870

@@ -72,6 +74,8 @@ class Traits implements \JsonSerializable
7274
* subnets under names associated with them, we will likely only flag their IP
7375
* ranges using the isHostingProvider property. This property is only available
7476
* from GeoIP2 Insights.
77+
*
78+
* @deprecated use $anonymizer->isAnonymousVpn in the Insights response instead
7579
*/
7680
public readonly bool $isAnonymousVpn;
7781

@@ -86,6 +90,8 @@ class Traits implements \JsonSerializable
8690
* @var bool This is true if the IP address belongs
8791
* to a hosting or VPN provider (see description of isAnonymousVpn property).
8892
* This property is only available from GeoIP2 Insights.
93+
*
94+
* @deprecated use $anonymizer->isHostingProvider in the Insights response instead
8995
*/
9096
public readonly bool $isHostingProvider;
9197

@@ -100,19 +106,25 @@ class Traits implements \JsonSerializable
100106
/**
101107
* @var bool This is true if the IP address belongs to
102108
* a public proxy. This property is only available from GeoIP2 Insights.
109+
*
110+
* @deprecated use $anonymizer->isPublicProxy in the Insights response instead
103111
*/
104112
public readonly bool $isPublicProxy;
105113

106114
/**
107115
* @var bool This is true if the IP address is
108116
* on a suspected anonymizing network and belongs to a residential ISP. This
109117
* property is only available from GeoIP2 Insights.
118+
*
119+
* @deprecated use $anonymizer->isResidentialProxy in the Insights response instead
110120
*/
111121
public readonly bool $isResidentialProxy;
112122

113123
/**
114124
* @var bool This is true if the IP address is a Tor
115125
* exit node. This property is only available from GeoIP2 Insights.
126+
*
127+
* @deprecated use $anonymizer->isTorExitNode in the Insights response instead
116128
*/
117129
public readonly bool $isTorExitNode;
118130

@@ -153,6 +165,15 @@ class Traits implements \JsonSerializable
153165
*/
154166
public readonly ?string $organization;
155167

168+
/**
169+
* @var float|null A risk score from 0.01 to 99.99 indicating the risk
170+
* associated with the IP address. Higher values indicate higher risk.
171+
* This is a static snapshot that is less dynamic than minFraud risk
172+
* scoring. This attribute is only available from the GeoIP2 Insights web
173+
* service.
174+
*/
175+
public readonly ?float $ipRiskSnapshot;
176+
156177
/**
157178
* @var float|null An indicator of how static or
158179
* dynamic an IP address is. This property is only available from GeoIP2
@@ -220,6 +241,7 @@ public function __construct(array $record)
220241
$this->mobileCountryCode = $record['mobile_country_code'] ?? null;
221242
$this->mobileNetworkCode = $record['mobile_network_code'] ?? null;
222243
$this->organization = $record['organization'] ?? null;
244+
$this->ipRiskSnapshot = $record['ip_risk_snapshot'] ?? null;
223245
$this->staticIpScore = $record['static_ip_score'] ?? null;
224246
$this->userCount = $record['user_count'] ?? null;
225247
$this->userType = $record['user_type'] ?? null;
@@ -291,6 +313,9 @@ public function jsonSerialize(): array
291313
if ($this->organization !== null) {
292314
$js['organization'] = $this->organization;
293315
}
316+
if ($this->ipRiskSnapshot !== null) {
317+
$js['ip_risk_snapshot'] = $this->ipRiskSnapshot;
318+
}
294319
if ($this->staticIpScore !== null) {
295320
$js['static_ip_score'] = $this->staticIpScore;
296321
}

0 commit comments

Comments
 (0)