Skip to content

Commit

Permalink
Created HostIpXml all alternative to HostIp (#1057)
Browse files Browse the repository at this point in the history
* Created HostIpXml all alternative to HostIp(json) because JSON does not have lat,lng anymore.

!squash code style fixes

* added require to provider composer.json

* updated function fqn

* added interface to AbstractHostIp
  • Loading branch information
oleg-andreyev committed Oct 16, 2020
1 parent 41e5b83 commit 92eed06
Show file tree
Hide file tree
Showing 10 changed files with 373 additions and 74 deletions.
119 changes: 119 additions & 0 deletions AbstractHostIp.php
@@ -0,0 +1,119 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Geocoder package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/

namespace Geocoder\Provider\HostIp;

use Geocoder\Collection;
use Geocoder\Exception\UnsupportedOperation;
use Geocoder\Http\Provider\AbstractHttpProvider;
use Geocoder\Model\Address;
use Geocoder\Model\AddressCollection;
use Geocoder\Query\GeocodeQuery;
use Geocoder\Query\ReverseQuery;

/**
* @author William Durand <william.durand1@gmail.com>
* @author Oleg Andreyev <oleg@andreyev.lv>
*/
abstract class AbstractHostIp extends AbstractHttpProvider implements \Geocoder\Provider\Provider
{
abstract protected function executeQuery(string $url): AddressCollection;

/**
* {@inheritdoc}
*/
public function geocodeQuery(GeocodeQuery $query): Collection
{
$address = $query->getText();
if (!filter_var($address, FILTER_VALIDATE_IP)) {
throw new UnsupportedOperation('The '.get_class($this).' provider does not support Street addresses.');
}

// This API does not support IPv6
if (filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
throw new UnsupportedOperation('The HostIp provider does not support IPv6 addresses.');
}

if ('127.0.0.1' === $address) {
return new AddressCollection([$this->getLocationForLocalhost()]);
}

$url = sprintf(static::ENDPOINT_URL, $address);

return $this->executeQuery($url);
}

/**
* {@inheritdoc}
*/
public function reverseQuery(ReverseQuery $query): Collection
{
throw new UnsupportedOperation('The HostIp provider is not able to do reverse geocoding.');
}

/**
* @param array $data
*
* @return bool
*/
protected function isUnknownLocation(array $data): bool
{
return empty($data['lat'])
&& empty($data['lng'])
&& '(Unknown City?)' === $data['city']
&& '(Unknown Country?)' === $data['country_name']
&& 'XX' === $data;
}

/**
* @param array $data
*
* @return bool
*/
protected function isPrivateLocation(array $data): bool
{
return empty($data['lat'])
&& empty($data['lng'])
&& '(Private Address)' === $data['city']
&& '(Private Address)' === $data['country_name']
&& 'XX' === $data;
}

/**
* @param array $data
*
* @return AddressCollection
*/
protected function prepareAddressCollection(array $data): AddressCollection
{
// Return empty collection if address was not found
if ($this->isUnknownLocation($data)) {
return new AddressCollection([]);
}

// Return empty collection if address was not found
if ($this->isPrivateLocation($data)) {
return new AddressCollection([]);
}

return new AddressCollection([
Address::createFromArray([
'providedBy' => $this->getName(),
'latitude' => $data['lat'] ?? null,
'longitude' => $data['lng'] ?? null,
'locality' => $data['city'],
'country' => $data['country_name'],
'countryCode' => $data['country_code'],
]),
]);
}
}
72 changes: 4 additions & 68 deletions HostIp.php
Expand Up @@ -12,57 +12,20 @@

namespace Geocoder\Provider\HostIp;

use Geocoder\Exception\UnsupportedOperation;
use Geocoder\Collection;
use Geocoder\Model\Address;
use Geocoder\Model\AddressCollection;
use Geocoder\Query\GeocodeQuery;
use Geocoder\Query\ReverseQuery;
use Geocoder\Http\Provider\AbstractHttpProvider;
use Geocoder\Provider\Provider;
use function json_decode;

/**
* @author William Durand <william.durand1@gmail.com>
*/
final class HostIp extends AbstractHttpProvider implements Provider
final class HostIp extends AbstractHostIp
{
/**
* @var string
*/
const ENDPOINT_URL = 'http://api.hostip.info/get_json.php?ip=%s&position=true';

/**
* {@inheritdoc}
*/
public function geocodeQuery(GeocodeQuery $query): Collection
{
$address = $query->getText();
if (!filter_var($address, FILTER_VALIDATE_IP)) {
throw new UnsupportedOperation('The HostIp provider does not support Street addresses.');
}

// This API does not support IPv6
if (filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
throw new UnsupportedOperation('The HostIp provider does not support IPv6 addresses.');
}

if ('127.0.0.1' === $address) {
return new AddressCollection([$this->getLocationForLocalhost()]);
}

$url = sprintf(self::ENDPOINT_URL, $address);

return $this->executeQuery($url);
}

/**
* {@inheritdoc}
*/
public function reverseQuery(ReverseQuery $query): Collection
{
throw new UnsupportedOperation('The HostIp provider is not able to do reverse geocoding.');
}

/**
* {@inheritdoc}
*/
Expand All @@ -76,7 +39,7 @@ public function getName(): string
*
* @return Collection
*/
private function executeQuery(string $url): AddressCollection
protected function executeQuery(string $url): AddressCollection
{
$content = $this->getUrlContents($url);
$data = json_decode($content, true);
Expand All @@ -85,33 +48,6 @@ private function executeQuery(string $url): AddressCollection
return new AddressCollection([]);
}

// Return empty collection if address was not found
if (null === $data['lat']
&& null === $data['lng']
&& '(Unknown City?)' === $data['city']
&& '(Unknown Country?)' === $data['country_name']
&& 'XX' === $data['country_code']) {
return new AddressCollection([]);
}

// Return empty collection if address was not found
if (null === $data['lat']
&& null === $data['lng']
&& '(Private Address)' === $data['city']
&& '(Private Address)' === $data['country_name']
&& 'XX' === $data['country_code']) {
return new AddressCollection([]);
}

return new AddressCollection([
Address::createFromArray([
'providedBy' => $this->getName(),
'latitude' => $data['lat'],
'longitude' => $data['lng'],
'locality' => $data['city'],
'country' => $data['country_name'],
'countryCode' => $data['country_code'],
]),
]);
return $this->prepareAddressCollection($data);
}
}
72 changes: 72 additions & 0 deletions HostIpXml.php
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Geocoder package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/

namespace Geocoder\Provider\HostIp;

use Geocoder\Collection;
use Geocoder\Model\AddressCollection;
use function simplexml_load_string;

/**
* @author Oleg Andreyev <oleg@andreyev.lv>
*/
final class HostIpXml extends AbstractHostIp
{
/**
* @var string
*/
const ENDPOINT_URL = 'http://api.hostip.info/get_xml.php?ip=%s&position=true';

/**
* {@inheritdoc}
*/
public function getName(): string
{
return 'host_ip_xml';
}

/**
* @param string $url
*
* @return Collection
*/
protected function executeQuery(string $url): AddressCollection
{
$content = $this->getUrlContents($url);
$xml = simplexml_load_string($content);

$hostIp = $xml->xpath('/HostipLookupResultSet/gml:featureMember/Hostip');
if (empty($hostIp[0])) {
return new AddressCollection([]);
}
$hostIp = $hostIp[0];

$city = (string) $hostIp->xpath('.//gml:name')[0];
$countryName = (string) $hostIp->xpath('.//countryName')[0];
$countryCode = (string) $hostIp->xpath('.//countryAbbrev')[0];
$coords = $hostIp->xpath('.//ipLocation/gml:pointProperty/gml:Point/gml:coordinates');

if (empty($coords)) {
list($lng, $lat) = [null, null];
} else {
list($lng, $lat) = explode(',', (string) $coords[0], 2);
}

return $this->prepareAddressCollection([
'lat' => $lat,
'lng' => $lng,
'city' => $city,
'country_name' => $countryName,
'country_code' => $countryCode,
]);
}
}
@@ -1 +1 @@
s:117:"{"country_name":"UNITED STATES","country_code":"US","city":"(Unknown city)","ip":"33.33.33.22","lat":null,"lng":null}";
s:95:"{"country_name":"UNITED STATES","country_code":"US","city":"(Unknown city)","ip":"33.33.33.22"}";
@@ -0,0 +1,18 @@
s:700:"<?xml version="1.0" encoding="ISO-8859-1" ?>
<HostipLookupResultSet version="1.0.1" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.hostip.info/api/hostip-1.0.1.xsd">
<gml:description>This is the Hostip Lookup Service</gml:description>
<gml:name>hostip</gml:name>
<gml:boundedBy>
<gml:Null>inapplicable</gml:Null>
</gml:boundedBy>
<gml:featureMember>
<Hostip>
<ip>33.33.33.22</ip>
<gml:name>(Unknown city)</gml:name>
<countryName>UNITED STATES</countryName>
<countryAbbrev>US</countryAbbrev>
<!-- Co-ordinates are unavailable -->
</Hostip>
</gml:featureMember>
</HostipLookupResultSet>
";
@@ -0,0 +1,25 @@
s:923:"<?xml version="1.0" encoding="ISO-8859-1" ?>
<HostipLookupResultSet version="1.0.1" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.hostip.info/api/hostip-1.0.1.xsd">
<gml:description>This is the Hostip Lookup Service</gml:description>
<gml:name>hostip</gml:name>
<gml:boundedBy>
<gml:Null>inapplicable</gml:Null>
</gml:boundedBy>
<gml:featureMember>
<Hostip>
<ip>77.38.216.139</ip>
<gml:name>Riga</gml:name>
<countryName>LATVIA</countryName>
<countryAbbrev>LV</countryAbbrev>
<!-- Co-ordinates are available as lng,lat -->
<ipLocation>
<gml:pointProperty>
<gml:Point srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">
<gml:coordinates>24.0833,56.8833</gml:coordinates>
</gml:Point>
</gml:pointProperty>
</ipLocation>
</Hostip>
</gml:featureMember>
</HostipLookupResultSet>
";
@@ -1 +1 @@
s:113:"{"country_name":"FRANCE","country_code":"FR","city":"Aulnat","ip":"88.188.221.14","lat":"45.5333","lng":"2.6167"}";
s:82:"{"country_name":"FRANCE","country_code":"FR","city":"Aulnat","ip":"88.188.221.14"}";
5 changes: 2 additions & 3 deletions Tests/HostIpTest.php
Expand Up @@ -33,7 +33,7 @@ public function testGetName()

/**
* @expectedException \Geocoder\Exception\UnsupportedOperation
* @expectedExceptionMessage The HostIp provider does not support Street addresses.
* @expectedExceptionMessage The Geocoder\Provider\HostIp\HostIp provider does not support Street addresses.
*/
public function testGeocodeWithAddress()
{
Expand Down Expand Up @@ -83,8 +83,7 @@ public function testGeocodeWithRealIPv4()
/** @var Location $result */
$result = $results->first();
$this->assertInstanceOf('\Geocoder\Model\Address', $result);
$this->assertEquals(45.5333, $result->getCoordinates()->getLatitude(), '', 0.0001);
$this->assertEquals(2.6167, $result->getCoordinates()->getLongitude(), '', 0.0001);
$this->assertEquals(null, $result->getCoordinates());
$this->assertNull($result->getPostalCode());
$this->assertEquals('Aulnat', $result->getLocality());
$this->assertEmpty($result->getAdminLevels());
Expand Down

0 comments on commit 92eed06

Please sign in to comment.