Skip to content

Commit 37774da

Browse files
committed
Use readonly properties
1 parent a463af1 commit 37774da

24 files changed

+614
-485
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ CHANGELOG
55
-------------------
66

77
* IMPORTANT: PHP 8.1 or greater is now required.
8+
* BREAKING: Read-only properties are now used for the model and record
9+
classes rather than magic methods. This significantly improves performance.
10+
* BREAKING: The `raw` property on model classess and the `record` property on
11+
record classes have been removed.
12+
* BREAKING: The `jsonSerialize` output has changed.
813
* `GeoIp2\WebService\Client` methods now throw an `InvalidArgumentException`
914
if an invalid IP address is passed to them. Previously, they would make
1015
a request to the web service and throw a

src/Database/Reader.php

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace GeoIp2\Database;
66

77
use GeoIp2\Exception\AddressNotFoundException;
8-
use GeoIp2\Model\AbstractModel;
98
use GeoIp2\Model\AnonymousIp;
109
use GeoIp2\Model\Asn;
1110
use GeoIp2\Model\City;
@@ -84,7 +83,6 @@ public function __construct(
8483
*/
8584
public function city(string $ipAddress): City
8685
{
87-
// @phpstan-ignore-next-line
8886
return $this->modelFor(City::class, 'City', $ipAddress);
8987
}
9088

@@ -100,7 +98,6 @@ public function city(string $ipAddress): City
10098
*/
10199
public function country(string $ipAddress): Country
102100
{
103-
// @phpstan-ignore-next-line
104101
return $this->modelFor(Country::class, 'Country', $ipAddress);
105102
}
106103

@@ -116,7 +113,6 @@ public function country(string $ipAddress): Country
116113
*/
117114
public function anonymousIp(string $ipAddress): AnonymousIp
118115
{
119-
// @phpstan-ignore-next-line
120116
return $this->flatModelFor(
121117
AnonymousIp::class,
122118
'GeoIP2-Anonymous-IP',
@@ -136,7 +132,6 @@ public function anonymousIp(string $ipAddress): AnonymousIp
136132
*/
137133
public function asn(string $ipAddress): Asn
138134
{
139-
// @phpstan-ignore-next-line
140135
return $this->flatModelFor(
141136
Asn::class,
142137
'GeoLite2-ASN',
@@ -156,7 +151,6 @@ public function asn(string $ipAddress): Asn
156151
*/
157152
public function connectionType(string $ipAddress): ConnectionType
158153
{
159-
// @phpstan-ignore-next-line
160154
return $this->flatModelFor(
161155
ConnectionType::class,
162156
'GeoIP2-Connection-Type',
@@ -176,7 +170,6 @@ public function connectionType(string $ipAddress): ConnectionType
176170
*/
177171
public function domain(string $ipAddress): Domain
178172
{
179-
// @phpstan-ignore-next-line
180173
return $this->flatModelFor(
181174
Domain::class,
182175
'GeoIP2-Domain',
@@ -196,7 +189,6 @@ public function domain(string $ipAddress): Domain
196189
*/
197190
public function enterprise(string $ipAddress): Enterprise
198191
{
199-
// @phpstan-ignore-next-line
200192
return $this->modelFor(Enterprise::class, 'Enterprise', $ipAddress);
201193
}
202194

@@ -212,15 +204,14 @@ public function enterprise(string $ipAddress): Enterprise
212204
*/
213205
public function isp(string $ipAddress): Isp
214206
{
215-
// @phpstan-ignore-next-line
216207
return $this->flatModelFor(
217208
Isp::class,
218209
'GeoIP2-ISP',
219210
$ipAddress
220211
);
221212
}
222213

223-
private function modelFor(string $class, string $type, string $ipAddress): AbstractModel
214+
private function modelFor(string $class, string $type, string $ipAddress): object
224215
{
225216
[$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress);
226217

@@ -230,7 +221,7 @@ private function modelFor(string $class, string $type, string $ipAddress): Abstr
230221
return new $class($record, $this->locales);
231222
}
232223

233-
private function flatModelFor(string $class, string $type, string $ipAddress): AbstractModel
224+
private function flatModelFor(string $class, string $type, string $ipAddress): object
234225
{
235226
[$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress);
236227

src/Model/AbstractModel.php

Lines changed: 0 additions & 68 deletions
This file was deleted.

src/Model/AnonymousIp.php

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,44 @@
2929
* the record. In particular, this is the largest network where all of the
3030
* fields besides $ipAddress have the same value.
3131
*/
32-
class AnonymousIp extends AbstractModel
32+
class AnonymousIp implements \JsonSerializable
3333
{
34-
protected bool $isAnonymous;
35-
protected bool $isAnonymousVpn;
36-
protected bool $isHostingProvider;
37-
protected bool $isPublicProxy;
38-
protected bool $isResidentialProxy;
39-
protected bool $isTorExitNode;
40-
protected string $ipAddress;
41-
protected string $network;
34+
public readonly bool $isAnonymous;
35+
public readonly bool $isAnonymousVpn;
36+
public readonly bool $isHostingProvider;
37+
public readonly bool $isPublicProxy;
38+
public readonly bool $isResidentialProxy;
39+
public readonly bool $isTorExitNode;
40+
public readonly string $ipAddress;
41+
public readonly string $network;
4242

4343
/**
4444
* @ignore
4545
*/
4646
public function __construct(array $raw)
4747
{
48-
parent::__construct($raw);
49-
50-
$this->isAnonymous = $this->get('is_anonymous');
51-
$this->isAnonymousVpn = $this->get('is_anonymous_vpn');
52-
$this->isHostingProvider = $this->get('is_hosting_provider');
53-
$this->isPublicProxy = $this->get('is_public_proxy');
54-
$this->isResidentialProxy = $this->get('is_residential_proxy');
55-
$this->isTorExitNode = $this->get('is_tor_exit_node');
56-
$ipAddress = $this->get('ip_address');
48+
$this->isAnonymous = $raw['is_anonymous'] ?? false;
49+
$this->isAnonymousVpn = $raw['is_anonymous_vpn'] ?? false;
50+
$this->isHostingProvider = $raw['is_hosting_provider'] ?? false;
51+
$this->isPublicProxy = $raw['is_public_proxy'] ?? false;
52+
$this->isResidentialProxy = $raw['is_residential_proxy'] ?? false;
53+
$this->isTorExitNode = $raw['is_tor_exit_node'] ?? false;
54+
$ipAddress = $raw['ip_address'];
5755
$this->ipAddress = $ipAddress;
58-
$this->network = Util::cidr($ipAddress, $this->get('prefix_len'));
56+
$this->network = Util::cidr($ipAddress, $raw['prefix_len']);
57+
}
58+
59+
public function jsonSerialize(): ?array
60+
{
61+
return [
62+
'is_anonymous' => $this->isAnonymous,
63+
'is_anonymous_vpn' => $this->isAnonymousVpn,
64+
'is_hosting_provider' => $this->isHostingProvider,
65+
'is_public_proxy' => $this->isPublicProxy,
66+
'is_residential_proxy' => $this->isResidentialProxy,
67+
'is_tor_exit_node' => $this->isTorExitNode,
68+
'ip_address' => $this->ipAddress,
69+
'network' => $this->network,
70+
];
5971
}
6072
}

src/Model/Asn.php

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,33 @@
2020
* the record. In particular, this is the largest network where all of the
2121
* fields besides $ipAddress have the same value.
2222
*/
23-
class Asn extends AbstractModel
23+
class Asn implements \JsonSerializable
2424
{
25-
protected ?int $autonomousSystemNumber;
26-
protected ?string $autonomousSystemOrganization;
27-
protected string $ipAddress;
28-
protected string $network;
25+
public readonly ?int $autonomousSystemNumber;
26+
public readonly ?string $autonomousSystemOrganization;
27+
public readonly string $ipAddress;
28+
public readonly string $network;
2929

3030
/**
3131
* @ignore
3232
*/
3333
public function __construct(array $raw)
3434
{
35-
parent::__construct($raw);
36-
$this->autonomousSystemNumber = $this->get('autonomous_system_number');
35+
$this->autonomousSystemNumber = $raw['autonomous_system_number'] ?? null;
3736
$this->autonomousSystemOrganization =
38-
$this->get('autonomous_system_organization');
39-
$ipAddress = $this->get('ip_address');
37+
$raw['autonomous_system_organization'] ?? null;
38+
$ipAddress = $raw['ip_address'];
4039
$this->ipAddress = $ipAddress;
41-
$this->network = Util::cidr($ipAddress, $this->get('prefix_len'));
40+
$this->network = Util::cidr($ipAddress, $raw['prefix_len']);
41+
}
42+
43+
public function jsonSerialize(): ?array
44+
{
45+
return [
46+
'autonomous_system_number' => $this->autonomousSystemNumber,
47+
'autonomous_system_organization' => $this->autonomousSystemOrganization,
48+
'ip_address' => $this->ipAddress,
49+
'network' => $this->network,
50+
];
4251
}
4352
}

src/Model/City.php

Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,29 @@ class City extends Country
3434
/**
3535
* @ignore
3636
*/
37-
protected \GeoIp2\Record\City $city;
37+
public readonly \GeoIp2\Record\City $city;
3838

3939
/**
4040
* @ignore
4141
*/
42-
protected \GeoIp2\Record\Location $location;
42+
public readonly \GeoIp2\Record\Location $location;
4343

4444
/**
4545
* @ignore
4646
*/
47-
protected \GeoIp2\Record\Postal $postal;
47+
public readonly \GeoIp2\Record\Subdivision $mostSpecificSubdivision;
48+
49+
/**
50+
* @ignore
51+
*/
52+
public readonly \GeoIp2\Record\Postal $postal;
4853

4954
/**
5055
* @ignore
5156
*
5257
* @var array<\GeoIp2\Record\Subdivision>
5358
*/
54-
protected array $subdivisions = [];
59+
public readonly array $subdivisions;
5560

5661
/**
5762
* @ignore
@@ -60,58 +65,45 @@ public function __construct(array $raw, array $locales = ['en'])
6065
{
6166
parent::__construct($raw, $locales);
6267

63-
$this->city = new \GeoIp2\Record\City($this->get('city'), $locales);
64-
$this->location = new \GeoIp2\Record\Location($this->get('location'));
65-
$this->postal = new \GeoIp2\Record\Postal($this->get('postal'));
66-
67-
$this->createSubdivisions($raw, $locales);
68-
}
68+
$this->city = new \GeoIp2\Record\City($raw['city'] ?? [], $locales);
69+
$this->location = new \GeoIp2\Record\Location($raw['location'] ?? []);
70+
$this->postal = new \GeoIp2\Record\Postal($raw['postal'] ?? []);
6971

70-
private function createSubdivisions(array $raw, array $locales): void
71-
{
7272
if (!isset($raw['subdivisions'])) {
73+
$this->subdivisions = [];
74+
$this->mostSpecificSubdivision =
75+
new \GeoIp2\Record\Subdivision([], $locales);
76+
7377
return;
7478
}
7579

80+
$subdivisions = [];
7681
foreach ($raw['subdivisions'] as $sub) {
77-
$this->subdivisions[] =
82+
$subdivisions[] =
7883
new \GeoIp2\Record\Subdivision($sub, $locales)
7984
;
8085
}
86+
87+
// Not using end as we don't want to modify internal pointer.
88+
$this->mostSpecificSubdivision =
89+
$subdivisions[\count($subdivisions) - 1];
90+
$this->subdivisions = $subdivisions;
8191
}
8292

83-
/**
84-
* @ignore
85-
*
86-
* @return mixed
87-
*/
88-
public function __get(string $attr)
93+
public function jsonSerialize(): ?array
8994
{
90-
if ($attr === 'mostSpecificSubdivision') {
91-
return $this->{$attr}();
92-
}
95+
$js = parent::jsonSerialize();
9396

94-
return parent::__get($attr);
95-
}
97+
$js['city'] = $this->city->jsonSerialize();
98+
$js['location'] = $this->location->jsonSerialize();
99+
$js['postal'] = $this->postal->jsonSerialize();
96100

97-
/**
98-
* @ignore
99-
*/
100-
public function __isset(string $attr): bool
101-
{
102-
if ($attr === 'mostSpecificSubdivision') {
103-
// We always return a mostSpecificSubdivision, even if it is the
104-
// empty subdivision
105-
return true;
101+
$subdivisions = [];
102+
foreach ($this->subdivisions as $sub) {
103+
$subdivisions[] = $sub->jsonSerialize();
106104
}
105+
$js['subdivisions'] = $subdivisions;
107106

108-
return parent::__isset($attr);
109-
}
110-
111-
private function mostSpecificSubdivision(): \GeoIp2\Record\Subdivision
112-
{
113-
return empty($this->subdivisions) ?
114-
new \GeoIp2\Record\Subdivision([], $this->locales) :
115-
end($this->subdivisions);
107+
return $js;
116108
}
117109
}

0 commit comments

Comments
 (0)