Completed
Push — master ( b8455b...ac458d )
by Tobias
03:32
created

IpInfoDb::__construct()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 12
cts 12
cp 1
rs 9.6
c 0
b 0
f 0
cc 3
nc 3
nop 3
crap 3
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('Invalid precision value "%s" (allowed values: "city", "country").', $precision));
77
        }
78 17
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83 15
    public function geocodeQuery(GeocodeQuery $query): Collection
84
    {
85 15
        $address = $query->getText();
86 15
        if (null === $this->apiKey) {
87
            throw new InvalidCredentials('No API Key provided.');
88
        }
89
90 15
        if (!filter_var($address, FILTER_VALIDATE_IP)) {
91 2
            throw new UnsupportedOperation('The IpInfoDb provider does not support street addresses, only IPv4 addresses.');
92
        }
93
94
        // This API does not support IPv6
95 13
        if (filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
96 2
            throw new UnsupportedOperation('The IpInfoDb provider does not support IPv6 addresses, only IPv4 addresses.');
97
        }
98
99 11
        if ('127.0.0.1' === $address) {
100 1
            return new AddressCollection([$this->getLocationForLocalhost()]);
101
        }
102
103 10
        $url = sprintf($this->endpointUrl, $this->apiKey, $address);
104
105 10
        return $this->executeQuery($url);
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111 1
    public function reverseQuery(ReverseQuery $query): Collection
112
    {
113 1
        throw new UnsupportedOperation('The IpInfoDb provider is not able to do reverse geocoding.');
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119 5
    public function getName(): string
120
    {
121 5
        return 'ip_info_db';
122
    }
123
124
    /**
125
     * @param string $url
126
     *
127
     * @return Collection
128
     */
129 10
    private function executeQuery(string $url): AddressCollection
130
    {
131 10
        $content = $this->getUrlContents($url);
132 3
        $data = json_decode($content, true);
133
134 3
        if (empty($data) || 'OK' !== $data['statusCode']) {
135
            return new AddressCollection([]);
136
        }
137
138 3
        $timezone = null;
139 3
        if (isset($data['timeZone'])) {
140 2
            $timezone = timezone_name_from_abbr('', (int) substr($data['timeZone'], 0, strpos($data['timeZone'], ':')) * 3600, 0);
141
        }
142
143 3
        return new AddressCollection([
144 3
            Address::createFromArray([
145 3
                'providedBy' => $this->getName(),
146 3
                'latitude' => $data['latitude'] ?? null,
147 3
                'longitude' => $data['longitude'] ?? null,
148 3
                'locality' => $data['cityName'] ?? null,
149 3
                'postalCode' => $data['zipCode'] ?? null,
150 3
                'adminLevels' => isset($data['regionName']) ? [['name' => $data['regionName'], 'level' => 1]] : [],
151 3
                'country' => $data['countryName'] ?? null,
152 3
                'countryCode' => $data['countryName'] ?? null,
153 3
                'timezone' => $timezone,
154
            ]),
155
        ]);
156
    }
157
}
158