Completed
Push — master ( 6ed73a...e1f43a )
by Tobias
03:02
created

IpInfoDb::reverseQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
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\IpInfoDb;
14
15
use Geocoder\Exception\InvalidArgument;
16
use Geocoder\Exception\InvalidCredentials;
17
use Geocoder\Exception\UnsupportedOperation;
18
use Geocoder\Collection;
19
use Geocoder\Model\Address;
20
use Geocoder\Model\AddressCollection;
21
use Geocoder\Query\GeocodeQuery;
22
use Geocoder\Query\ReverseQuery;
23
use Geocoder\Http\Provider\AbstractHttpProvider;
24
use Geocoder\Provider\Provider;
25
use Http\Client\HttpClient;
26
27
/**
28
 * @author William Durand <[email protected]>
29
 */
30
final class IpInfoDb extends AbstractHttpProvider implements Provider
31
{
32
    /**
33
     * @var string
34
     */
35
    const CITY_PRECISION_ENDPOINT_URL = 'https://api.ipinfodb.com/v3/ip-city/?key=%s&format=json&ip=%s';
36
37
    /**
38
     * @var string
39
     */
40
    const COUNTRY_PRECISION_ENDPOINT_URL = 'https://api.ipinfodb.com/v3/ip-country/?key=%s&format=json&ip=%s';
41
42
    /**
43
     * @var string
44
     */
45
    private $apiKey;
46
47
    /**
48
     * @var string
49
     */
50
    private $endpointUrl;
51
52
    /**
53
     * @param HttpClient $client    an HTTP adapter
54
     * @param string     $apiKey    an API key
55
     * @param string     $precision The endpoint precision. Either "city" or "country" (faster)
56
     *
57
     * @throws \Geocoder\Exception\InvalidArgument
58
     */
59 18
    public function __construct(HttpClient $client, string $apiKey, string $precision = 'city')
60
    {
61 18
        parent::__construct($client);
62
63 18
        $this->apiKey = $apiKey;
64 18
        switch ($precision) {
65 18
            case 'city':
66 16
                $this->endpointUrl = self::CITY_PRECISION_ENDPOINT_URL;
67
68 16
                break;
69
70 2
            case 'country':
71 1
                $this->endpointUrl = self::COUNTRY_PRECISION_ENDPOINT_URL;
72
73 1
                break;
74
75
            default:
76 1
                throw new InvalidArgument(sprintf(
77 1
                    'Invalid precision value "%s" (allowed values: "city", "country").',
78 1
                    $precision
79
                ));
80
        }
81 17
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86 15
    public function geocodeQuery(GeocodeQuery $query): Collection
87
    {
88 15
        $address = $query->getText();
89 15
        if (null === $this->apiKey) {
90
            throw new InvalidCredentials('No API Key provided.');
91
        }
92
93 15
        if (!filter_var($address, FILTER_VALIDATE_IP)) {
94 2
            throw new UnsupportedOperation('The IpInfoDb provider does not support street addresses, only IPv4 addresses.');
95
        }
96
97
        // This API does not support IPv6
98 13
        if (filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
99 2
            throw new UnsupportedOperation('The IpInfoDb provider does not support IPv6 addresses, only IPv4 addresses.');
100
        }
101
102 11
        if ('127.0.0.1' === $address) {
103 1
            return new AddressCollection([$this->getLocationForLocalhost()]);
104
        }
105
106 10
        $url = sprintf($this->endpointUrl, $this->apiKey, $address);
107
108 10
        return $this->executeQuery($url);
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114 1
    public function reverseQuery(ReverseQuery $query): Collection
115
    {
116 1
        throw new UnsupportedOperation('The IpInfoDb provider is not able to do reverse geocoding.');
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122 5
    public function getName(): string
123
    {
124 5
        return 'ip_info_db';
125
    }
126
127
    /**
128
     * @param string $url
129
     *
130
     * @return Collection
131
     */
132 10
    private function executeQuery(string $url): AddressCollection
133
    {
134 10
        $content = $this->getUrlContents($url);
135 3
        $data = json_decode($content, true);
136
137 3
        if (empty($data) || 'OK' !== $data['statusCode']) {
138
            return new AddressCollection([]);
139
        }
140
141 3
        $timezone = null;
142 3
        if (isset($data['timeZone'])) {
143 2
            $timezone = timezone_name_from_abbr('', (int) substr($data['timeZone'], 0, strpos($data['timeZone'], ':')) * 3600, 0);
144
        }
145
146 3
        return new AddressCollection([
147 3
            Address::createFromArray([
148 3
                'providedBy' => $this->getName(),
149 3
                'latitude' => $data['latitude'] ?? null,
150 3
                'longitude' => $data['longitude'] ?? null,
151 3
                'locality' => $data['cityName'] ?? null,
152 3
                'postalCode' => $data['zipCode'] ?? null,
153 3
                'adminLevels' => isset($data['regionName']) ? [['name' => $data['regionName'], 'level' => 1]] : [],
154 3
                'country' => $data['countryName'] ?? null,
155 3
                'countryCode' => $data['countryName'] ?? null,
156 3
                'timezone' => $timezone,
157
            ]),
158
        ]);
159
    }
160
}
161