Skip to content

Commit e2fcac2

Browse files
ip2locationjbelien
authored andcommitted
Added IP2Location and IP2LocationBinary provider. (#1031)
* Added IP2Location and IP2LocationBinary provider. * Fixed spacing issues. * Replaced tabs into spaces. * Update src/Provider/IP2Location/IP2Location.php Co-Authored-By: atymic <[email protected]> * Updated codes and format to match Geocoder standards and requirements. * Added IP2Location API configuration. * Updated indent and formatting issues. * Updated Readme.md. * Updated Readme.md to explain IP2Location Web service credit usage. * Update Readme.md
0 parents  commit e2fcac2

File tree

12 files changed

+516
-0
lines changed

12 files changed

+516
-0
lines changed

.gitattributes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.gitattributes export-ignore
2+
.travis.yml export-ignore
3+
phpunit.xml.dist export-ignore
4+
Tests/ export-ignore

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml

.travis.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
language: php
2+
sudo: false
3+
4+
php: 7.2
5+
6+
install:
7+
- composer update --prefer-stable --prefer-dist
8+
9+
script:
10+
- composer test-ci
11+
12+
after_success:
13+
- wget https://scrutinizer-ci.com/ocular.phar
14+
- php ocular.phar code-coverage:upload --format=php-clover build/coverage.xml
15+

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Change Log
2+
3+
The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release.
4+
5+
## 1.0.0
6+
7+
First release of this library.

IP2LocationBinary.php

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Geocoder package.
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*
10+
* @license MIT License
11+
*/
12+
13+
namespace Geocoder\Provider\IP2LocationBinary;
14+
15+
use Geocoder\Collection;
16+
use Geocoder\Exception\FunctionNotFound;
17+
use Geocoder\Exception\InvalidArgument;
18+
use Geocoder\Exception\UnsupportedOperation;
19+
use Geocoder\Model\Address;
20+
use Geocoder\Model\AddressCollection;
21+
use Geocoder\Query\GeocodeQuery;
22+
use Geocoder\Query\ReverseQuery;
23+
use Geocoder\Provider\AbstractProvider;
24+
use Geocoder\Provider\Provider;
25+
26+
final class IP2LocationBinary extends AbstractProvider implements Provider
27+
{
28+
/**
29+
* @var string
30+
*/
31+
private $binFile;
32+
33+
/**
34+
* @var int|null
35+
*/
36+
private $openFlag;
37+
38+
/**
39+
* @param string $binFile
40+
* @param int|null $openFlag
41+
*
42+
* @throws FunctionNotFound if IP2Location's library not installed
43+
* @throws InvalidArgument if dat file is not correct (optional)
44+
*/
45+
public function __construct(string $binFile, int $openFlag = null)
46+
{
47+
if (false === class_exists('\\IP2Location\\Database')) {
48+
throw new FunctionNotFound(
49+
'ip2location_database',
50+
'The IP2LocationBinary requires IP2Location\'s library to be installed and loaded.'
51+
);
52+
}
53+
54+
if (false === is_file($binFile)) {
55+
throw new InvalidArgument(sprintf('Given IP2Location BIN file "%s" does not exist.', $binFile));
56+
}
57+
58+
if (false === is_readable($binFile)) {
59+
throw new InvalidArgument(sprintf('Given IP2Location BIN file "%s" does not readable.', $binFile));
60+
}
61+
62+
$this->binFile = $binFile;
63+
$this->openFlag = null === $openFlag ? \IP2Location\Database::FILE_IO : $openFlag;
64+
}
65+
66+
/**
67+
* {@inheritdoc}
68+
*/
69+
public function geocodeQuery(GeocodeQuery $query): Collection
70+
{
71+
$address = $query->getText();
72+
if (false === filter_var($address, FILTER_VALIDATE_IP)) {
73+
throw new UnsupportedOperation('The IP2LocationBinary provider does not support street addresses.');
74+
}
75+
76+
$db = new \IP2Location\Database($this->binFile, $this->openFlag);
77+
$records = $db->lookup($address, \IP2Location\Database::ALL);
78+
79+
if (false === $records) {
80+
return new AddressCollection([]);
81+
}
82+
83+
$adminLevels = [];
84+
85+
if (isset($records['regionName'])) {
86+
$adminLevels[] = ['name' => $records['regionName'], 'level' => 1];
87+
}
88+
89+
return new AddressCollection([
90+
Address::createFromArray([
91+
'providedBy' => $this->getName(),
92+
'countryCode' => $records['countryCode'],
93+
'country' => null === $records['countryName'] ? null : utf8_encode($records['countryName']),
94+
'adminLevels' => $adminLevels,
95+
'locality' => null === $records['cityName'] ? null : utf8_encode($records['cityName']),
96+
'latitude' => $records['latitude'],
97+
'longitude' => $records['longitude'],
98+
'postalCode' => $records['zipCode'],
99+
]),
100+
]);
101+
}
102+
103+
/**
104+
* {@inheritdoc}
105+
*/
106+
public function reverseQuery(ReverseQuery $query): Collection
107+
{
108+
throw new UnsupportedOperation('The IP2LocationBinary is not able to do reverse geocoding.');
109+
}
110+
111+
/**
112+
* {@inheritdoc}
113+
*/
114+
public function getName(): string
115+
{
116+
return 'ip2location_binary';
117+
}
118+
}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2011 — William Durand <[email protected]>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Readme.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# IP2Location Binary Geocoder provider
2+
[![Build Status](https://travis-ci.org/geocoder-php/ip2location-binary-provider.svg?branch=master)](http://travis-ci.org/geocoder-php/ip2location-binary-provider)
3+
[![Latest Stable Version](https://poser.pugx.org/geocoder-php/ip2location-binary-provider/v/stable)](https://packagist.org/packages/geocoder-php/ip2location-binary-provider)
4+
[![Total Downloads](https://poser.pugx.org/geocoder-php/ip2location-binary-provider/downloads)](https://packagist.org/packages/geocoder-php/ip2location-binary-provider)
5+
[![Monthly Downloads](https://poser.pugx.org/geocoder-php/ip2location-binary-provider/d/monthly.png)](https://packagist.org/packages/geocoder-php/ip2location-binary-provider)
6+
[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/geocoder-php/ip2location-binary-provider.svg?style=flat-square)](https://scrutinizer-ci.com/g/geocoder-php/ip2location-binary-provider)
7+
[![Quality Score](https://img.shields.io/scrutinizer/g/geocoder-php/ip2location-binary-provider.svg?style=flat-square)](https://scrutinizer-ci.com/g/geocoder-php/ip2location-binary-provider)
8+
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
9+
10+
This is the IP2Location Binary provider from the PHP Geocoder. This is a **READ ONLY** repository. See the
11+
[main repo](https://github.com/geocoder-php/Geocoder) for information and documentation.
12+
13+
### Install
14+
15+
```bash
16+
composer require geocoder-php/ip2location-binary-provider
17+
```
18+
19+
### Note
20+
21+
This provider requires a BIN database file. You can download the free database from [IP2Location LITE](https://lite.ip2location.com/) website or the commercial database from [IP2Location](https://www.ip2location.com/).
22+
23+
For testing, download the BIN database from [here](https://lite.ip2location.com/database/ip-country-region-city-latitude-longitude-zipcode), decompress and upload the .BIN file into `.\Tests\fixtures`.
24+
25+
### Contribute
26+
27+
Contributions are very welcome! Send a pull request to the [main repository](https://github.com/geocoder-php/Geocoder) or
28+
report any issues you find on the [issue tracker](https://github.com/geocoder-php/Geocoder/issues).

Tests/.cached_responses/.gitkeep

Whitespace-only changes.

Tests/IP2LocationBinaryTest.php

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Geocoder package.
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*
10+
* @license MIT License
11+
*/
12+
13+
namespace Geocoder\Provider\IP2LocationBinary\Tests;
14+
15+
use Geocoder\IntegrationTest\BaseTestCase;
16+
use Geocoder\Location;
17+
use Geocoder\Query\GeocodeQuery;
18+
use Geocoder\Query\ReverseQuery;
19+
use Geocoder\Provider\IP2LocationBinary\IP2LocationBinary;
20+
21+
class IP2LocationBinaryTest extends BaseTestCase
22+
{
23+
private $binaryFile;
24+
25+
public function setUp()
26+
{
27+
// Download this BIN database from https://lite.ip2location.com/database/ip-country-region-city-latitude-longitude-zipcode
28+
$this->binaryFile = __DIR__.'/fixtures/IP2LOCATION-LITE-DB9.IPV6.BIN';
29+
}
30+
31+
protected function getCacheDir()
32+
{
33+
return __DIR__.'/.cached_responses';
34+
}
35+
36+
public static function setUpBeforeClass()
37+
{
38+
if (false == class_exists('\\IP2Location\\Database')) {
39+
self::markTestSkipped('The IP2Location\'s official library required to run these tests.');
40+
}
41+
}
42+
43+
public static function provideIps()
44+
{
45+
return [
46+
'8.8.8.8' => ['8.8.8.8', 'Mountain View', 'United States'],
47+
'123.123.123.123' => ['123.123.123.123', 'Beijing', 'China'],
48+
];
49+
}
50+
51+
/**
52+
* @expectedException \Geocoder\Exception\InvalidArgument
53+
* @expectedExceptionMessage Given IP2Location BIN file "NOT_EXIST.BIN" does not exist.
54+
*/
55+
public function testThrowIfNotExistBinaryFileGiven()
56+
{
57+
new IP2LocationBinary('NOT_EXIST.BIN');
58+
}
59+
60+
public function testLocationResultContainsExpectedFieldsForAnAmericanIp()
61+
{
62+
$provider = new IP2LocationBinary($this->binaryFile);
63+
$results = $provider->geocodeQuery(GeocodeQuery::create('8.8.8.8'));
64+
65+
$this->assertInstanceOf('Geocoder\Model\AddressCollection', $results);
66+
$this->assertCount(1, $results);
67+
68+
/** @var Location $result */
69+
$result = $results->first();
70+
$this->assertInstanceOf('\Geocoder\Model\Address', $result);
71+
72+
$this->assertEquals('37.405990600586', $result->getCoordinates()->getLatitude(), '', 0.001);
73+
$this->assertEquals('-122.07851409912', $result->getCoordinates()->getLongitude(), '', 0.001);
74+
$this->assertNull($result->getBounds());
75+
$this->assertNull($result->getStreetNumber());
76+
$this->assertNull($result->getStreetName());
77+
$this->assertEquals('94043', $result->getPostalCode());
78+
$this->assertEquals('Mountain View', $result->getLocality());
79+
$this->assertNull($result->getSubLocality());
80+
$this->assertCount(1, $result->getAdminLevels());
81+
$this->assertEquals('California', $result->getAdminLevels()->get(1)->getName());
82+
$this->assertNull($result->getAdminLevels()->get(1)->getCode());
83+
$this->assertEquals('United States', $result->getCountry()->getName());
84+
$this->assertEquals('US', $result->getCountry()->getCode());
85+
$this->assertNull($result->getTimezone());
86+
}
87+
88+
public function testLocationResultContainsExpectedFieldsForAChinaIp()
89+
{
90+
$provider = new IP2LocationBinary($this->binaryFile);
91+
$results = $provider->geocodeQuery(GeocodeQuery::create('123.123.123.123'));
92+
93+
$this->assertInstanceOf('Geocoder\Model\AddressCollection', $results);
94+
$this->assertCount(1, $results);
95+
96+
/** @var Location $result */
97+
$result = $results->first();
98+
$this->assertInstanceOf('\Geocoder\Model\Address', $result);
99+
100+
$this->assertEquals('39.907501220703', $result->getCoordinates()->getLatitude(), '', 0.001);
101+
$this->assertEquals('116.39723205566', $result->getCoordinates()->getLongitude(), '', 0.001);
102+
$this->assertNull($result->getBounds());
103+
$this->assertNull($result->getStreetNumber());
104+
$this->assertNull($result->getStreetName());
105+
$this->assertEquals('100006', $result->getPostalCode());
106+
$this->assertEquals('Beijing', $result->getLocality());
107+
$this->assertNull($result->getSubLocality());
108+
$this->assertCount(1, $result->getAdminLevels());
109+
$this->assertEquals('Beijing', $result->getAdminLevels()->get(1)->getName());
110+
$this->assertNull($result->getAdminLevels()->get(1)->getCode());
111+
$this->assertEquals('China', $result->getCountry()->getName());
112+
$this->assertEquals('CN', $result->getCountry()->getCode());
113+
$this->assertNull($result->getTimezone());
114+
}
115+
116+
public function testGeocodeWithRealIPv6()
117+
{
118+
$provider = new IP2LocationBinary($this->binaryFile);
119+
$results = $provider->geocodeQuery(GeocodeQuery::create('2001:4860:4860::8888'));
120+
121+
$this->assertInstanceOf('Geocoder\Model\AddressCollection', $results);
122+
$this->assertCount(1, $results);
123+
124+
/** @var Location $result */
125+
$result = $results->first();
126+
$this->assertInstanceOf('\Geocoder\Model\Address', $result);
127+
128+
$this->assertEquals('37.386051', $result->getCoordinates()->getLatitude(), '', 0.001);
129+
$this->assertEquals('-122.083847', $result->getCoordinates()->getLongitude(), '', 0.001);
130+
$this->assertNull($result->getBounds());
131+
$this->assertNull($result->getStreetNumber());
132+
$this->assertNull($result->getStreetName());
133+
$this->assertEquals('94041', $result->getPostalCode());
134+
$this->assertEquals('Mountain View', $result->getLocality());
135+
$this->assertNull($result->getSubLocality());
136+
$this->assertCount(1, $result->getAdminLevels());
137+
$this->assertEquals('California', $result->getAdminLevels()->get(1)->getName());
138+
$this->assertNull($result->getAdminLevels()->get(1)->getCode());
139+
$this->assertEquals('United States', $result->getCountry()->getName());
140+
$this->assertEquals('US', $result->getCountry()->getCode());
141+
$this->assertNull($result->getTimezone());
142+
}
143+
144+
/**
145+
* @dataProvider provideIps
146+
*/
147+
public function testFindLocationByIp($ip, $expectedCity, $expectedCountry)
148+
{
149+
$provider = new IP2LocationBinary($this->binaryFile);
150+
$results = $provider->geocodeQuery(GeocodeQuery::create($ip));
151+
152+
$this->assertInstanceOf('Geocoder\Model\AddressCollection', $results);
153+
$this->assertCount(1, $results);
154+
155+
/** @var Location $result */
156+
$result = $results->first();
157+
$this->assertInstanceOf('\Geocoder\Model\Address', $result);
158+
$this->assertEquals($expectedCity, $result->getLocality());
159+
$this->assertEquals($expectedCountry, $result->getCountry()->getName());
160+
}
161+
162+
public function testGetName()
163+
{
164+
$provider = new IP2LocationBinary($this->binaryFile);
165+
166+
$this->assertEquals('ip2location_binary', $provider->getName());
167+
}
168+
169+
/**
170+
* @expectedException \Geocoder\Exception\UnsupportedOperation
171+
* @expectedExceptionMessage The IP2LocationBinary provider does not support street addresses.
172+
*/
173+
public function testThrowIfInvalidIpAddressGiven()
174+
{
175+
$provider = new IP2LocationBinary($this->binaryFile);
176+
177+
$provider->geocodeQuery(GeocodeQuery::create('invalidIp'));
178+
}
179+
180+
/**
181+
* @expectedException \Geocoder\Exception\UnsupportedOperation
182+
* @expectedExceptionMessage The IP2LocationBinary is not able to do reverse geocoding.
183+
*/
184+
public function testThrowOnReverseMethodUsage()
185+
{
186+
$provider = new IP2LocationBinary($this->binaryFile);
187+
188+
$provider->reverseQuery(ReverseQuery::fromCoordinates(0, 0));
189+
}
190+
}

0 commit comments

Comments
 (0)