diff --git a/lib/maxmind/GeoIp2/src/CHANGELOG.md b/lib/maxmind/GeoIp2/CHANGELOG.md similarity index 97% rename from lib/maxmind/GeoIp2/src/CHANGELOG.md rename to lib/maxmind/GeoIp2/CHANGELOG.md index fe872d0fabe..7f01bd2c270 100644 --- a/lib/maxmind/GeoIp2/src/CHANGELOG.md +++ b/lib/maxmind/GeoIp2/CHANGELOG.md @@ -1,6 +1,14 @@ CHANGELOG ========= +3.1.0 (2024-11-15) +------------------ + +* This library no longer uses implicitly nullable parameter types. This + will fix deprecation warning in PHP 8.4. Reported by Steven Lewis. + GitHub #230. +* The PHPDoc type hints have been improved for use with PHPStan. + 3.0.0 (2023-12-04) ------------------ diff --git a/lib/maxmind/GeoIp2/src/LICENSE b/lib/maxmind/GeoIp2/LICENSE similarity index 100% rename from lib/maxmind/GeoIp2/src/LICENSE rename to lib/maxmind/GeoIp2/LICENSE diff --git a/lib/maxmind/GeoIp2/src/README.md b/lib/maxmind/GeoIp2/README.md similarity index 97% rename from lib/maxmind/GeoIp2/src/README.md rename to lib/maxmind/GeoIp2/README.md index 01502e9bd0c..9121b7252bb 100644 --- a/lib/maxmind/GeoIp2/src/README.md +++ b/lib/maxmind/GeoIp2/README.md @@ -266,11 +266,6 @@ You can keep your databases up to date with our [Learn more about GeoIP Update on our developer portal.](https://dev.maxmind.com/geoip/updating-databases?lang=en) -There is also a third-party tool for updating databases using PHP and -Composer. MaxMind does not offer support for this tool or maintain it. -[Learn more about the Geoip2 Update tool for PHP and Composer on its -GitHub page.](https://github.com/tronovav/geoip2-update) - ## Web Service Client ## ### Usage ### @@ -448,6 +443,6 @@ The GeoIP2 PHP API uses [Semantic Versioning](https://semver.org/). ## Copyright and License ## -This software is Copyright (c) 2013-2023 by MaxMind, Inc. +This software is Copyright (c) 2013-2024 by MaxMind, Inc. This is free software, licensed under the Apache License, Version 2.0. diff --git a/lib/maxmind/GeoIp2/composer.json b/lib/maxmind/GeoIp2/composer.json index 60908c7d09b..6d1f07bf61a 100644 --- a/lib/maxmind/GeoIp2/composer.json +++ b/lib/maxmind/GeoIp2/composer.json @@ -13,8 +13,8 @@ } ], "require": { - "maxmind-db/reader": "^1.11.1", - "maxmind/web-service-common": "~0.8", + "maxmind-db/reader": "^1.12.0", + "maxmind/web-service-common": "~0.10", "php": ">=8.1", "ext-json": "*" }, diff --git a/lib/maxmind/GeoIp2/src/Database/Reader.php b/lib/maxmind/GeoIp2/src/Database/Reader.php index 1570afb8dc6..9e3a128a754 100644 --- a/lib/maxmind/GeoIp2/src/Database/Reader.php +++ b/lib/maxmind/GeoIp2/src/Database/Reader.php @@ -16,6 +16,7 @@ use GeoIp2\Model\Isp; use GeoIp2\ProviderInterface; use MaxMind\Db\Reader as DbReader; use MaxMind\Db\Reader\InvalidDatabaseException; +use MaxMind\Db\Reader\Metadata; /** * Instances of this class provide a reader for the GeoIP2 database format. @@ -55,12 +56,11 @@ class Reader implements ProviderInterface /** * Constructor. * - * @param string $filename the path to the GeoIP2 database file - * @param array $locales list of locale codes to use in name property - * from most preferred to least preferred + * @param string $filename the path to the GeoIP2 database file + * @param array $locales list of locale codes to use in name property + * from most preferred to least preferred * - * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database - * is corrupt or invalid + * @throws InvalidDatabaseException if the database is corrupt or invalid */ public function __construct( string $filename, @@ -76,10 +76,8 @@ class Reader implements ProviderInterface * * @param string $ipAddress an IPv4 or IPv6 address as a string * - * @throws \GeoIp2\Exception\AddressNotFoundException if the address is - * not in the database - * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database - * is corrupt or invalid + * @throws AddressNotFoundException if the address is not in the database + * @throws InvalidDatabaseException if the database is corrupt or invalid */ public function city(string $ipAddress): City { @@ -91,10 +89,8 @@ class Reader implements ProviderInterface * * @param string $ipAddress an IPv4 or IPv6 address as a string * - * @throws \GeoIp2\Exception\AddressNotFoundException if the address is - * not in the database - * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database - * is corrupt or invalid + * @throws AddressNotFoundException if the address is not in the database + * @throws InvalidDatabaseException if the database is corrupt or invalid */ public function country(string $ipAddress): Country { @@ -106,10 +102,8 @@ class Reader implements ProviderInterface * * @param string $ipAddress an IPv4 or IPv6 address as a string * - * @throws \GeoIp2\Exception\AddressNotFoundException if the address is - * not in the database - * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database - * is corrupt or invalid + * @throws AddressNotFoundException if the address is not in the database + * @throws InvalidDatabaseException if the database is corrupt or invalid */ public function anonymousIp(string $ipAddress): AnonymousIp { @@ -125,10 +119,8 @@ class Reader implements ProviderInterface * * @param string $ipAddress an IPv4 or IPv6 address as a string * - * @throws \GeoIp2\Exception\AddressNotFoundException if the address is - * not in the database - * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database - * is corrupt or invalid + * @throws AddressNotFoundException if the address is not in the database + * @throws InvalidDatabaseException if the database is corrupt or invalid */ public function asn(string $ipAddress): Asn { @@ -144,10 +136,8 @@ class Reader implements ProviderInterface * * @param string $ipAddress an IPv4 or IPv6 address as a string * - * @throws \GeoIp2\Exception\AddressNotFoundException if the address is - * not in the database - * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database - * is corrupt or invalid + * @throws AddressNotFoundException if the address is not in the database + * @throws InvalidDatabaseException if the database is corrupt or invalid */ public function connectionType(string $ipAddress): ConnectionType { @@ -163,10 +153,8 @@ class Reader implements ProviderInterface * * @param string $ipAddress an IPv4 or IPv6 address as a string * - * @throws \GeoIp2\Exception\AddressNotFoundException if the address is - * not in the database - * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database - * is corrupt or invalid + * @throws AddressNotFoundException if the address is not in the database + * @throws InvalidDatabaseException if the database is corrupt or invalid */ public function domain(string $ipAddress): Domain { @@ -182,10 +170,8 @@ class Reader implements ProviderInterface * * @param string $ipAddress an IPv4 or IPv6 address as a string * - * @throws \GeoIp2\Exception\AddressNotFoundException if the address is - * not in the database - * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database - * is corrupt or invalid + * @throws AddressNotFoundException if the address is not in the database + * @throws InvalidDatabaseException if the database is corrupt or invalid */ public function enterprise(string $ipAddress): Enterprise { @@ -197,10 +183,8 @@ class Reader implements ProviderInterface * * @param string $ipAddress an IPv4 or IPv6 address as a string * - * @throws \GeoIp2\Exception\AddressNotFoundException if the address is - * not in the database - * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database - * is corrupt or invalid + * @throws AddressNotFoundException if the address is not in the database + * @throws InvalidDatabaseException if the database is corrupt or invalid */ public function isp(string $ipAddress): Isp { @@ -231,6 +215,9 @@ class Reader implements ProviderInterface return new $class($record); } + /** + * @return array{0:array, 1:int} + */ private function getRecord(string $class, string $type, string $ipAddress): array { if (!str_contains($this->dbType, $type)) { @@ -267,9 +254,9 @@ class Reader implements ProviderInterface * @throws \InvalidArgumentException if arguments are passed to the method * @throws \BadMethodCallException if the database has been closed * - * @return \MaxMind\Db\Reader\Metadata object for the database + * @return Metadata object for the database */ - public function metadata(): DbReader\Metadata + public function metadata(): Metadata { return $this->dbReader->metadata(); } diff --git a/lib/maxmind/GeoIp2/src/Exception/HttpException.php b/lib/maxmind/GeoIp2/src/Exception/HttpException.php index f56923e2de8..ef6aaf3b4b5 100644 --- a/lib/maxmind/GeoIp2/src/Exception/HttpException.php +++ b/lib/maxmind/GeoIp2/src/Exception/HttpException.php @@ -18,7 +18,7 @@ class HttpException extends GeoIp2Exception string $message, int $httpStatus, string $uri, - \Exception $previous = null + ?\Exception $previous = null ) { $this->uri = $uri; parent::__construct($message, $httpStatus, $previous); diff --git a/lib/maxmind/GeoIp2/src/Exception/InvalidRequestException.php b/lib/maxmind/GeoIp2/src/Exception/InvalidRequestException.php index 5a58ff9513d..e1a1fbfe6b5 100644 --- a/lib/maxmind/GeoIp2/src/Exception/InvalidRequestException.php +++ b/lib/maxmind/GeoIp2/src/Exception/InvalidRequestException.php @@ -20,7 +20,7 @@ class InvalidRequestException extends HttpException string $error, int $httpStatus, string $uri, - \Exception $previous = null + ?\Exception $previous = null ) { $this->error = $error; parent::__construct($message, $httpStatus, $uri, $previous); diff --git a/lib/maxmind/GeoIp2/src/Model/AnonymousIp.php b/lib/maxmind/GeoIp2/src/Model/AnonymousIp.php index 7e3b9b6ea76..e8a99dc5f7d 100644 --- a/lib/maxmind/GeoIp2/src/Model/AnonymousIp.php +++ b/lib/maxmind/GeoIp2/src/Model/AnonymousIp.php @@ -64,6 +64,8 @@ class AnonymousIp implements \JsonSerializable /** * @ignore + * + * @param array $raw */ public function __construct(array $raw) { @@ -78,27 +80,18 @@ class AnonymousIp implements \JsonSerializable $this->network = Util::cidr($ipAddress, $raw['prefix_len']); } + /** + * @return array|null + */ public function jsonSerialize(): ?array { $js = []; - if ($this->isAnonymous !== null) { - $js['is_anonymous'] = $this->isAnonymous; - } - if ($this->isAnonymousVpn !== null) { - $js['is_anonymous_vpn'] = $this->isAnonymousVpn; - } - if ($this->isHostingProvider !== null) { - $js['is_hosting_provider'] = $this->isHostingProvider; - } - if ($this->isPublicProxy !== null) { - $js['is_public_proxy'] = $this->isPublicProxy; - } - if ($this->isResidentialProxy !== null) { - $js['is_residential_proxy'] = $this->isResidentialProxy; - } - if ($this->isTorExitNode !== null) { - $js['is_tor_exit_node'] = $this->isTorExitNode; - } + $js['is_anonymous'] = $this->isAnonymous; + $js['is_anonymous_vpn'] = $this->isAnonymousVpn; + $js['is_hosting_provider'] = $this->isHostingProvider; + $js['is_public_proxy'] = $this->isPublicProxy; + $js['is_residential_proxy'] = $this->isResidentialProxy; + $js['is_tor_exit_node'] = $this->isTorExitNode; $js['ip_address'] = $this->ipAddress; $js['network'] = $this->network; diff --git a/lib/maxmind/GeoIp2/src/Model/Asn.php b/lib/maxmind/GeoIp2/src/Model/Asn.php index 55b85c5e321..5feae2f0f4b 100644 --- a/lib/maxmind/GeoIp2/src/Model/Asn.php +++ b/lib/maxmind/GeoIp2/src/Model/Asn.php @@ -39,6 +39,8 @@ class Asn implements \JsonSerializable /** * @ignore + * + * @param array $raw */ public function __construct(array $raw) { @@ -50,6 +52,9 @@ class Asn implements \JsonSerializable $this->network = Util::cidr($ipAddress, $raw['prefix_len']); } + /** + * @return array|null + */ public function jsonSerialize(): ?array { $js = []; diff --git a/lib/maxmind/GeoIp2/src/Model/City.php b/lib/maxmind/GeoIp2/src/Model/City.php index f89ccfe40d7..bf2323ed45a 100644 --- a/lib/maxmind/GeoIp2/src/Model/City.php +++ b/lib/maxmind/GeoIp2/src/Model/City.php @@ -4,6 +4,11 @@ declare(strict_types=1); namespace GeoIp2\Model; +use GeoIp2\Record\City as CityRecord; +use GeoIp2\Record\Location; +use GeoIp2\Record\Postal; +use GeoIp2\Record\Subdivision; + /** * Model class for the data returned by City Plus web service and City * database. @@ -14,57 +19,63 @@ namespace GeoIp2\Model; class City extends Country { /** - * @var \GeoIp2\Record\City city data for the requested IP - * address + * @var CityRecord city data for the requested IP address */ - public readonly \GeoIp2\Record\City $city; + public readonly CityRecord $city; /** - * @var \GeoIp2\Record\Location location data for the - * requested IP address + * @var Location location data for the requested IP address */ - public readonly \GeoIp2\Record\Location $location; + public readonly Location $location; /** - * @var \GeoIp2\Record\Subdivision An object - * representing the most specific subdivision returned. If the response - * did not contain any subdivisions, this method returns an empty - * \GeoIp2\Record\Subdivision object. + * @var Subdivision An object representing the most specific subdivision + * returned. If the response did not contain any + * subdivisions, this method returns an empty + * \GeoIp2\Record\Subdivision object. */ - public readonly \GeoIp2\Record\Subdivision $mostSpecificSubdivision; + public readonly Subdivision $mostSpecificSubdivision; /** - * @var \GeoIp2\Record\Postal postal data for the - * requested IP address + * @var Postal postal data for the + * requested IP address */ - public readonly \GeoIp2\Record\Postal $postal; + public readonly Postal $postal; /** * @var array<\GeoIp2\Record\Subdivision> An array of \GeoIp2\Record\Subdivision - * objects representing the country subdivisions for the requested IP - * address. The number and type of subdivisions varies by country, but a - * subdivision is typically a state, province, county, etc. Subdivisions - * are ordered from most general (largest) to most specific (smallest). - * If the response did not contain any subdivisions, this method returns - * an empty array. + * objects representing the country + * subdivisions for the requested IP + * address. The number and type of + * subdivisions varies by country, + * but a subdivision is typically a + * state, province, county, etc. + * Subdivisions are ordered from most + * general (largest) to most specific + * (smallest). If the response did + * not contain any subdivisions, this + * method returns an empty array. */ public readonly array $subdivisions; /** * @ignore + * + * @param array $raw + * @param list $locales */ public function __construct(array $raw, array $locales = ['en']) { parent::__construct($raw, $locales); - $this->city = new \GeoIp2\Record\City($raw['city'] ?? [], $locales); - $this->location = new \GeoIp2\Record\Location($raw['location'] ?? []); - $this->postal = new \GeoIp2\Record\Postal($raw['postal'] ?? []); + $this->city = new CityRecord($raw['city'] ?? [], $locales); + $this->location = new Location($raw['location'] ?? []); + $this->postal = new Postal($raw['postal'] ?? []); if (!isset($raw['subdivisions'])) { $this->subdivisions = []; $this->mostSpecificSubdivision = - new \GeoIp2\Record\Subdivision([], $locales); + new Subdivision([], $locales); return; } @@ -72,7 +83,7 @@ class City extends Country $subdivisions = []; foreach ($raw['subdivisions'] as $sub) { $subdivisions[] = - new \GeoIp2\Record\Subdivision($sub, $locales) + new Subdivision($sub, $locales) ; } @@ -82,6 +93,9 @@ class City extends Country $this->subdivisions = $subdivisions; } + /** + * @return array|null + */ public function jsonSerialize(): ?array { $js = parent::jsonSerialize(); diff --git a/lib/maxmind/GeoIp2/src/Model/ConnectionType.php b/lib/maxmind/GeoIp2/src/Model/ConnectionType.php index 6facb5e852b..47e24cc4260 100644 --- a/lib/maxmind/GeoIp2/src/Model/ConnectionType.php +++ b/lib/maxmind/GeoIp2/src/Model/ConnectionType.php @@ -33,6 +33,8 @@ class ConnectionType implements \JsonSerializable /** * @ignore + * + * @param array $raw */ public function __construct(array $raw) { @@ -42,6 +44,9 @@ class ConnectionType implements \JsonSerializable $this->network = Util::cidr($ipAddress, $raw['prefix_len']); } + /** + * @return array|null + */ public function jsonSerialize(): ?array { $js = []; diff --git a/lib/maxmind/GeoIp2/src/Model/Country.php b/lib/maxmind/GeoIp2/src/Model/Country.php index 0fae2db75f0..c41af4a2b97 100644 --- a/lib/maxmind/GeoIp2/src/Model/Country.php +++ b/lib/maxmind/GeoIp2/src/Model/Country.php @@ -4,6 +4,12 @@ declare(strict_types=1); namespace GeoIp2\Model; +use GeoIp2\Record\Continent; +use GeoIp2\Record\Country as CountryRecord; +use GeoIp2\Record\MaxMind; +use GeoIp2\Record\RepresentedCountry; +use GeoIp2\Record\Traits; + /** * Model class for the data returned by GeoIP2 Country web service and database. * @@ -12,70 +18,75 @@ namespace GeoIp2\Model; class Country implements \JsonSerializable { /** - * @var \GeoIp2\Record\Continent continent data for the - * requested IP address + * @var Continent continent data for the requested IP address */ - public readonly \GeoIp2\Record\Continent $continent; + public readonly Continent $continent; /** - * @var \GeoIp2\Record\Country Country data for the requested - * IP address. This object represents the country where MaxMind believes the - * end user is located. + * @var CountryRecord Country data for the requested IP address. This + * object represents the country where MaxMind believes + * the end user is located. */ - public readonly \GeoIp2\Record\Country $country; + public readonly CountryRecord $country; /** - * @var \GeoIp2\Record\MaxMind data related to your MaxMind - * account + * @var MaxMind data related to your MaxMind account */ - public readonly \GeoIp2\Record\MaxMind $maxmind; + public readonly MaxMind $maxmind; /** - * @var \GeoIp2\Record\Country Registered country - * data for the requested IP address. This record represents the country - * where the ISP has registered a given IP block and may differ from the - * user's country. + * @var CountryRecord Registered country data for the requested IP address. + * This record represents the country where the ISP has + * registered a given IP block and may differ from the + * user's country. */ - public readonly \GeoIp2\Record\Country $registeredCountry; + public readonly CountryRecord $registeredCountry; /** - * @var \GeoIp2\Record\RepresentedCountry * Represented country data for the requested IP address. The represented - * country is used for things like military bases. It is only present when - * the represented country differs from the country. + * @var RepresentedCountry Represented country data for the requested IP + * address. The represented country is used for + * things like military bases. It is only present + * when the represented country differs from the + * country. */ - public readonly \GeoIp2\Record\RepresentedCountry $representedCountry; + public readonly RepresentedCountry $representedCountry; /** - * @var \GeoIp2\Record\Traits data for the traits of the - * requested IP address + * @var Traits data for the traits of the requested IP address */ - public readonly \GeoIp2\Record\Traits $traits; + public readonly Traits $traits; /** * @ignore + * + * @param array $raw + * @param list $locales */ public function __construct(array $raw, array $locales = ['en']) { - $this->continent = new \GeoIp2\Record\Continent( + $this->continent = new Continent( $raw['continent'] ?? [], $locales ); - $this->country = new \GeoIp2\Record\Country( + $this->country = new CountryRecord( $raw['country'] ?? [], $locales ); - $this->maxmind = new \GeoIp2\Record\MaxMind($raw['maxmind'] ?? []); - $this->registeredCountry = new \GeoIp2\Record\Country( + $this->maxmind = new MaxMind($raw['maxmind'] ?? []); + $this->registeredCountry = new CountryRecord( $raw['registered_country'] ?? [], $locales ); - $this->representedCountry = new \GeoIp2\Record\RepresentedCountry( + $this->representedCountry = new RepresentedCountry( $raw['represented_country'] ?? [], $locales ); - $this->traits = new \GeoIp2\Record\Traits($raw['traits'] ?? []); + $this->traits = new Traits($raw['traits'] ?? []); } + /** + * @return array|null + */ public function jsonSerialize(): ?array { $js = []; diff --git a/lib/maxmind/GeoIp2/src/Model/Domain.php b/lib/maxmind/GeoIp2/src/Model/Domain.php index 1ce83fdbb09..ed401c8c283 100644 --- a/lib/maxmind/GeoIp2/src/Model/Domain.php +++ b/lib/maxmind/GeoIp2/src/Model/Domain.php @@ -33,6 +33,8 @@ class Domain implements \JsonSerializable /** * @ignore + * + * @param array $raw */ public function __construct(array $raw) { @@ -42,6 +44,9 @@ class Domain implements \JsonSerializable $this->network = Util::cidr($ipAddress, $raw['prefix_len']); } + /** + * @return array|null + */ public function jsonSerialize(): ?array { $js = []; diff --git a/lib/maxmind/GeoIp2/src/Model/Isp.php b/lib/maxmind/GeoIp2/src/Model/Isp.php index f329063e919..6b9130510cc 100644 --- a/lib/maxmind/GeoIp2/src/Model/Isp.php +++ b/lib/maxmind/GeoIp2/src/Model/Isp.php @@ -65,6 +65,8 @@ class Isp implements \JsonSerializable /** * @ignore + * + * @param array $raw */ public function __construct(array $raw) { @@ -81,6 +83,9 @@ class Isp implements \JsonSerializable $this->network = Util::cidr($ipAddress, $raw['prefix_len']); } + /** + * @return array|null + */ public function jsonSerialize(): ?array { $js = []; diff --git a/lib/maxmind/GeoIp2/src/ProviderInterface.php b/lib/maxmind/GeoIp2/src/ProviderInterface.php index 7d148912665..8f40bee5915 100644 --- a/lib/maxmind/GeoIp2/src/ProviderInterface.php +++ b/lib/maxmind/GeoIp2/src/ProviderInterface.php @@ -9,14 +9,14 @@ interface ProviderInterface /** * @param string $ipAddress an IPv4 or IPv6 address to lookup * - * @return \GeoIp2\Model\Country a Country model for the requested IP address + * @return Model\Country a Country model for the requested IP address */ public function country(string $ipAddress): Model\Country; /** * @param string $ipAddress an IPv4 or IPv6 address to lookup * - * @return \GeoIp2\Model\City a City model for the requested IP address + * @return Model\City a City model for the requested IP address */ public function city(string $ipAddress): Model\City; } diff --git a/lib/maxmind/GeoIp2/src/Record/AbstractNamedRecord.php b/lib/maxmind/GeoIp2/src/Record/AbstractNamedRecord.php index 414841d5df3..b28162d6111 100644 --- a/lib/maxmind/GeoIp2/src/Record/AbstractNamedRecord.php +++ b/lib/maxmind/GeoIp2/src/Record/AbstractNamedRecord.php @@ -14,14 +14,17 @@ abstract class AbstractNamedRecord implements \JsonSerializable public readonly ?string $name; /** - * @var array An array map where the keys are locale codes - * and the values are names. This attribute is returned by all location - * services and databases. + * @var array An array map where the keys are locale codes + * and the values are names. This attribute is returned by all location + * services and databases. */ public readonly array $names; /** * @ignore + * + * @param array $record + * @param list $locales */ public function __construct(array $record, array $locales = ['en']) { @@ -37,6 +40,9 @@ abstract class AbstractNamedRecord implements \JsonSerializable $this->name = null; } + /** + * @return array + */ public function jsonSerialize(): array { $js = []; diff --git a/lib/maxmind/GeoIp2/src/Record/AbstractPlaceRecord.php b/lib/maxmind/GeoIp2/src/Record/AbstractPlaceRecord.php index 364d7bb810b..71b68abd66f 100644 --- a/lib/maxmind/GeoIp2/src/Record/AbstractPlaceRecord.php +++ b/lib/maxmind/GeoIp2/src/Record/AbstractPlaceRecord.php @@ -21,6 +21,9 @@ abstract class AbstractPlaceRecord extends AbstractNamedRecord /** * @ignore + * + * @param array $record + * @param list $locales */ public function __construct(array $record, array $locales = ['en']) { @@ -30,6 +33,9 @@ abstract class AbstractPlaceRecord extends AbstractNamedRecord $this->geonameId = $record['geoname_id'] ?? null; } + /** + * @return array + */ public function jsonSerialize(): array { $js = parent::jsonSerialize(); diff --git a/lib/maxmind/GeoIp2/src/Record/Continent.php b/lib/maxmind/GeoIp2/src/Record/Continent.php index 873014ae2b5..380c423c54c 100644 --- a/lib/maxmind/GeoIp2/src/Record/Continent.php +++ b/lib/maxmind/GeoIp2/src/Record/Continent.php @@ -26,6 +26,9 @@ class Continent extends AbstractNamedRecord /** * @ignore + * + * @param array $record + * @param list $locales */ public function __construct(array $record, array $locales = ['en']) { @@ -35,6 +38,9 @@ class Continent extends AbstractNamedRecord $this->geonameId = $record['geoname_id'] ?? null; } + /** + * @return array + */ public function jsonSerialize(): array { $js = parent::jsonSerialize(); diff --git a/lib/maxmind/GeoIp2/src/Record/Country.php b/lib/maxmind/GeoIp2/src/Record/Country.php index 438ebd89cd8..60fd1f90515 100644 --- a/lib/maxmind/GeoIp2/src/Record/Country.php +++ b/lib/maxmind/GeoIp2/src/Record/Country.php @@ -27,6 +27,9 @@ class Country extends AbstractPlaceRecord /** * @ignore + * + * @param array $record + * @param list $locales */ public function __construct(array $record, array $locales = ['en']) { @@ -36,6 +39,9 @@ class Country extends AbstractPlaceRecord $this->isoCode = $record['iso_code'] ?? null; } + /** + * @return array + */ public function jsonSerialize(): array { $js = parent::jsonSerialize(); diff --git a/lib/maxmind/GeoIp2/src/Record/Location.php b/lib/maxmind/GeoIp2/src/Record/Location.php index ca13b52c1e3..6172ac83c64 100644 --- a/lib/maxmind/GeoIp2/src/Record/Location.php +++ b/lib/maxmind/GeoIp2/src/Record/Location.php @@ -64,6 +64,11 @@ class Location implements \JsonSerializable */ public readonly ?string $timeZone; + /** + * @ignore + * + * @param array $record + */ public function __construct(array $record) { $this->averageIncome = $record['average_income'] ?? null; @@ -75,6 +80,9 @@ class Location implements \JsonSerializable $this->timeZone = $record['time_zone'] ?? null; } + /** + * @return array + */ public function jsonSerialize(): array { $js = []; diff --git a/lib/maxmind/GeoIp2/src/Record/MaxMind.php b/lib/maxmind/GeoIp2/src/Record/MaxMind.php index e80334d5846..b2c33d9c89f 100644 --- a/lib/maxmind/GeoIp2/src/Record/MaxMind.php +++ b/lib/maxmind/GeoIp2/src/Record/MaxMind.php @@ -17,11 +17,19 @@ class MaxMind implements \JsonSerializable */ public readonly ?int $queriesRemaining; + /** + * @ignore + * + * @param array $record + */ public function __construct(array $record) { $this->queriesRemaining = $record['queries_remaining'] ?? null; } + /** + * @return array + */ public function jsonSerialize(): array { $js = []; diff --git a/lib/maxmind/GeoIp2/src/Record/Postal.php b/lib/maxmind/GeoIp2/src/Record/Postal.php index d3adac6f293..cea2f2ae566 100644 --- a/lib/maxmind/GeoIp2/src/Record/Postal.php +++ b/lib/maxmind/GeoIp2/src/Record/Postal.php @@ -30,6 +30,8 @@ class Postal implements \JsonSerializable /** * @ignore + * + * @param array $record */ public function __construct(array $record) { @@ -37,6 +39,9 @@ class Postal implements \JsonSerializable $this->confidence = $record['confidence'] ?? null; } + /** + * @return array + */ public function jsonSerialize(): array { $js = []; diff --git a/lib/maxmind/GeoIp2/src/Record/RepresentedCountry.php b/lib/maxmind/GeoIp2/src/Record/RepresentedCountry.php index a810fc633a0..67f24b08fac 100644 --- a/lib/maxmind/GeoIp2/src/Record/RepresentedCountry.php +++ b/lib/maxmind/GeoIp2/src/Record/RepresentedCountry.php @@ -22,6 +22,9 @@ class RepresentedCountry extends Country /** * @ignore + * + * @param array $record + * @param list $locales */ public function __construct(array $record, array $locales = ['en']) { @@ -30,6 +33,9 @@ class RepresentedCountry extends Country $this->type = $record['type'] ?? null; } + /** + * @return array + */ public function jsonSerialize(): array { $js = parent::jsonSerialize(); diff --git a/lib/maxmind/GeoIp2/src/Record/Subdivision.php b/lib/maxmind/GeoIp2/src/Record/Subdivision.php index cd11e58bb54..3719a6c3515 100644 --- a/lib/maxmind/GeoIp2/src/Record/Subdivision.php +++ b/lib/maxmind/GeoIp2/src/Record/Subdivision.php @@ -22,6 +22,9 @@ class Subdivision extends AbstractPlaceRecord /** * @ignore + * + * @param array $record + * @param list $locales */ public function __construct(array $record, array $locales = ['en']) { @@ -30,6 +33,9 @@ class Subdivision extends AbstractPlaceRecord $this->isoCode = $record['iso_code'] ?? null; } + /** + * @return array + */ public function jsonSerialize(): array { $js = parent::jsonSerialize(); diff --git a/lib/maxmind/GeoIp2/src/Record/Traits.php b/lib/maxmind/GeoIp2/src/Record/Traits.php index 493f86ae0b1..d75a8f71100 100644 --- a/lib/maxmind/GeoIp2/src/Record/Traits.php +++ b/lib/maxmind/GeoIp2/src/Record/Traits.php @@ -196,6 +196,11 @@ class Traits implements \JsonSerializable */ public readonly ?string $userType; + /** + * @ignore + * + * @param array $record + */ public function __construct(array $record) { $this->autonomousSystemNumber = $record['autonomous_system_number'] ?? null; @@ -226,6 +231,9 @@ class Traits implements \JsonSerializable } } + /** + * @return array + */ public function jsonSerialize(): array { $js = []; diff --git a/lib/maxmind/GeoIp2/src/WebService/Client.php b/lib/maxmind/GeoIp2/src/WebService/Client.php index 1e0954195b5..fb3966eff4c 100644 --- a/lib/maxmind/GeoIp2/src/WebService/Client.php +++ b/lib/maxmind/GeoIp2/src/WebService/Client.php @@ -14,6 +14,9 @@ use GeoIp2\Model\City; use GeoIp2\Model\Country; use GeoIp2\Model\Insights; use GeoIp2\ProviderInterface; +use MaxMind\Exception\InsufficientFundsException; +use MaxMind\Exception\IpAddressNotFoundException; +use MaxMind\Exception\WebServiceException; use MaxMind\WebService\Client as WsClient; /** @@ -55,30 +58,30 @@ class Client implements ProviderInterface private WsClient $client; private static string $basePath = '/geoip/v2.1'; - public const VERSION = 'v3.0.0'; + public const VERSION = 'v3.1.0'; /** * Constructor. * - * @param int $accountId your MaxMind account ID - * @param string $licenseKey your MaxMind license key - * @param array $locales list of locale codes to use in name property - * from most preferred to least preferred - * @param array $options array of options. Valid options include: - * * `host` - The host to use when querying the web - * service. To query the GeoLite2 web service - * instead of the GeoIP2 web service, set the - * host to `geolite.info`. To query the Sandbox - * GeoIP2 web service instead of the production - * GeoIP2 web service, set the host to - * `sandbox.maxmind.com`. The sandbox allows you to - * experiment with the API without affecting your - * production data. - * * `timeout` - Timeout in seconds. - * * `connectTimeout` - Initial connection timeout in seconds. - * * `proxy` - The HTTP proxy to use. May include a schema, port, - * username, and password, e.g., - * `http://username:password@127.0.0.1:10`. + * @param int $accountId your MaxMind account ID + * @param string $licenseKey your MaxMind license key + * @param list $locales list of locale codes to use in name property + * from most preferred to least preferred + * @param array $options array of options. Valid options include: + * * `host` - The host to use when querying the web + * service. To query the GeoLite2 web service + * instead of the GeoIP2 web service, set the + * host to `geolite.info`. To query the Sandbox + * GeoIP2 web service instead of the production + * GeoIP2 web service, set the host to + * `sandbox.maxmind.com`. The sandbox allows you to + * experiment with the API without affecting your + * production data. + * * `timeout` - Timeout in seconds. + * * `connectTimeout` - Initial connection timeout in seconds. + * * `proxy` - The HTTP proxy to use. May include a schema, port, + * username, and password, e.g., + * `http://username:password@127.0.0.1:10`. */ public function __construct( int $accountId, @@ -116,24 +119,23 @@ class Client implements ProviderInterface * address is provided, the address that the web service is called * from will be used. * - * @throws \GeoIp2\Exception\AddressNotFoundException if the address you - * provided is not in our database (e.g., a private address). - * @throws \GeoIp2\Exception\AuthenticationException if there is a problem - * with the account ID or license key that you provided - * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out - * of queries - * @throws \GeoIp2\Exception\InvalidRequestException} if your request was received by the web service but is - * invalid for some other reason. This may indicate an issue - * with this API. Please report the error to MaxMind. - * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error code or message was returned. - * This could indicate a problem with the connection between - * your server and the web service or that the web service - * returned an invalid document or 500 error code - * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent - * class to the above exceptions. It will be thrown directly - * if a 200 status code is returned but the body is invalid. - * @throws \InvalidArgumentException if something other than a single IP address or "me" is - * passed to the method + * @throws AddressNotFoundException if the address you provided is not in our database (e.g., + * a private address). + * @throws AuthenticationException if there is a problem with the account ID or license key + * that you provided + * @throws OutOfQueriesException if your account is out of queries + * @throws InvalidRequestException if your request was received by the web service but is + * invalid for some other reason. This may indicate an issue + * with this API. Please report the error to MaxMind. + * @throws HttpException if an unexpected HTTP error code or message was returned. + * This could indicate a problem with the connection between + * your server and the web service or that the web service + * returned an invalid document or 500 error code + * @throws GeoIp2Exception This serves as the parent + * class to the above exceptions. It will be thrown directly + * if a 200 status code is returned but the body is invalid. + * @throws \InvalidArgumentException if something other than a single IP address or "me" is + * passed to the method */ public function city(string $ipAddress = 'me'): City { @@ -148,24 +150,23 @@ class Client implements ProviderInterface * address is provided, the address that the web service is called * from will be used. * - * @throws \GeoIp2\Exception\AddressNotFoundException if the address you provided is not in our database (e.g., - * a private address). - * @throws \GeoIp2\Exception\AuthenticationException if there is a problem - * with the account ID or license key that you provided - * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out of queries - * @throws \GeoIp2\Exception\InvalidRequestException} if your request was received by the web service but is - * invalid for some other reason. This may indicate an - * issue with this API. Please report the error to MaxMind. - * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error - * code or message was returned. This could indicate a problem - * with the connection between your server and the web service - * or that the web service returned an invalid document or 500 - * error code. - * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent class to the above exceptions. It - * will be thrown directly if a 200 status code is returned but - * the body is invalid. - * @throws \InvalidArgumentException if something other than a single IP address or "me" is - * passed to the method + * @throws AddressNotFoundException if the address you provided is not in our database (e.g., + * a private address). + * @throws AuthenticationException if there is a problem with the account ID or license key that you provided + * @throws OutOfQueriesException if your account is out of queries + * @throws InvalidRequestException if your request was received by the web service but is + * invalid for some other reason. This may indicate an + * issue with this API. Please report the error to MaxMind. + * @throws HttpException if an unexpected HTTP error + * code or message was returned. This could indicate a problem + * with the connection between your server and the web service + * or that the web service returned an invalid document or 500 + * error code. + * @throws GeoIp2Exception This serves as the parent class to the above exceptions. It + * will be thrown directly if a 200 status code is returned but + * the body is invalid. + * @throws \InvalidArgumentException if something other than a single IP address or "me" is + * passed to the method */ public function country(string $ipAddress = 'me'): Country { @@ -180,24 +181,23 @@ class Client implements ProviderInterface * address is provided, the address that the web service is called * from will be used. * - * @throws \GeoIp2\Exception\AddressNotFoundException if the address you - * provided is not in our database (e.g., a private address). - * @throws \GeoIp2\Exception\AuthenticationException if there is a problem - * with the account ID or license key that you provided - * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out - * of queries - * @throws \GeoIp2\Exception\InvalidRequestException} if your request was received by the web service but is - * invalid for some other reason. This may indicate an - * issue with this API. Please report the error to MaxMind. - * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error code or message was returned. - * This could indicate a problem with the connection between - * your server and the web service or that the web service - * returned an invalid document or 500 error code - * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent - * class to the above exceptions. It will be thrown directly - * if a 200 status code is returned but the body is invalid. - * @throws \InvalidArgumentException if something other than a single IP address or "me" is - * passed to the method + * @throws AddressNotFoundException if the address you provided is not in our database (e.g., + * a private address). + * @throws AuthenticationException if there is a problem with the account ID or license key + * that you provided + * @throws OutOfQueriesException if your account is out of queries + * @throws InvalidRequestException if your request was received by the web service but is + * invalid for some other reason. This may indicate an + * issue with this API. Please report the error to MaxMind. + * @throws HttpException if an unexpected HTTP error code or message was returned. + * This could indicate a problem with the connection between + * your server and the web service or that the web service + * returned an invalid document or 500 error code + * @throws GeoIp2Exception This serves as the parent + * class to the above exceptions. It will be thrown directly + * if a 200 status code is returned but the body is invalid. + * @throws \InvalidArgumentException if something other than a single IP address or "me" is + * passed to the method */ public function insights(string $ipAddress = 'me'): Insights { @@ -217,7 +217,7 @@ class Client implements ProviderInterface try { $service = (new \ReflectionClass($class))->getShortName(); $body = $this->client->get('GeoIP2 ' . $service, $path); - } catch (\MaxMind\Exception\IpAddressNotFoundException $ex) { + } catch (IpAddressNotFoundException $ex) { throw new AddressNotFoundException( $ex->getMessage(), $ex->getStatusCode(), @@ -229,7 +229,7 @@ class Client implements ProviderInterface $ex->getStatusCode(), $ex ); - } catch (\MaxMind\Exception\InsufficientFundsException $ex) { + } catch (InsufficientFundsException $ex) { throw new OutOfQueriesException( $ex->getMessage(), $ex->getStatusCode(), @@ -250,7 +250,7 @@ class Client implements ProviderInterface $ex->getUri(), $ex ); - } catch (\MaxMind\Exception\WebServiceException $ex) { + } catch (WebServiceException $ex) { throw new GeoIp2Exception( $ex->getMessage(), $ex->getCode(), diff --git a/lib/maxmind/MaxMind/CHANGELOG.md b/lib/maxmind/MaxMind/CHANGELOG.md index 15e5361797b..40d89d42ead 100644 --- a/lib/maxmind/MaxMind/CHANGELOG.md +++ b/lib/maxmind/MaxMind/CHANGELOG.md @@ -1,6 +1,13 @@ CHANGELOG ========= +1.12.0 (2024-11-14) +------------------- + +* Improve the error handling when the user tries to open a directory + with the pure PHP reader. +* Improve the typehints on arrays in the PHPDocs. + 1.11.1 (2023-12-01) ------------------- diff --git a/lib/maxmind/MaxMind/README.md b/lib/maxmind/MaxMind/README.md index fa5693a2968..afefc3fd129 100644 --- a/lib/maxmind/MaxMind/README.md +++ b/lib/maxmind/MaxMind/README.md @@ -180,6 +180,6 @@ The MaxMind DB Reader PHP API uses [Semantic Versioning](https://semver.org/). ## Copyright and License ## -This software is Copyright (c) 2014-2023 by MaxMind, Inc. +This software is Copyright (c) 2014-2024 by MaxMind, Inc. This is free software, licensed under the Apache License, Version 2.0. diff --git a/lib/maxmind/MaxMind/composer.json b/lib/maxmind/MaxMind/composer.json index 22ea7975230..c9cc524e26b 100644 --- a/lib/maxmind/MaxMind/composer.json +++ b/lib/maxmind/MaxMind/composer.json @@ -21,13 +21,11 @@ "ext-maxminddb": "A C-based database decoder that provides significantly faster lookups" }, "conflict": { - "ext-maxminddb": "<1.11.1,>=2.0.0" + "ext-maxminddb": "<1.11.1 || >=2.0.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "3.*", "phpunit/phpunit": ">=8.0.0,<10.0.0", - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpcov": ">=6.0.0", "squizlabs/php_codesniffer": "3.*", "phpstan/phpstan": "*" }, diff --git a/lib/maxmind/MaxMind/src/MaxMind/Db/Reader.php b/lib/maxmind/MaxMind/src/MaxMind/Db/Reader.php index 1f35e7a4f94..12c6b0950d0 100644 --- a/lib/maxmind/MaxMind/src/MaxMind/Db/Reader.php +++ b/lib/maxmind/MaxMind/src/MaxMind/Db/Reader.php @@ -64,8 +64,7 @@ class Reader * Constructs a Reader for the MaxMind DB format. The file passed to it must * be a valid MaxMind DB file such as a GeoIp2 database file. * - * @param string $database - * the MaxMind DB file to use + * @param string $database the MaxMind DB file to use * * @throws \InvalidArgumentException for invalid database path or unknown arguments * @throws InvalidDatabaseException @@ -76,7 +75,14 @@ class Reader { if (\func_num_args() !== 1) { throw new \ArgumentCountError( - sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()) + \sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()) + ); + } + + if (is_dir($database)) { + // This matches the error that the C extension throws. + throw new InvalidDatabaseException( + "Error opening database file ($database). Is this a valid MaxMind DB file?" ); } @@ -110,8 +116,7 @@ class Reader /** * Retrieves the record for the IP address. * - * @param string $ipAddress - * the IP address to look up + * @param string $ipAddress the IP address to look up * * @throws \BadMethodCallException if this method is called on a closed database * @throws \InvalidArgumentException if something other than a single IP address is passed to the method @@ -125,7 +130,7 @@ class Reader { if (\func_num_args() !== 1) { throw new \ArgumentCountError( - sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()) + \sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()) ); } [$record] = $this->getWithPrefixLen($ipAddress); @@ -136,8 +141,7 @@ class Reader /** * Retrieves the record for the IP address and its associated network prefix length. * - * @param string $ipAddress - * the IP address to look up + * @param string $ipAddress the IP address to look up * * @throws \BadMethodCallException if this method is called on a closed database * @throws \InvalidArgumentException if something other than a single IP address is passed to the method @@ -145,14 +149,14 @@ class Reader * if the database is invalid or there is an error reading * from it * - * @return array an array where the first element is the record and the - * second the network prefix length for the record + * @return array{0:mixed, 1:int} an array where the first element is the record and the + * second the network prefix length for the record */ public function getWithPrefixLen(string $ipAddress): array { if (\func_num_args() !== 1) { throw new \ArgumentCountError( - sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()) + \sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()) ); } @@ -170,6 +174,9 @@ class Reader return [$this->resolveDataPointer($pointer), $prefixLen]; } + /** + * @return array{0:int, 1:int} + */ private function findAddressInTree(string $ipAddress): array { $packedAddr = @inet_pton($ipAddress); @@ -364,7 +371,7 @@ class Reader { if (\func_num_args()) { throw new \ArgumentCountError( - sprintf('%s() expects exactly 0 parameters, %d given', __METHOD__, \func_num_args()) + \sprintf('%s() expects exactly 0 parameters, %d given', __METHOD__, \func_num_args()) ); } @@ -389,7 +396,7 @@ class Reader { if (\func_num_args()) { throw new \ArgumentCountError( - sprintf('%s() expects exactly 0 parameters, %d given', __METHOD__, \func_num_args()) + \sprintf('%s() expects exactly 0 parameters, %d given', __METHOD__, \func_num_args()) ); } diff --git a/lib/maxmind/MaxMind/src/MaxMind/Db/Reader/Decoder.php b/lib/maxmind/MaxMind/src/MaxMind/Db/Reader/Decoder.php index 44eee622ab1..1bb6731671c 100644 --- a/lib/maxmind/MaxMind/src/MaxMind/Db/Reader/Decoder.php +++ b/lib/maxmind/MaxMind/src/MaxMind/Db/Reader/Decoder.php @@ -63,6 +63,9 @@ class Decoder $this->switchByteOrder = $this->isPlatformLittleEndian(); } + /** + * @return array + */ public function decode(int $offset): array { $ctrlByte = \ord(Util::read($this->fileStream, $offset, 1)); @@ -110,6 +113,8 @@ class Decoder /** * @param int<0, max> $size + * + * @return array{0:mixed, 1:int} */ private function decodeByType(int $type, int $offset, int $size): array { @@ -167,6 +172,9 @@ class Decoder } } + /** + * @return array{0:array, 1:int} + */ private function decodeArray(int $size, int $offset): array { $array = []; @@ -247,6 +255,9 @@ class Decoder return $int; } + /** + * @return array{0:array, 1:int} + */ private function decodeMap(int $size, int $offset): array { $map = []; @@ -260,6 +271,9 @@ class Decoder return [$map, $offset]; } + /** + * @return array{0:int, 1:int} + */ private function decodePointer(int $ctrlByte, int $offset): array { $pointerSize = (($ctrlByte >> 3) & 0x3) + 1; @@ -378,6 +392,9 @@ class Decoder return $integerAsString; } + /** + * @return array{0:int, 1:int} + */ private function sizeFromCtrlByte(int $ctrlByte, int $offset): array { $size = $ctrlByte & 0x1F; diff --git a/lib/maxmind/MaxMind/src/MaxMind/Db/Reader/Metadata.php b/lib/maxmind/MaxMind/src/MaxMind/Db/Reader/Metadata.php index 97ef48821c4..9cade222118 100644 --- a/lib/maxmind/MaxMind/src/MaxMind/Db/Reader/Metadata.php +++ b/lib/maxmind/MaxMind/src/MaxMind/Db/Reader/Metadata.php @@ -48,7 +48,7 @@ class Metadata * in that language as a UTF-8 string. May be undefined for some * databases. * - * @var array + * @var array */ public $description; @@ -65,7 +65,7 @@ class Metadata * may contain data items that have been localized to some or all of * these languages. This may be undefined. * - * @var array + * @var array */ public $languages; @@ -95,11 +95,14 @@ class Metadata */ public $searchTreeSize; + /** + * @param array $metadata + */ public function __construct(array $metadata) { if (\func_num_args() !== 1) { throw new \ArgumentCountError( - sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()) + \sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()) ); } diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml index 09e28417e87..a531ed460de 100644 --- a/lib/thirdpartylibs.xml +++ b/lib/thirdpartylibs.xml @@ -388,7 +388,7 @@ All rights reserved. maxmind/GeoIp2 GeoIP2 PHP API Library for processing of GeoIP data files. - 3.0.0 + 3.1.0 Apache 2.0 https://github.com/maxmind/GeoIP2-php @@ -400,7 +400,7 @@ All rights reserved. maxmind/MaxMind MaxMind DB Reader API PHP API for reading MaxMind DB files. - 1.11.1 + 1.12.0 Apache 2.0 https://github.com/maxmind/MaxMind-DB-Reader-php/