diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 19ba7ffa..7b22bd53 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.2 - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c763ecf9..14ea6937 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest, windows-latest, macos-latest] - php-versions: ['8.0', '8.1'] + php-versions: ['8.1', '8.2'] name: "PHP ${{ matrix.php-versions }} test on ${{ matrix.operating-system }}" steps: - name: Setup PHP @@ -33,4 +33,4 @@ jobs: run: composer install --no-progress --prefer-dist --optimize-autoloader - name: Test with phpunit - run: vendor/bin/phpunit --coverage-text + run: vendor/bin/phpunit diff --git a/CHANGELOG.md b/CHANGELOG.md index 72e83e96..537221c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,14 @@ CHANGELOG 3.0.0 ------------------- -* IMPORTANT: PHP 8.0 or greater is now required. +* IMPORTANT: PHP 8.1 or greater is now required. +* BREAKING: Read-only properties are now used for the model and record + classes rather than magic methods. This significantly improves performance. +* BREAKING: The `raw` property on model classess and the `record` property on + record classes have been removed. +* BREAKING: On `GeoIp2\Record\Traits`, the deprecated `isAnonymousProxy` and + `isSatelliteProvider` properties have been removed. +* BREAKING: The `jsonSerialize` output has changed. * `GeoIp2\WebService\Client` methods now throw an `InvalidArgumentException` if an invalid IP address is passed to them. Previously, they would make a request to the web service and throw a diff --git a/README.md b/README.md index f92a4162..01502e9b 100644 --- a/README.md +++ b/README.md @@ -430,7 +430,7 @@ to the client API, please see ## Requirements ## -This library requires PHP 8.0 or greater. +This library requires PHP 8.1 or greater. This library also relies on the [MaxMind DB Reader](https://github.com/maxmind/MaxMind-DB-Reader-php). diff --git a/composer.json b/composer.json index c7365a43..74616276 100644 --- a/composer.json +++ b/composer.json @@ -15,12 +15,12 @@ "require": { "maxmind-db/reader": "~1.8", "maxmind/web-service-common": "~0.8", - "php": ">=8.0", + "php": ">=8.1", "ext-json": "*" }, "require-dev": { "friendsofphp/php-cs-fixer": "3.*", - "phpunit/phpunit": "^8.0 || ^9.0", + "phpunit/phpunit": "^10.0", "squizlabs/php_codesniffer": "3.*", "phpstan/phpstan": "*" }, diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5125feff..88df9f13 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,20 +1,8 @@ - - - - - ./tests/GeoIp2/Test/ - - - - - - ./src/GeoIp2/ - - - - - - - + + + + ./tests/GeoIp2/Test/ + + diff --git a/src/Database/Reader.php b/src/Database/Reader.php index 0030e370..1570afb8 100644 --- a/src/Database/Reader.php +++ b/src/Database/Reader.php @@ -5,7 +5,6 @@ namespace GeoIp2\Database; use GeoIp2\Exception\AddressNotFoundException; -use GeoIp2\Model\AbstractModel; use GeoIp2\Model\AnonymousIp; use GeoIp2\Model\Asn; use GeoIp2\Model\City; @@ -84,7 +83,6 @@ public function __construct( */ public function city(string $ipAddress): City { - // @phpstan-ignore-next-line return $this->modelFor(City::class, 'City', $ipAddress); } @@ -100,7 +98,6 @@ public function city(string $ipAddress): City */ public function country(string $ipAddress): Country { - // @phpstan-ignore-next-line return $this->modelFor(Country::class, 'Country', $ipAddress); } @@ -116,7 +113,6 @@ public function country(string $ipAddress): Country */ public function anonymousIp(string $ipAddress): AnonymousIp { - // @phpstan-ignore-next-line return $this->flatModelFor( AnonymousIp::class, 'GeoIP2-Anonymous-IP', @@ -136,7 +132,6 @@ public function anonymousIp(string $ipAddress): AnonymousIp */ public function asn(string $ipAddress): Asn { - // @phpstan-ignore-next-line return $this->flatModelFor( Asn::class, 'GeoLite2-ASN', @@ -156,7 +151,6 @@ public function asn(string $ipAddress): Asn */ public function connectionType(string $ipAddress): ConnectionType { - // @phpstan-ignore-next-line return $this->flatModelFor( ConnectionType::class, 'GeoIP2-Connection-Type', @@ -176,7 +170,6 @@ public function connectionType(string $ipAddress): ConnectionType */ public function domain(string $ipAddress): Domain { - // @phpstan-ignore-next-line return $this->flatModelFor( Domain::class, 'GeoIP2-Domain', @@ -196,7 +189,6 @@ public function domain(string $ipAddress): Domain */ public function enterprise(string $ipAddress): Enterprise { - // @phpstan-ignore-next-line return $this->modelFor(Enterprise::class, 'Enterprise', $ipAddress); } @@ -212,7 +204,6 @@ public function enterprise(string $ipAddress): Enterprise */ public function isp(string $ipAddress): Isp { - // @phpstan-ignore-next-line return $this->flatModelFor( Isp::class, 'GeoIP2-ISP', @@ -220,7 +211,7 @@ public function isp(string $ipAddress): Isp ); } - private function modelFor(string $class, string $type, string $ipAddress): AbstractModel + private function modelFor(string $class, string $type, string $ipAddress): object { [$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress); @@ -230,7 +221,7 @@ private function modelFor(string $class, string $type, string $ipAddress): Abstr return new $class($record, $this->locales); } - private function flatModelFor(string $class, string $type, string $ipAddress): AbstractModel + private function flatModelFor(string $class, string $type, string $ipAddress): object { [$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress); diff --git a/src/Model/AbstractModel.php b/src/Model/AbstractModel.php deleted file mode 100644 index f44feaeb..00000000 --- a/src/Model/AbstractModel.php +++ /dev/null @@ -1,68 +0,0 @@ - - */ - protected array $raw; - - /** - * @ignore - */ - public function __construct(array $raw) - { - $this->raw = $raw; - } - - /** - * @ignore - * - * @return mixed - */ - protected function get(string $field) - { - if (isset($this->raw[$field])) { - return $this->raw[$field]; - } - if (preg_match('/^is_/', $field)) { - return false; - } - - return null; - } - - /** - * @ignore - * - * @return mixed - */ - public function __get(string $attr) - { - if ($attr !== 'instance' && property_exists($this, $attr)) { - return $this->{$attr}; - } - - throw new \RuntimeException("Unknown attribute: $attr"); - } - - /** - * @ignore - */ - public function __isset(string $attr): bool - { - return $attr !== 'instance' && isset($this->{$attr}); - } - - public function jsonSerialize(): array - { - return $this->raw; - } -} diff --git a/src/Model/AnonymousIp.php b/src/Model/AnonymousIp.php index da566146..7e3b9b6e 100644 --- a/src/Model/AnonymousIp.php +++ b/src/Model/AnonymousIp.php @@ -8,53 +8,100 @@ /** * This class provides the GeoIP2 Anonymous IP model. - * - * @property-read bool $isAnonymous This is true if the IP address belongs to - * any sort of anonymous network. - * @property-read bool $isAnonymousVpn This is true if the IP address is - * registered to an anonymous VPN provider. If a VPN provider does not - * register subnets under names associated with them, we will likely only - * flag their IP ranges using the isHostingProvider property. - * @property-read bool $isHostingProvider This is true if the IP address belongs - * to a hosting or VPN provider (see description of isAnonymousVpn property). - * @property-read bool $isPublicProxy This is true if the IP address belongs to - * a public proxy. - * @property-read bool $isResidentialProxy This is true if the IP address is - * on a suspected anonymizing network and belongs to a residential ISP. - * @property-read bool $isTorExitNode This is true if the IP address is a Tor - * exit node. - * @property-read string $ipAddress The IP address that the data in the model is - * for. - * @property-read string $network The network in CIDR notation associated with - * the record. In particular, this is the largest network where all of the - * fields besides $ipAddress have the same value. */ -class AnonymousIp extends AbstractModel +class AnonymousIp implements \JsonSerializable { - protected bool $isAnonymous; - protected bool $isAnonymousVpn; - protected bool $isHostingProvider; - protected bool $isPublicProxy; - protected bool $isResidentialProxy; - protected bool $isTorExitNode; - protected string $ipAddress; - protected string $network; + /** + * @var bool this is true if the IP address belongs to + * any sort of anonymous network + */ + public readonly bool $isAnonymous; + + /** + * @var bool This is true if the IP address is + * registered to an anonymous VPN provider. If a VPN provider does not + * register subnets under names associated with them, we will likely only + * flag their IP ranges using the isHostingProvider property. + */ + public readonly bool $isAnonymousVpn; + + /** + * @var bool this is true if the IP address belongs + * to a hosting or VPN provider (see description of isAnonymousVpn property) + */ + public readonly bool $isHostingProvider; + + /** + * @var bool this is true if the IP address belongs to + * a public proxy + */ + public readonly bool $isPublicProxy; + + /** + * @var bool this is true if the IP address is + * on a suspected anonymizing network and belongs to a residential ISP + */ + public readonly bool $isResidentialProxy; + + /** + * @var bool this is true if the IP address is a Tor + * exit node + */ + public readonly bool $isTorExitNode; + + /** + * @var string the IP address that the data in the model is + * for + */ + public readonly string $ipAddress; + + /** + * @var string The network in CIDR notation associated with + * the record. In particular, this is the largest network where all of the + * fields besides $ipAddress have the same value. + */ + public readonly string $network; /** * @ignore */ public function __construct(array $raw) { - parent::__construct($raw); - - $this->isAnonymous = $this->get('is_anonymous'); - $this->isAnonymousVpn = $this->get('is_anonymous_vpn'); - $this->isHostingProvider = $this->get('is_hosting_provider'); - $this->isPublicProxy = $this->get('is_public_proxy'); - $this->isResidentialProxy = $this->get('is_residential_proxy'); - $this->isTorExitNode = $this->get('is_tor_exit_node'); - $ipAddress = $this->get('ip_address'); + $this->isAnonymous = $raw['is_anonymous'] ?? false; + $this->isAnonymousVpn = $raw['is_anonymous_vpn'] ?? false; + $this->isHostingProvider = $raw['is_hosting_provider'] ?? false; + $this->isPublicProxy = $raw['is_public_proxy'] ?? false; + $this->isResidentialProxy = $raw['is_residential_proxy'] ?? false; + $this->isTorExitNode = $raw['is_tor_exit_node'] ?? false; + $ipAddress = $raw['ip_address']; $this->ipAddress = $ipAddress; - $this->network = Util::cidr($ipAddress, $this->get('prefix_len')); + $this->network = Util::cidr($ipAddress, $raw['prefix_len']); + } + + 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['ip_address'] = $this->ipAddress; + $js['network'] = $this->network; + + return $js; } } diff --git a/src/Model/Asn.php b/src/Model/Asn.php index 42e9a4a2..55b85c5e 100644 --- a/src/Model/Asn.php +++ b/src/Model/Asn.php @@ -8,36 +8,61 @@ /** * This class provides the GeoLite2 ASN model. - * - * @property-read int|null $autonomousSystemNumber The autonomous system number - * associated with the IP address. - * @property-read string|null $autonomousSystemOrganization The organization - * associated with the registered autonomous system number for the IP - * address. - * @property-read string $ipAddress The IP address that the data in the model is - * for. - * @property-read string $network The network in CIDR notation associated with - * the record. In particular, this is the largest network where all of the - * fields besides $ipAddress have the same value. */ -class Asn extends AbstractModel +class Asn implements \JsonSerializable { - protected ?int $autonomousSystemNumber; - protected ?string $autonomousSystemOrganization; - protected string $ipAddress; - protected string $network; + /** + * @var int|null the autonomous system number + * associated with the IP address + */ + public readonly ?int $autonomousSystemNumber; + + /** + * @var string|null the organization + * associated with the registered autonomous system number for the IP + * address + */ + public readonly ?string $autonomousSystemOrganization; + + /** + * @var string the IP address that the data in the model is + * for + */ + public readonly string $ipAddress; + + /** + * @var string The network in CIDR notation associated with + * the record. In particular, this is the largest network where all of the + * fields besides $ipAddress have the same value. + */ + public readonly string $network; /** * @ignore */ public function __construct(array $raw) { - parent::__construct($raw); - $this->autonomousSystemNumber = $this->get('autonomous_system_number'); + $this->autonomousSystemNumber = $raw['autonomous_system_number'] ?? null; $this->autonomousSystemOrganization = - $this->get('autonomous_system_organization'); - $ipAddress = $this->get('ip_address'); + $raw['autonomous_system_organization'] ?? null; + $ipAddress = $raw['ip_address']; $this->ipAddress = $ipAddress; - $this->network = Util::cidr($ipAddress, $this->get('prefix_len')); + $this->network = Util::cidr($ipAddress, $raw['prefix_len']); + } + + public function jsonSerialize(): ?array + { + $js = []; + + if ($this->autonomousSystemNumber !== null) { + $js['autonomous_system_number'] = $this->autonomousSystemNumber; + } + if ($this->autonomousSystemOrganization !== null) { + $js['autonomous_system_organization'] = $this->autonomousSystemOrganization; + } + $js['ip_address'] = $this->ipAddress; + $js['network'] = $this->network; + + return $js; } } diff --git a/src/Model/City.php b/src/Model/City.php index 19f2b2c4..f89ccfe4 100644 --- a/src/Model/City.php +++ b/src/Model/City.php @@ -10,48 +10,45 @@ * * See https://dev.maxmind.com/geoip/docs/web-services?lang=en for more * details. - * - * @property-read \GeoIp2\Record\City $city City data for the requested IP - * address. - * @property-read \GeoIp2\Record\Location $location Location data for the - * requested IP address. - * @property-read \GeoIp2\Record\Postal $postal Postal data for the - * requested IP address. - * @property-read array $subdivisions An array \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. - * @property-read \GeoIp2\Record\Subdivision $mostSpecificSubdivision 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. */ class City extends Country { /** - * @ignore + * @var \GeoIp2\Record\City city data for the requested IP + * address */ - protected \GeoIp2\Record\City $city; + public readonly \GeoIp2\Record\City $city; /** - * @ignore + * @var \GeoIp2\Record\Location location data for the + * requested IP address */ - protected \GeoIp2\Record\Location $location; + public readonly \GeoIp2\Record\Location $location; /** - * @ignore + * @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. */ - protected \GeoIp2\Record\Postal $postal; + public readonly \GeoIp2\Record\Subdivision $mostSpecificSubdivision; /** - * @ignore - * - * @var array<\GeoIp2\Record\Subdivision> + * @var \GeoIp2\Record\Postal postal data for the + * requested IP address + */ + public readonly \GeoIp2\Record\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. */ - protected array $subdivisions = []; + public readonly array $subdivisions; /** * @ignore @@ -60,58 +57,59 @@ public function __construct(array $raw, array $locales = ['en']) { parent::__construct($raw, $locales); - $this->city = new \GeoIp2\Record\City($this->get('city'), $locales); - $this->location = new \GeoIp2\Record\Location($this->get('location')); - $this->postal = new \GeoIp2\Record\Postal($this->get('postal')); - - $this->createSubdivisions($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'] ?? []); - private function createSubdivisions(array $raw, array $locales): void - { if (!isset($raw['subdivisions'])) { + $this->subdivisions = []; + $this->mostSpecificSubdivision = + new \GeoIp2\Record\Subdivision([], $locales); + return; } + $subdivisions = []; foreach ($raw['subdivisions'] as $sub) { - $this->subdivisions[] = + $subdivisions[] = new \GeoIp2\Record\Subdivision($sub, $locales) ; } + + // Not using end as we don't want to modify internal pointer. + $this->mostSpecificSubdivision = + $subdivisions[\count($subdivisions) - 1]; + $this->subdivisions = $subdivisions; } - /** - * @ignore - * - * @return mixed - */ - public function __get(string $attr) + public function jsonSerialize(): ?array { - if ($attr === 'mostSpecificSubdivision') { - return $this->{$attr}(); + $js = parent::jsonSerialize(); + + $city = $this->city->jsonSerialize(); + if (!empty($city)) { + $js['city'] = $city; } - return parent::__get($attr); - } + $location = $this->location->jsonSerialize(); + if (!empty($location)) { + $js['location'] = $location; + } - /** - * @ignore - */ - public function __isset(string $attr): bool - { - if ($attr === 'mostSpecificSubdivision') { - // We always return a mostSpecificSubdivision, even if it is the - // empty subdivision - return true; + $postal = + $this->postal->jsonSerialize(); + if (!empty($postal)) { + $js['postal'] = $postal; } - return parent::__isset($attr); - } + $subdivisions = []; + foreach ($this->subdivisions as $sub) { + $subdivisions[] = $sub->jsonSerialize(); + } + if (!empty($subdivisions)) { + $js['subdivisions'] = $subdivisions; + } - private function mostSpecificSubdivision(): \GeoIp2\Record\Subdivision - { - return empty($this->subdivisions) ? - new \GeoIp2\Record\Subdivision([], $this->locales) : - end($this->subdivisions); + return $js; } } diff --git a/src/Model/ConnectionType.php b/src/Model/ConnectionType.php index eba9b4d3..6facb5e8 100644 --- a/src/Model/ConnectionType.php +++ b/src/Model/ConnectionType.php @@ -8,32 +8,49 @@ /** * This class provides the GeoIP2 Connection-Type model. - * - * @property-read string|null $connectionType The connection type may take the - * following values: "Dialup", "Cable/DSL", "Corporate", "Cellular", and - * "Satellite". Additional values may be added in the future. - * @property-read string $ipAddress The IP address that the data in the model is - * for. - * @property-read string $network The network in CIDR notation associated with - * the record. In particular, this is the largest network where all of the - * fields besides $ipAddress have the same value. */ -class ConnectionType extends AbstractModel +class ConnectionType implements \JsonSerializable { - protected ?string $connectionType; - protected string $ipAddress; - protected string $network; + /** + * @var string|null The connection type may take the + * following values: "Dialup", "Cable/DSL", "Corporate", "Cellular", and + * "Satellite". Additional values may be added in the future. + */ + public readonly ?string $connectionType; + + /** + * @var string the IP address that the data in the model is + * for + */ + public readonly string $ipAddress; + + /** + * @var string The network in CIDR notation associated with + * the record. In particular, this is the largest network where all of the + * fields besides $ipAddress have the same value. + */ + public readonly string $network; /** * @ignore */ public function __construct(array $raw) { - parent::__construct($raw); - - $this->connectionType = $this->get('connection_type'); - $ipAddress = $this->get('ip_address'); + $this->connectionType = $raw['connection_type'] ?? null; + $ipAddress = $raw['ip_address']; $this->ipAddress = $ipAddress; - $this->network = Util::cidr($ipAddress, $this->get('prefix_len')); + $this->network = Util::cidr($ipAddress, $raw['prefix_len']); + } + + public function jsonSerialize(): ?array + { + $js = []; + if ($this->connectionType !== null) { + $js['connection_type'] = $this->connectionType; + } + $js['ip_address'] = $this->ipAddress; + $js['network'] = $this->network; + + return $js; } } diff --git a/src/Model/Country.php b/src/Model/Country.php index b026b1fe..0fae2db7 100644 --- a/src/Model/Country.php +++ b/src/Model/Country.php @@ -8,67 +8,102 @@ * Model class for the data returned by GeoIP2 Country web service and database. * * See https://dev.maxmind.com/geoip/docs/web-services?lang=en for more details. - * - * @property-read \GeoIp2\Record\Continent $continent Continent data for the - * requested IP address. - * @property-read \GeoIp2\Record\Country $country Country data for the requested - * IP address. This object represents the country where MaxMind believes the - * end user is located. - * @property-read \GeoIp2\Record\MaxMind $maxmind Data related to your MaxMind - * account. - * @property-read \GeoIp2\Record\Country $registeredCountry 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. - * @property-read \GeoIp2\Record\RepresentedCountry $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. - * @property-read \GeoIp2\Record\Traits $traits Data for the traits of the - * requested IP address. - * @property-read array $raw The raw data from the web service. */ -class Country extends AbstractModel +class Country implements \JsonSerializable { - protected \GeoIp2\Record\Continent $continent; - protected \GeoIp2\Record\Country $country; + /** + * @var \GeoIp2\Record\Continent continent data for the + * requested IP address + */ + public readonly \GeoIp2\Record\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. + */ + public readonly \GeoIp2\Record\Country $country; + + /** + * @var \GeoIp2\Record\MaxMind data related to your MaxMind + * account + */ + public readonly \GeoIp2\Record\MaxMind $maxmind; /** - * @var array + * @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. */ - protected array $locales; + public readonly \GeoIp2\Record\Country $registeredCountry; - protected \GeoIp2\Record\MaxMind $maxmind; - protected \GeoIp2\Record\Country $registeredCountry; - protected \GeoIp2\Record\RepresentedCountry $representedCountry; - protected \GeoIp2\Record\Traits $traits; + /** + * @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. + */ + public readonly \GeoIp2\Record\RepresentedCountry $representedCountry; + + /** + * @var \GeoIp2\Record\Traits data for the traits of the + * requested IP address + */ + public readonly \GeoIp2\Record\Traits $traits; /** * @ignore */ public function __construct(array $raw, array $locales = ['en']) { - parent::__construct($raw); - $this->continent = new \GeoIp2\Record\Continent( - $this->get('continent'), + $raw['continent'] ?? [], $locales ); $this->country = new \GeoIp2\Record\Country( - $this->get('country'), + $raw['country'] ?? [], $locales ); - $this->maxmind = new \GeoIp2\Record\MaxMind($this->get('maxmind')); + $this->maxmind = new \GeoIp2\Record\MaxMind($raw['maxmind'] ?? []); $this->registeredCountry = new \GeoIp2\Record\Country( - $this->get('registered_country'), + $raw['registered_country'] ?? [], $locales ); $this->representedCountry = new \GeoIp2\Record\RepresentedCountry( - $this->get('represented_country'), + $raw['represented_country'] ?? [], $locales ); - $this->traits = new \GeoIp2\Record\Traits($this->get('traits')); + $this->traits = new \GeoIp2\Record\Traits($raw['traits'] ?? []); + } + + public function jsonSerialize(): ?array + { + $js = []; + $continent = $this->continent->jsonSerialize(); + if (!empty($continent)) { + $js['continent'] = $continent; + } + $country = $this->country->jsonSerialize(); + if (!empty($country)) { + $js['country'] = $country; + } + $maxmind = $this->maxmind->jsonSerialize(); + if (!empty($maxmind)) { + $js['maxmind'] = $maxmind; + } + $registeredCountry = $this->registeredCountry->jsonSerialize(); + if (!empty($registeredCountry)) { + $js['registered_country'] = $registeredCountry; + } + $representedCountry = $this->representedCountry->jsonSerialize(); + if (!empty($representedCountry)) { + $js['represented_country'] = $representedCountry; + } + $traits = $this->traits->jsonSerialize(); + if (!empty($traits)) { + $js['traits'] = $traits; + } - $this->locales = $locales; + return $js; } } diff --git a/src/Model/Domain.php b/src/Model/Domain.php index 95b8dcb8..1ce83fdb 100644 --- a/src/Model/Domain.php +++ b/src/Model/Domain.php @@ -8,32 +8,49 @@ /** * This class provides the GeoIP2 Domain model. - * - * @property-read string|null $domain The second level domain associated with the - * IP address. This will be something like "example.com" or - * "example.co.uk", not "foo.example.com". - * @property-read string $ipAddress The IP address that the data in the model is - * for. - * @property-read string $network The network in CIDR notation associated with - * the record. In particular, this is the largest network where all of the - * fields besides $ipAddress have the same value. */ -class Domain extends AbstractModel +class Domain implements \JsonSerializable { - protected ?string $domain; - protected string $ipAddress; - protected string $network; + /** + * @var string|null The second level domain associated with the + * IP address. This will be something like "example.com" or + * "example.co.uk", not "foo.example.com". + */ + public readonly ?string $domain; + + /** + * @var string the IP address that the data in the model is + * for + */ + public readonly string $ipAddress; + + /** + * @var string The network in CIDR notation associated with + * the record. In particular, this is the largest network where all of the + * fields besides $ipAddress have the same value. + */ + public readonly string $network; /** * @ignore */ public function __construct(array $raw) { - parent::__construct($raw); - - $this->domain = $this->get('domain'); - $ipAddress = $this->get('ip_address'); + $this->domain = $raw['domain'] ?? null; + $ipAddress = $raw['ip_address']; $this->ipAddress = $ipAddress; - $this->network = Util::cidr($ipAddress, $this->get('prefix_len')); + $this->network = Util::cidr($ipAddress, $raw['prefix_len']); + } + + public function jsonSerialize(): ?array + { + $js = []; + if ($this->domain !== null) { + $js['domain'] = $this->domain; + } + $js['ip_address'] = $this->ipAddress; + $js['network'] = $this->network; + + return $js; } } diff --git a/src/Model/Isp.php b/src/Model/Isp.php index 992ea2a6..f329063e 100644 --- a/src/Model/Isp.php +++ b/src/Model/Isp.php @@ -8,55 +8,103 @@ /** * This class provides the GeoIP2 ISP model. - * - * @property-read int|null $autonomousSystemNumber The autonomous system number - * associated with the IP address. - * @property-read string|null $autonomousSystemOrganization The organization - * associated with the registered autonomous system number for the IP - * address. - * @property-read string|null $isp The name of the ISP associated with the IP - * address. - * @property-read string|null $mobileCountryCode The [mobile country code - * (MCC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with - * the IP address and ISP. - * @property-read string|null $mobileNetworkCode The [mobile network code - * (MNC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with - * the IP address and ISP. - * @property-read string|null $organization The name of the organization associated - * with the IP address. - * @property-read string $ipAddress The IP address that the data in the model is - * for. - * @property-read string $network The network in CIDR notation associated with - * the record. In particular, this is the largest network where all of the - * fields besides $ipAddress have the same value. */ -class Isp extends AbstractModel +class Isp implements \JsonSerializable { - protected ?int $autonomousSystemNumber; - protected ?string $autonomousSystemOrganization; - protected ?string $isp; - protected ?string $mobileCountryCode; - protected ?string $mobileNetworkCode; - protected ?string $organization; - protected string $ipAddress; - protected string $network; + /** + * @var int|null the autonomous system number + * associated with the IP address + */ + public readonly ?int $autonomousSystemNumber; + + /** + * @var string|null the organization + * associated with the registered autonomous system number for the IP + * address + */ + public readonly ?string $autonomousSystemOrganization; + + /** + * @var string|null the name of the ISP associated with the IP + * address + */ + public readonly ?string $isp; + + /** + * @var string|null The [mobile country code + * (MCC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with + * the IP address and ISP. + */ + public readonly ?string $mobileCountryCode; + + /** + * @var string|null The [mobile network code + * (MNC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with + * the IP address and ISP. + */ + public readonly ?string $mobileNetworkCode; + + /** + * @var string|null the name of the organization associated + * with the IP address + */ + public readonly ?string $organization; + + /** + * @var string the IP address that the data in the model is + * for + */ + public readonly string $ipAddress; + + /** + * @var string The network in CIDR notation associated with + * the record. In particular, this is the largest network where all of the + * fields besides $ipAddress have the same value. + */ + public readonly string $network; /** * @ignore */ public function __construct(array $raw) { - parent::__construct($raw); - $this->autonomousSystemNumber = $this->get('autonomous_system_number'); + $this->autonomousSystemNumber = $raw['autonomous_system_number'] ?? null; $this->autonomousSystemOrganization = - $this->get('autonomous_system_organization'); - $this->isp = $this->get('isp'); - $this->mobileCountryCode = $this->get('mobile_country_code'); - $this->mobileNetworkCode = $this->get('mobile_network_code'); - $this->organization = $this->get('organization'); + $raw['autonomous_system_organization'] ?? null; + $this->isp = $raw['isp'] ?? null; + $this->mobileCountryCode = $raw['mobile_country_code'] ?? null; + $this->mobileNetworkCode = $raw['mobile_network_code'] ?? null; + $this->organization = $raw['organization'] ?? null; - $ipAddress = $this->get('ip_address'); + $ipAddress = $raw['ip_address']; $this->ipAddress = $ipAddress; - $this->network = Util::cidr($ipAddress, $this->get('prefix_len')); + $this->network = Util::cidr($ipAddress, $raw['prefix_len']); + } + + public function jsonSerialize(): ?array + { + $js = []; + if ($this->autonomousSystemNumber !== null) { + $js['autonomous_system_number'] = $this->autonomousSystemNumber; + } + if ($this->autonomousSystemOrganization !== null) { + $js['autonomous_system_organization'] = $this->autonomousSystemOrganization; + } + if ($this->isp !== null) { + $js['isp'] = $this->isp; + } + if ($this->mobileCountryCode !== null) { + $js['mobile_country_code'] = $this->mobileCountryCode; + } + if ($this->mobileNetworkCode !== null) { + $js['mobile_network_code'] = $this->mobileNetworkCode; + } + if ($this->organization !== null) { + $js['organization'] = $this->organization; + } + $js['ip_address'] = $this->ipAddress; + $js['network'] = $this->network; + + return $js; } } diff --git a/src/Record/AbstractNamedRecord.php b/src/Record/AbstractNamedRecord.php new file mode 100644 index 00000000..414841d5 --- /dev/null +++ b/src/Record/AbstractNamedRecord.php @@ -0,0 +1,49 @@ +names = $record['names'] ?? []; + + foreach ($locales as $locale) { + if (isset($this->names[$locale])) { + $this->name = $this->names[$locale]; + + return; + } + } + $this->name = null; + } + + public function jsonSerialize(): array + { + $js = []; + if (!empty($this->names)) { + $js['names'] = $this->names; + } + + return $js; + } +} diff --git a/src/Record/AbstractPlaceRecord.php b/src/Record/AbstractPlaceRecord.php index 16329a3d..364d7bb8 100644 --- a/src/Record/AbstractPlaceRecord.php +++ b/src/Record/AbstractPlaceRecord.php @@ -4,64 +4,43 @@ namespace GeoIp2\Record; -abstract class AbstractPlaceRecord extends AbstractRecord +abstract class AbstractPlaceRecord extends AbstractNamedRecord { /** - * @var array + * @var int|null A value from 0-100 indicating MaxMind's + * confidence that the location level is correct. This attribute is only available + * from the Insights service and the GeoIP2 Enterprise database. */ - private array $locales; + public readonly ?int $confidence; /** - * @ignore + * @var int|null The GeoName ID for the location level. This attribute + * is returned by all location services and databases. */ - public function __construct(?array $record, array $locales = ['en']) - { - $this->locales = $locales; - parent::__construct($record); - } + public readonly ?int $geonameId; /** * @ignore - * - * @return mixed */ - public function __get(string $attr) + public function __construct(array $record, array $locales = ['en']) { - if ($attr === 'name') { - return $this->name(); - } + parent::__construct($record, $locales); - return parent::__get($attr); + $this->confidence = $record['confidence'] ?? null; + $this->geonameId = $record['geoname_id'] ?? null; } - /** - * @ignore - */ - public function __isset(string $attr): bool + public function jsonSerialize(): array { - if ($attr === 'name') { - return $this->firstSetNameLocale() !== null; + $js = parent::jsonSerialize(); + if ($this->confidence !== null) { + $js['confidence'] = $this->confidence; } - return parent::__isset($attr); - } - - private function name(): ?string - { - $locale = $this->firstSetNameLocale(); - - // @phpstan-ignore-next-line - return $locale === null ? null : $this->names[$locale]; - } - - private function firstSetNameLocale(): ?string - { - foreach ($this->locales as $locale) { - if (isset($this->names[$locale])) { - return $locale; - } + if ($this->geonameId !== null) { + $js['geoname_id'] = $this->geonameId; } - return null; + return $js; } } diff --git a/src/Record/AbstractRecord.php b/src/Record/AbstractRecord.php deleted file mode 100644 index 1e36fd0f..00000000 --- a/src/Record/AbstractRecord.php +++ /dev/null @@ -1,67 +0,0 @@ - - */ - private array $record; - - /** - * @ignore - */ - public function __construct(?array $record) - { - $this->record = isset($record) ? $record : []; - } - - /** - * @ignore - * - * @return mixed - */ - public function __get(string $attr) - { - // XXX - kind of ugly but greatly reduces boilerplate code - $key = $this->attributeToKey($attr); - - if ($this->__isset($attr)) { - return $this->record[$key]; - } - if ($this->validAttribute($attr)) { - if (preg_match('/^is_/', $key)) { - return false; - } - - return null; - } - - throw new \RuntimeException("Unknown attribute: $attr"); - } - - public function __isset(string $attr): bool - { - return $this->validAttribute($attr) - && isset($this->record[$this->attributeToKey($attr)]); - } - - private function attributeToKey(string $attr): string - { - return strtolower(preg_replace('/([A-Z])/', '_\1', $attr)); - } - - private function validAttribute(string $attr): bool - { - // @phpstan-ignore-next-line - return \in_array($attr, $this->validAttributes, true); - } - - public function jsonSerialize(): ?array - { - return $this->record; - } -} diff --git a/src/Record/City.php b/src/Record/City.php index f65d96b0..9ebb9a28 100644 --- a/src/Record/City.php +++ b/src/Record/City.php @@ -9,25 +9,6 @@ * * This record is returned by all location services and databases besides * Country. - * - * @property-read int|null $confidence A value from 0-100 indicating MaxMind's - * confidence that the city is correct. This attribute is only available - * from the Insights service and the GeoIP2 Enterprise database. - * @property-read int|null $geonameId The GeoName ID for the city. This attribute - * is returned by all location services and databases. - * @property-read string|null $name The name of the city based on the locales list - * passed to the constructor. This attribute is returned by all location - * services and databases. - * @property-read array|null $names An array map where the keys are locale codes - * and the values are names. This attribute is returned by all location - * services and databases. */ -class City extends AbstractPlaceRecord -{ - /** - * @ignore - * - * @var array - */ - protected array $validAttributes = ['confidence', 'geonameId', 'names']; -} +// phpcs:disable +class City extends AbstractPlaceRecord {} diff --git a/src/Record/Continent.php b/src/Record/Continent.php index a3e28722..873014ae 100644 --- a/src/Record/Continent.php +++ b/src/Record/Continent.php @@ -8,29 +8,43 @@ * Contains data for the continent record associated with an IP address. * * This record is returned by all location services and databases. - * - * @property-read string|null $code A two character continent code like "NA" (North - * America) or "OC" (Oceania). This attribute is returned by all location - * services and databases. - * @property-read int|null $geonameId The GeoName ID for the continent. This - * attribute is returned by all location services and databases. - * @property-read string|null $name Returns the name of the continent based on the - * locales list passed to the constructor. This attribute is returned by all location - * services and databases. - * @property-read array|null $names An array map where the keys are locale codes - * and the values are names. This attribute is returned by all location - * services and databases. */ -class Continent extends AbstractPlaceRecord +class Continent extends AbstractNamedRecord { + /** + * @var string|null A two character continent code like "NA" (North + * America) or "OC" (Oceania). This attribute is returned by all location + * services and databases. + */ + public readonly ?string $code; + + /** + * @var int|null The GeoName ID for the continent. This + * attribute is returned by all location services and databases. + */ + public readonly ?int $geonameId; + /** * @ignore - * - * @var array */ - protected array $validAttributes = [ - 'code', - 'geonameId', - 'names', - ]; + public function __construct(array $record, array $locales = ['en']) + { + parent::__construct($record, $locales); + + $this->code = $record['code'] ?? null; + $this->geonameId = $record['geoname_id'] ?? null; + } + + public function jsonSerialize(): array + { + $js = parent::jsonSerialize(); + if ($this->code !== null) { + $js['code'] = $this->code; + } + if ($this->geonameId !== null) { + $js['geoname_id'] = $this->geonameId; + } + + return $js; + } } diff --git a/src/Record/Country.php b/src/Record/Country.php index d1f239bb..438ebd89 100644 --- a/src/Record/Country.php +++ b/src/Record/Country.php @@ -8,37 +8,44 @@ * Contains data for the country record associated with an IP address. * * This record is returned by all location services and databases. - * - * @property-read int|null $confidence A value from 0-100 indicating MaxMind's - * confidence that the country is correct. This attribute is only available - * from the Insights service and the GeoIP2 Enterprise database. - * @property-read int|null $geonameId The GeoName ID for the country. This - * attribute is returned by all location services and databases. - * @property-read bool $isInEuropeanUnion This is true if the country is a - * member state of the European Union. This attribute is returned by all - * location services and databases. - * @property-read string|null $isoCode The two-character ISO 3166-1 alpha code - * for the country. See https://en.wikipedia.org/wiki/ISO_3166-1. This - * attribute is returned by all location services and databases. - * @property-read string|null $name The name of the country based on the locales - * list passed to the constructor. This attribute is returned by all location - * services and databases. - * @property-read array|null $names An array map where the keys are locale codes - * and the values are names. This attribute is returned by all location - * services and databases. */ class Country extends AbstractPlaceRecord { + /** + * @var bool This is true if the country is a + * member state of the European Union. This attribute is returned by all + * location services and databases. + */ + public readonly bool $isInEuropeanUnion; + + /** + * @var string|null The two-character ISO 3166-1 alpha code + * for the country. See https://en.wikipedia.org/wiki/ISO_3166-1. This + * attribute is returned by all location services and databases. + */ + public readonly ?string $isoCode; + /** * @ignore - * - * @var array */ - protected array $validAttributes = [ - 'confidence', - 'geonameId', - 'isInEuropeanUnion', - 'isoCode', - 'names', - ]; + public function __construct(array $record, array $locales = ['en']) + { + parent::__construct($record, $locales); + + $this->isInEuropeanUnion = $record['is_in_european_union'] ?? false; + $this->isoCode = $record['iso_code'] ?? null; + } + + public function jsonSerialize(): array + { + $js = parent::jsonSerialize(); + if ($this->isInEuropeanUnion !== false) { + $js['is_in_european_union'] = $this->isInEuropeanUnion; + } + if ($this->isoCode !== null) { + $js['iso_code'] = $this->isoCode; + } + + return $js; + } } diff --git a/src/Record/Location.php b/src/Record/Location.php index 202fffa6..ca13b52c 100644 --- a/src/Record/Location.php +++ b/src/Record/Location.php @@ -9,48 +9,97 @@ * * This record is returned by all location services and databases besides * Country. - * - * @property-read int|null $averageIncome The average income in US dollars - * associated with the requested IP address. This attribute is only available - * from the Insights service. - * @property-read int|null $accuracyRadius The approximate accuracy radius in - * kilometers around the latitude and longitude for the IP address. This is - * the radius where we have a 67% confidence that the device using the IP - * address resides within the circle centered at the latitude and longitude - * with the provided radius. - * @property-read float|null $latitude The approximate latitude of the location - * associated with the IP address. This value is not precise and should not be - * used to identify a particular address or household. - * @property-read float|null $longitude The approximate longitude of the location - * associated with the IP address. This value is not precise and should not be - * used to identify a particular address or household. - * @property-read int|null $populationDensity The estimated population per square - * kilometer associated with the IP address. This attribute is only available - * from the Insights service. - * @property-read int|null $metroCode The metro code of the location if the location - * is in the US. MaxMind returns the same metro codes as the - * Google AdWords API. See - * https://developers.google.com/adwords/api/docs/appendix/cities-DMAregions. - * @property-read string|null $timeZone The time zone associated with location, as - * specified by the IANA Time Zone Database, e.g., "America/New_York". See - * https://www.iana.org/time-zones. */ -class Location extends AbstractRecord +class Location implements \JsonSerializable { /** - * @ignore - * - * @var array + * @var int|null The average income in US dollars + * associated with the requested IP address. This attribute is only available + * from the Insights service. + */ + public readonly ?int $averageIncome; + + /** + * @var int|null The approximate accuracy radius in + * kilometers around the latitude and longitude for the IP address. This is + * the radius where we have a 67% confidence that the device using the IP + * address resides within the circle centered at the latitude and longitude + * with the provided radius. + */ + public readonly ?int $accuracyRadius; + + /** + * @var float|null The approximate latitude of the location + * associated with the IP address. This value is not precise and should not be + * used to identify a particular address or household. + */ + public readonly ?float $latitude; + + /** + * @var float|null The approximate longitude of the location + * associated with the IP address. This value is not precise and should not be + * used to identify a particular address or household. + */ + public readonly ?float $longitude; + + /** + * @var int|null The metro code of the location if the location + * is in the US. MaxMind returns the same metro codes as the + * Google AdWords API. See + * https://developers.google.com/adwords/api/docs/appendix/cities-DMAregions. + */ + public readonly ?int $metroCode; + + /** + * @var int|null The estimated population per square + * kilometer associated with the IP address. This attribute is only available + * from the Insights service. */ - protected array $validAttributes = [ - 'averageIncome', - 'accuracyRadius', - 'latitude', - 'longitude', - 'metroCode', - 'populationDensity', - 'postalCode', - 'postalConfidence', - 'timeZone', - ]; + public readonly ?int $populationDensity; + + /** + * @var string|null The time zone associated with location, as + * specified by the IANA Time Zone Database, e.g., "America/New_York". See + * https://www.iana.org/time-zones. + */ + public readonly ?string $timeZone; + + public function __construct(array $record) + { + $this->averageIncome = $record['average_income'] ?? null; + $this->accuracyRadius = $record['accuracy_radius'] ?? null; + $this->latitude = $record['latitude'] ?? null; + $this->longitude = $record['longitude'] ?? null; + $this->metroCode = $record['metro_code'] ?? null; + $this->populationDensity = $record['population_density'] ?? null; + $this->timeZone = $record['time_zone'] ?? null; + } + + public function jsonSerialize(): array + { + $js = []; + if ($this->averageIncome !== null) { + $js['average_income'] = $this->averageIncome; + } + if ($this->accuracyRadius !== null) { + $js['accuracy_radius'] = $this->accuracyRadius; + } + if ($this->latitude !== null) { + $js['latitude'] = $this->latitude; + } + if ($this->longitude !== null) { + $js['longitude'] = $this->longitude; + } + if ($this->metroCode !== null) { + $js['metro_code'] = $this->metroCode; + } + if ($this->populationDensity !== null) { + $js['population_density'] = $this->populationDensity; + } + if ($this->timeZone !== null) { + $js['time_zone'] = $this->timeZone; + } + + return $js; + } } diff --git a/src/Record/MaxMind.php b/src/Record/MaxMind.php index 4e599675..e80334d5 100644 --- a/src/Record/MaxMind.php +++ b/src/Record/MaxMind.php @@ -8,16 +8,27 @@ * Contains data about your account. * * This record is returned by all location services and databases. - * - * @property-read int|null $queriesRemaining The number of remaining queries you - * have for the service you are calling. */ -class MaxMind extends AbstractRecord +class MaxMind implements \JsonSerializable { /** - * @ignore - * - * @var array + * @var int|null the number of remaining queries you + * have for the service you are calling */ - protected array $validAttributes = ['queriesRemaining']; + public readonly ?int $queriesRemaining; + + public function __construct(array $record) + { + $this->queriesRemaining = $record['queries_remaining'] ?? null; + } + + public function jsonSerialize(): array + { + $js = []; + if ($this->queriesRemaining !== null) { + $js['queries_remaining'] = $this->queriesRemaining; + } + + return $js; + } } diff --git a/src/Record/Postal.php b/src/Record/Postal.php index 2ac67e30..d3adac6f 100644 --- a/src/Record/Postal.php +++ b/src/Record/Postal.php @@ -9,22 +9,44 @@ * * This record is returned by all location databases and services besides * Country. - * - * @property-read string|null $code The postal code of the location. Postal codes - * are not available for all countries. In some countries, this will only - * contain part of the postal code. This attribute is returned by all location - * databases and services besides Country. - * @property-read int|null $confidence A value from 0-100 indicating MaxMind's - * confidence that the postal code is correct. This attribute is only - * available from the Insights service and the GeoIP2 Enterprise - * database. */ -class Postal extends AbstractRecord +class Postal implements \JsonSerializable { + /** + * @var string|null The postal code of the location. Postal codes + * are not available for all countries. In some countries, this will only + * contain part of the postal code. This attribute is returned by all location + * databases and services besides Country. + */ + public readonly ?string $code; + + /** + * @var int|null A value from 0-100 indicating MaxMind's + * confidence that the postal code is correct. This attribute is only + * available from the Insights service and the GeoIP2 Enterprise + * database. + */ + public readonly ?int $confidence; + /** * @ignore - * - * @var array */ - protected array $validAttributes = ['code', 'confidence']; + public function __construct(array $record) + { + $this->code = $record['code'] ?? null; + $this->confidence = $record['confidence'] ?? null; + } + + public function jsonSerialize(): array + { + $js = []; + if ($this->code !== null) { + $js['code'] = $this->code; + } + if ($this->confidence !== null) { + $js['confidence'] = $this->confidence; + } + + return $js; + } } diff --git a/src/Record/RepresentedCountry.php b/src/Record/RepresentedCountry.php index 717fd852..a810fc63 100644 --- a/src/Record/RepresentedCountry.php +++ b/src/Record/RepresentedCountry.php @@ -10,24 +10,33 @@ * This class contains the country-level data associated with an IP address * for the IP's represented country. The represented country is the country * represented by something like a military base. - * - * @property-read string|null $type A string indicating the type of entity that is - * representing the country. Currently we only return military - * but this could expand to include other types in the future. */ class RepresentedCountry extends Country { + /** + * @var string|null A string indicating the type of entity that is + * representing the country. Currently we only return military + * but this could expand to include other types in the future. + */ + public readonly ?string $type; + /** * @ignore - * - * @var array */ - protected array $validAttributes = [ - 'confidence', - 'geonameId', - 'isInEuropeanUnion', - 'isoCode', - 'names', - 'type', - ]; + public function __construct(array $record, array $locales = ['en']) + { + parent::__construct($record, $locales); + + $this->type = $record['type'] ?? null; + } + + public function jsonSerialize(): array + { + $js = parent::jsonSerialize(); + if ($this->type !== null) { + $js['type'] = $this->type; + } + + return $js; + } } diff --git a/src/Record/Subdivision.php b/src/Record/Subdivision.php index ac8cd285..cd11e58b 100644 --- a/src/Record/Subdivision.php +++ b/src/Record/Subdivision.php @@ -9,36 +9,34 @@ * * This record is returned by all location databases and services besides * Country. - * - * @property-read int|null $confidence This is a value from 0-100 indicating - * MaxMind's confidence that the subdivision is correct. This attribute is - * only available from the Insights service and the GeoIP2 Enterprise - * database. - * @property-read int|null $geonameId This is a GeoName ID for the subdivision. - * This attribute is returned by all location databases and services besides - * Country. - * @property-read string|null $isoCode This is a string up to three characters long - * contain the subdivision portion of the ISO 3166-2 code. See - * https://en.wikipedia.org/wiki/ISO_3166-2. This attribute is returned by all - * location databases and services except Country. - * @property-read string|null $name The name of the subdivision based on the - * locales list passed to the constructor. This attribute is returned by all - * location databases and services besides Country. - * @property-read array|null $names An array map where the keys are locale codes - * and the values are names. This attribute is returned by all location - * databases and services besides Country. */ class Subdivision extends AbstractPlaceRecord { + /** + * @var string|null This is a string up to three characters long + * contain the subdivision portion of the ISO 3166-2 code. See + * https://en.wikipedia.org/wiki/ISO_3166-2. This attribute is returned by all + * location databases and services except Country. + */ + public readonly ?string $isoCode; + /** * @ignore - * - * @var array */ - protected array $validAttributes = [ - 'confidence', - 'geonameId', - 'isoCode', - 'names', - ]; + public function __construct(array $record, array $locales = ['en']) + { + parent::__construct($record, $locales); + + $this->isoCode = $record['iso_code'] ?? null; + } + + public function jsonSerialize(): array + { + $js = parent::jsonSerialize(); + if ($this->isoCode !== null) { + $js['iso_code'] = $this->isoCode; + } + + return $js; + } } diff --git a/src/Record/Traits.php b/src/Record/Traits.php index f831f323..5ffcb45d 100644 --- a/src/Record/Traits.php +++ b/src/Record/Traits.php @@ -10,150 +10,278 @@ * Contains data for the traits record associated with an IP address. * * This record is returned by all location services and databases. - * - * @property-read int|null $autonomousSystemNumber The autonomous system number - * associated with the IP address. See - * https://en.wikipedia.org/wiki/Autonomous_system_(Internet%29. This attribute - * is only available from the City Plus and Insights web services and the - * GeoIP2 Enterprise database. - * @property-read string|null $autonomousSystemOrganization The organization - * associated with the registered autonomous system number for the IP address. - * See https://en.wikipedia.org/wiki/Autonomous_system_(Internet%29. This - * attribute is only available from the City Plus and Insights web services and - * the GeoIP2 Enterprise database. - * @property-read string|null $connectionType The connection type may take the - * following values: "Dialup", "Cable/DSL", "Corporate", "Cellular", and - * "Satellite". Additional values may be added in the future. This attribute is - * only available from the City Plus and Insights web services and the GeoIP2 - * Enterprise database. - * @property-read string|null $domain The second level domain associated with the - * IP address. This will be something like "example.com" or "example.co.uk", - * not "foo.example.com". This attribute is only available from the - * City Plus and Insights web services and the GeoIP2 Enterprise - * database. - * @property-read string $ipAddress The IP address that the data in the model - * is for. If you performed a "me" lookup against the web service, this - * will be the externally routable IP address for the system the code is - * running on. If the system is behind a NAT, this may differ from the IP - * address locally assigned to it. This attribute is returned by all end - * points. - * @property-read bool $isAnonymous This is true if the IP address belongs to - * any sort of anonymous network. This property is only available from GeoIP2 - * Insights. - * @property-read bool $isAnonymousProxy *Deprecated.* Please see our GeoIP2 - * Anonymous IP database - * (https://www.maxmind.com/en/geoip2-anonymous-ip-database) to determine - * whether the IP address is used by an anonymizing service. - * @property-read bool $isAnonymousVpn This is true if the IP address is - * registered to an anonymous VPN provider. If a VPN provider does not register - * subnets under names associated with them, we will likely only flag their IP - * ranges using the isHostingProvider property. This property is only available - * from GeoIP2 Insights. - * @property-read bool $isHostingProvider This is true if the IP address belongs - * to a hosting or VPN provider (see description of isAnonymousVpn property). - * This property is only available from GeoIP2 Insights. - * @property-read bool $isLegitimateProxy This attribute is true if MaxMind - * believes this IP address to be a legitimate proxy, such as an internal - * VPN used by a corporation. This attribute is only available in the GeoIP2 - * Enterprise database. - * @property-read bool $isPublicProxy This is true if the IP address belongs to - * a public proxy. This property is only available from GeoIP2 Insights. - * @property-read bool $isResidentialProxy This is true if the IP address is - * on a suspected anonymizing network and belongs to a residential ISP. This - * property is only available from GeoIP2 Insights. - * @property-read bool $isSatelliteProvider *Deprecated.* Due to the - * increased coverage by mobile carriers, very few satellite providers now - * serve multiple countries. As a result, the output does not provide - * sufficiently relevant data for us to maintain it. - * @property-read bool $isTorExitNode This is true if the IP address is a Tor - * exit node. This property is only available from GeoIP2 Insights. - * @property-read string|null $isp The name of the ISP associated with the IP - * address. This attribute is only available from the City Plus and Insights - * web services and the GeoIP2 Enterprise database. - * @property-read string $network The network in CIDR notation associated with - * the record. In particular, this is the largest network where all of the - * fields besides $ipAddress have the same value. - * @property-read string|null $organization The name of the organization - * associated with the IP address. This attribute is only available from the - * City Plus and Insights web services and the GeoIP2 Enterprise database. - * @property-read string|null $mobileCountryCode The [mobile country code - * (MCC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with - * the IP address and ISP. This property is available from the City Plus and - * Insights web services and the GeoIP2 Enterprise database. - * @property-read string|null $mobileNetworkCode The [mobile network code - * (MNC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with - * the IP address and ISP. This property is available from the City Plus and - * Insights web services and the GeoIP2 Enterprise database. - * @property-read float|null $staticIpScore An indicator of how static or - * dynamic an IP address is. This property is only available from GeoIP2 - * Insights. - * @property-read int|null $userCount The estimated number of users sharing - * the IP/network during the past 24 hours. For IPv4, the count is for the - * individual IP. For IPv6, the count is for the /64 network. This property is - * only available from GeoIP2 Insights. - * @property-read string|null $userType

The user type associated with the IP - * address. This can be one of the following values:

- *
    - *
  • business - *
  • cafe - *
  • cellular - *
  • college - *
  • consumer_privacy_network - *
  • content_delivery_network - *
  • dialup - *
  • government - *
  • hosting - *
  • library - *
  • military - *
  • residential - *
  • router - *
  • school - *
  • search_engine_spider - *
  • traveler - *
- *

- * This attribute is only available from the Insights web service and the - * GeoIP2 Enterprise database. - *

*/ -class Traits extends AbstractRecord +class Traits implements \JsonSerializable { /** - * @ignore - * - * @var array - */ - protected array $validAttributes = [ - 'autonomousSystemNumber', - 'autonomousSystemOrganization', - 'connectionType', - 'domain', - 'ipAddress', - 'isAnonymous', - 'isAnonymousProxy', - 'isAnonymousVpn', - 'isHostingProvider', - 'isLegitimateProxy', - 'isp', - 'isPublicProxy', - 'isResidentialProxy', - 'isSatelliteProvider', - 'isTorExitNode', - 'mobileCountryCode', - 'mobileNetworkCode', - 'network', - 'organization', - 'staticIpScore', - 'userCount', - 'userType', - ]; - - public function __construct(?array $record) + * @var int|null The autonomous system number + * associated with the IP address. See + * https://en.wikipedia.org/wiki/Autonomous_system_(Internet%29. This attribute + * is only available from the City Plus and Insights web services and the + * GeoIP2 Enterprise database. + */ + public readonly ?int $autonomousSystemNumber; + + /** + * @var string|null The organization + * associated with the registered autonomous system number for the IP address. + * See https://en.wikipedia.org/wiki/Autonomous_system_(Internet%29. This + * attribute is only available from the City Plus and Insights web services and + * the GeoIP2 Enterprise database. + */ + public readonly ?string $autonomousSystemOrganization; + + /** + * @var string|null The connection type may take the + * following values: "Dialup", "Cable/DSL", "Corporate", "Cellular", and + * "Satellite". Additional values may be added in the future. This attribute is + * only available from the City Plus and Insights web services and the GeoIP2 + * Enterprise database. + */ + public readonly ?string $connectionType; + + /** + * @var string|null The second level domain associated with the + * IP address. This will be something like "example.com" or "example.co.uk", + * not "foo.example.com". This attribute is only available from the + * City Plus and Insights web services and the GeoIP2 Enterprise + * database. + */ + public readonly ?string $domain; + + /** + * @var string|null The IP address that the data in the model + * is for. If you performed a "me" lookup against the web service, this + * will be the externally routable IP address for the system the code is + * running on. If the system is behind a NAT, this may differ from the IP + * address locally assigned to it. This attribute is returned by all end + * points. + */ + public readonly ?string $ipAddress; + + /** + * @var bool This is true if the IP address belongs to + * any sort of anonymous network. This property is only available from GeoIP2 + * Insights. + */ + public readonly bool $isAnonymous; + + /** + * @var bool This is true if the IP address is + * registered to an anonymous VPN provider. If a VPN provider does not register + * subnets under names associated with them, we will likely only flag their IP + * ranges using the isHostingProvider property. This property is only available + * from GeoIP2 Insights. + */ + public readonly bool $isAnonymousVpn; + + /** + * @var bool This is true if the IP address belongs + * to a hosting or VPN provider (see description of isAnonymousVpn property). + * This property is only available from GeoIP2 Insights. + */ + public readonly bool $isHostingProvider; + + /** + * @var bool This attribute is true if MaxMind + * believes this IP address to be a legitimate proxy, such as an internal + * VPN used by a corporation. This attribute is only available in the GeoIP2 + * Enterprise database. + */ + public readonly bool $isLegitimateProxy; + + /** + * @var bool This is true if the IP address belongs to + * a public proxy. This property is only available from GeoIP2 Insights. + */ + public readonly bool $isPublicProxy; + + /** + * @var bool This is true if the IP address is + * on a suspected anonymizing network and belongs to a residential ISP. This + * property is only available from GeoIP2 Insights. + */ + public readonly bool $isResidentialProxy; + + /** + * @var bool This is true if the IP address is a Tor + * exit node. This property is only available from GeoIP2 Insights. + */ + public readonly bool $isTorExitNode; + + /** + * @var string|null The name of the ISP associated with the IP + * address. This attribute is only available from the City Plus and Insights + * web services and the GeoIP2 Enterprise database. + */ + public readonly ?string $isp; + + /** + * @var string|null The [mobile country code + * (MCC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with + * the IP address and ISP. This property is available from the City Plus and + * Insights web services and the GeoIP2 Enterprise database. + */ + public readonly ?string $mobileCountryCode; + + /** + * @var string|null The [mobile network code + * (MNC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with + * the IP address and ISP. This property is available from the City Plus and + * Insights web services and the GeoIP2 Enterprise database. + */ + public readonly ?string $mobileNetworkCode; + + /** + * @var string|null The network in CIDR notation associated with + * the record. In particular, this is the largest network where all of the + * fields besides $ipAddress have the same value. + */ + public readonly ?string $network; + + /** + * @var string|null The name of the organization + * associated with the IP address. This attribute is only available from the + * City Plus and Insights web services and the GeoIP2 Enterprise database. + */ + public readonly ?string $organization; + + /** + * @var float|null An indicator of how static or + * dynamic an IP address is. This property is only available from GeoIP2 + * Insights. + */ + public readonly ?float $staticIpScore; + + /** + * @var int|null The estimated number of users sharing + * the IP/network during the past 24 hours. For IPv4, the count is for the + * individual IP. For IPv6, the count is for the /64 network. This property is + * only available from GeoIP2 Insights. + */ + public readonly ?int $userCount; + + /** + * @var string|null

The user type associated with the IP + * address. This can be one of the following values:

+ *
    + *
  • business + *
  • cafe + *
  • cellular + *
  • college + *
  • consumer_privacy_network + *
  • content_delivery_network + *
  • dialup + *
  • government + *
  • hosting + *
  • library + *
  • military + *
  • residential + *
  • router + *
  • school + *
  • search_engine_spider + *
  • traveler + *
+ *

+ * This attribute is only available from the Insights web service and the + * GeoIP2 Enterprise database. + *

+ */ + public readonly ?string $userType; + + public function __construct(array $record) + { + $this->autonomousSystemNumber = $record['autonomous_system_number'] ?? null; + $this->autonomousSystemOrganization = $record['autonomous_system_organization'] ?? null; + $this->connectionType = $record['connection_type'] ?? null; + $this->domain = $record['domain'] ?? null; + $this->ipAddress = $record['ip_address'] ?? null; + $this->isAnonymous = $record['is_anonymous'] ?? false; + $this->isAnonymousVpn = $record['is_anonymous_vpn'] ?? false; + $this->isHostingProvider = $record['is_hosting_provider'] ?? false; + $this->isLegitimateProxy = $record['is_legitimate_proxy'] ?? false; + $this->isp = $record['isp'] ?? null; + $this->isPublicProxy = $record['is_public_proxy'] ?? false; + $this->isResidentialProxy = $record['is_residential_proxy'] ?? false; + $this->isTorExitNode = $record['is_tor_exit_node'] ?? false; + $this->mobileCountryCode = $record['mobile_country_code'] ?? null; + $this->mobileNetworkCode = $record['mobile_network_code'] ?? null; + $this->organization = $record['organization'] ?? null; + $this->staticIpScore = $record['static_ip_score'] ?? null; + $this->userCount = $record['user_count'] ?? null; + $this->userType = $record['user_type'] ?? null; + + if (isset($record['network'])) { + $this->network = $record['network']; + } else { + $this->network = isset($record['prefix_len']) ? Util::cidr($this->ipAddress, $record['prefix_len']) : null; + } + } + + public function jsonSerialize(): array { - if (!isset($record['network']) && isset($record['ip_address'], $record['prefix_len'])) { - $record['network'] = Util::cidr($record['ip_address'], $record['prefix_len']); + $js = []; + if ($this->autonomousSystemNumber !== null) { + $js['autonomous_system_number'] = $this->autonomousSystemNumber; + } + if ($this->autonomousSystemOrganization !== null) { + $js['autonomous_system_organization'] = $this->autonomousSystemOrganization; + } + if ($this->connectionType !== null) { + $js['connection_type'] = $this->connectionType; + } + if ($this->domain !== null) { + $js['domain'] = $this->domain; + } + if ($this->ipAddress !== null) { + $js['ip_address'] = $this->ipAddress; + } + if ($this->isAnonymous !== false) { + $js['is_anonymous'] = $this->isAnonymous; + } + if ($this->isAnonymousVpn !== false) { + $js['is_anonymous_vpn'] = $this->isAnonymousVpn; + } + if ($this->isHostingProvider !== false) { + $js['is_hosting_provider'] = $this->isHostingProvider; + } + if ($this->isLegitimateProxy !== false) { + $js['is_legitimate_proxy'] = $this->isLegitimateProxy; + } + if ($this->isPublicProxy !== false) { + $js['is_public_proxy'] = $this->isPublicProxy; + } + if ($this->isResidentialProxy !== false) { + $js['is_residential_proxy'] = $this->isResidentialProxy; + } + if ($this->isTorExitNode !== false) { + $js['is_tor_exit_node'] = $this->isTorExitNode; + } + if ($this->isp !== null) { + $js['isp'] = $this->isp; + } + if ($this->mobileCountryCode !== null) { + $js['mobile_country_code'] = $this->mobileCountryCode; + } + if ($this->mobileNetworkCode !== null) { + $js['mobile_network_code'] = $this->mobileNetworkCode; + } + if ($this->network !== null) { + $js['network'] = $this->network; + } + if ($this->organization !== null) { + $js['organization'] = $this->organization; + } + if ($this->staticIpScore !== null) { + $js['static_ip_score'] = $this->staticIpScore; + } + if ($this->userCount !== null) { + $js['user_count'] = $this->userCount; + } + if ($this->userType !== null) { + $js['user_type'] = $this->userType; } - parent::__construct($record); + return $js; } } diff --git a/tests/GeoIp2/Test/Database/ReaderTest.php b/tests/GeoIp2/Test/Database/ReaderTest.php index fbbb3468..a7091137 100644 --- a/tests/GeoIp2/Test/Database/ReaderTest.php +++ b/tests/GeoIp2/Test/Database/ReaderTest.php @@ -14,7 +14,7 @@ */ class ReaderTest extends TestCase { - public function databaseTypes(): array + public static function databaseTypes(): array { return [['City', 'city'], ['Country', 'country']]; } diff --git a/tests/GeoIp2/Test/Model/CountryTest.php b/tests/GeoIp2/Test/Model/CountryTest.php index 14fd5cd2..9f1bc77e 100644 --- a/tests/GeoIp2/Test/Model/CountryTest.php +++ b/tests/GeoIp2/Test/Model/CountryTest.php @@ -164,31 +164,41 @@ public function testValues(): void $this->model->registeredCountry->name, 'registered_country name is Germany' ); - - foreach (['isAnonymousProxy', 'isSatelliteProvider'] as $meth) { - $this->assertFalse( - $this->model->traits->{$meth}, - "traits $meth returns 0 by default" - ); - } - - $this->assertSame( - $this->raw, - $this->model->raw, - 'raw method returns raw input' - ); } public function testJsonSerialize(): void { - $this->assertSame( - $this->raw, + $js = + [ + 'continent' => [ + 'names' => ['en' => 'North America'], + 'code' => 'NA', + 'geoname_id' => 42, + ], + 'country' => [ + 'names' => ['en' => 'United States of America'], + 'geoname_id' => 1, + 'iso_code' => 'US', + ], + 'registered_country' => [ + 'names' => ['en' => 'Germany'], + 'geoname_id' => 2, + 'is_in_european_union' => true, + 'iso_code' => 'DE', + ], + 'traits' => [ + 'ip_address' => '1.2.3.4', + 'network' => '1.2.3.0/24', + ], + ]; + $this->assertSame( + $js, $this->model->jsonSerialize(), 'jsonSerialize returns initial array' ); $this->assertSame( - $this->raw['country'], + $js['country'], $this->model->country->jsonSerialize(), 'jsonSerialize returns initial array for the record' ); @@ -198,13 +208,13 @@ public function testJsonSerialize(): void } $this->assertSame( - json_encode($this->raw), + json_encode($js), json_encode($this->model), 'json_encode can be called on the model object directly' ); $this->assertSame( - json_encode($this->raw['country']), + json_encode($js['country']), json_encode($this->model->country), 'json_encode can be called on the record object directly' ); @@ -228,22 +238,4 @@ public function testIsSet(): void 'unknown trait is not set' ); } - - public function testUnknownRecord(): void - { - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('Unknown attribute'); - - // @phpstan-ignore-next-line - $this->model->unknownRecord; - } - - public function testUnknownTrait(): void - { - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('Unknown attribute'); - - // @phpstan-ignore-next-line - $this->model->traits->unknown; - } } diff --git a/tests/GeoIp2/Test/Model/InsightsTest.php b/tests/GeoIp2/Test/Model/InsightsTest.php index e18086af..15ced55c 100644 --- a/tests/GeoIp2/Test/Model/InsightsTest.php +++ b/tests/GeoIp2/Test/Model/InsightsTest.php @@ -40,13 +40,15 @@ public function testFull(): void 'longitude' => 93.2636, 'metro_code' => 765, 'population_density' => 1341, - 'postal_code' => '55401', - 'postal_confidence' => 33, 'time_zone' => 'America/Chicago', ], 'maxmind' => [ 'queries_remaining' => 22, ], + 'postal' => [ + 'code' => '55401', + 'confidence' => 33, + ], 'registered_country' => [ 'geoname_id' => 2, 'iso_code' => 'CA', @@ -56,6 +58,7 @@ public function testFull(): void 'geoname_id' => 3, 'iso_code' => 'GB', 'names' => ['en' => 'United Kingdom'], + 'type' => 'military', ], 'subdivisions' => [ [ @@ -74,13 +77,14 @@ public function testFull(): void 'is_anonymous' => true, 'is_anonymous_vpn' => true, 'is_hosting_provider' => true, + 'is_legitimate_proxy' => true, 'is_public_proxy' => true, 'is_residential_proxy' => true, - 'is_satellite_provider' => true, 'is_tor_exit_node' => true, 'isp' => 'Comcast', 'mobile_country_code' => '310', 'mobile_network_code' => '004', + 'network' => '1.2.3.0/24', 'organization' => 'Blorg', 'static_ip_score' => 1.3, 'user_count' => 2, @@ -181,21 +185,11 @@ public function testFull(): void '$model->traits->isResidentialProxy is true' ); - $this->assertTrue( - $model->traits->isSatelliteProvider, - '$model->traits->isSatelliteProvider is true' - ); - $this->assertTrue( $model->traits->isTorExitNode, '$model->traits->isTorExitNode is true' ); - $this->assertFalse( - $model->traits->isAnonymousProxy, - '$model->traits->isAnonymousProxy is false' - ); - $this->assertSame( '310', $model->traits->mobileCountryCode, @@ -220,22 +214,96 @@ public function testFull(): void 'queriesRemaining is correct' ); - $this->assertSame( - $raw, - $model->raw, - 'raw method returns raw input' - ); - $this->assertSame( 2, $model->traits->userCount, 'userCount is correct' ); + + $this->assertSame( + [ + 'continent' => [ + 'names' => ['en' => 'North America'], + 'code' => 'NA', + 'geoname_id' => 42, + ], + 'country' => [ + 'names' => ['en' => 'United States of America'], + 'confidence' => 99, + 'geoname_id' => 1, + 'iso_code' => 'US', + ], + 'maxmind' => [ + 'queries_remaining' => 22, + ], + 'registered_country' => [ + 'names' => ['en' => 'Canada'], + 'geoname_id' => 2, + 'iso_code' => 'CA', + ], + 'represented_country' => [ + 'names' => ['en' => 'United Kingdom'], + 'geoname_id' => 3, + 'iso_code' => 'GB', + 'type' => 'military', + ], + 'traits' => [ + 'autonomous_system_number' => 1234, + 'autonomous_system_organization' => 'AS Organization', + 'connection_type' => 'Cable/DSL', + 'domain' => 'example.com', + 'ip_address' => '1.2.3.4', + 'is_anonymous' => true, + 'is_anonymous_vpn' => true, + 'is_hosting_provider' => true, + 'is_legitimate_proxy' => true, + 'is_public_proxy' => true, + 'is_residential_proxy' => true, + 'is_tor_exit_node' => true, + 'isp' => 'Comcast', + 'mobile_country_code' => '310', + 'mobile_network_code' => '004', + 'network' => '1.2.3.0/24', + 'organization' => 'Blorg', + 'static_ip_score' => 1.3, + 'user_count' => 2, + 'user_type' => 'college', + ], + 'city' => [ + 'names' => ['en' => 'Minneapolis'], + 'confidence' => 76, + 'geoname_id' => 9876, + ], + 'location' => [ + 'average_income' => 24626, + 'accuracy_radius' => 1500, + 'latitude' => 44.98, + 'longitude' => 93.2636, + 'metro_code' => 765, + 'population_density' => 1341, + 'time_zone' => 'America/Chicago', + ], + 'postal' => [ + 'code' => '55401', + 'confidence' => 33, + ], + 'subdivisions' => [ + [ + 'names' => ['en' => 'Minnesota'], + 'confidence' => 88, + 'geoname_id' => 574635, + 'iso_code' => 'MN', + ], + ], + ], + $model->jsonSerialize(), + 'jsonSerialize returns initial array' + ); } public function testEmptyObjects(): void { - $raw = ['traits' => ['ip_address' => '5.6.7.8']]; + $raw = ['traits' => ['ip_address' => '5.6.7.8', 'network' => '5.6.7.0/24']]; $model = new Insights($raw, ['en']); @@ -293,11 +361,6 @@ public function testEmptyObjects(): void '$model->mostSpecificSubdivision' ); - $this->assertTrue( - isset($model->mostSpecificSubdivision), - 'mostSpecificSubdivision is set' - ); - $this->assertInstanceOf( 'GeoIp2\Record\Traits', $model->traits, @@ -306,8 +369,8 @@ public function testEmptyObjects(): void $this->assertSame( $raw, - $model->raw, - 'raw method returns raw input with no added empty values' + $model->jsonSerialize(), + 'jsonSerialize', ); } @@ -332,17 +395,11 @@ public function testUnknown(): void $model, 'no exception when Insights model gets raw data with unknown keys' ); - - $this->assertSame( - $raw, - $model->raw, - 'raw method returns raw input' - ); } public function testMostSpecificSubdivisionWithNoSubdivisions(): void { - $model = new Insights([], ['en']); + $model = new Insights(['traits' => ['ip_address' => '1.1.1.1']], ['en']); $this->assertTrue( isset($model->mostSpecificSubdivision), diff --git a/tests/bootstrap.php b/tests/bootstrap.php deleted file mode 100644 index 02c0592f..00000000 --- a/tests/bootstrap.php +++ /dev/null @@ -1,9 +0,0 @@ -add('GeoIp2\Test', __DIR__);