GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

OpenCage   A
last analyzed

Complexity

Total Complexity 33

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 33
lcom 1
cbo 10
dl 0
loc 205
rs 9.3999
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A guessLocality() 0 6 1
A guessStreetName() 0 6 1
A guessSubLocality() 0 6 1
A guessBestComponent() 0 10 4
A __construct() 0 9 2
A geocodeQuery() 0 13 2
A reverseQuery() 0 12 2
A getName() 0 4 1
C executeQuery() 0 76 19
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\OpenCage;
14
15
use Geocoder\Exception\InvalidArgument;
16
use Geocoder\Exception\InvalidCredentials;
17
use Geocoder\Exception\QuotaExceeded;
18
use Geocoder\Exception\UnsupportedOperation;
19
use Geocoder\Collection;
20
use Geocoder\Model\Address;
21
use Geocoder\Model\AddressCollection;
22
use Geocoder\Query\GeocodeQuery;
23
use Geocoder\Query\ReverseQuery;
24
use Geocoder\Http\Provider\AbstractHttpProvider;
25
use Geocoder\Provider\Provider;
26
use Http\Client\HttpClient;
27
28
/**
29
 * @author mtm <[email protected]>
30
 */
31
final class OpenCage extends AbstractHttpProvider implements Provider
32
{
33
    /**
34
     * @var string
35
     */
36
    const GEOCODE_ENDPOINT_URL = 'https://api.opencagedata.com/geocode/v1/json?key=%s&query=%s&limit=%d&pretty=1';
37
38
    /**
39
     * @var string
40
     */
41
    private $apiKey;
42
43
    /**
44
     * @param HttpClient $client an HTTP adapter
45
     * @param string     $apiKey an API key
46
     */
47
    public function __construct(HttpClient $client, string $apiKey)
48
    {
49
        if (empty($apiKey)) {
50
            throw new InvalidCredentials('No API key provided.');
51
        }
52
53
        $this->apiKey = $apiKey;
54
        parent::__construct($client);
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    public function geocodeQuery(GeocodeQuery $query): Collection
61
    {
62
        $address = $query->getText();
63
64
        // This API doesn't handle IPs
65
        if (filter_var($address, FILTER_VALIDATE_IP)) {
66
            throw new UnsupportedOperation('The OpenCage provider does not support IP addresses, only street addresses.');
67
        }
68
69
        $url = sprintf(self::GEOCODE_ENDPOINT_URL, $this->apiKey, urlencode($address), $query->getLimit());
70
71
        return $this->executeQuery($url, $query->getLocale());
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77
    public function reverseQuery(ReverseQuery $query): Collection
78
    {
79
        $coordinates = $query->getCoordinates();
80
        $address = sprintf('%f, %f', $coordinates->getLatitude(), $coordinates->getLongitude());
81
82
        $geocodeQuery = GeocodeQuery::create($address);
83
        if (null !== $locale = $query->getLocale()) {
84
            $geocodeQuery = $geocodeQuery->withLocale($query->getLocale());
85
        }
86
87
        return $this->geocodeQuery($geocodeQuery);
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93
    public function getName(): string
94
    {
95
        return 'opencage';
96
    }
97
98
    /**
99
     * @param string      $url
100
     * @param string|null $locale
101
     *
102
     * @return AddressCollection
103
     *
104
     * @throws \Geocoder\Exception\Exception
105
     */
106
    private function executeQuery(string $url, string $locale = null): AddressCollection
107
    {
108
        if (null !== $locale) {
109
            $url = sprintf('%s&language=%s', $url, $locale);
110
        }
111
112
        $content = $this->getUrlContents($url);
113
        $json = json_decode($content, true);
114
115
        // https://geocoder.opencagedata.com/api#codes
116
        if (isset($json['status'])) {
117
            switch ($json['status']['code']) {
118
                case 400:
119
                    throw new InvalidArgument('Invalid request (a required parameter is missing).');
120
                case 402:
121
                    throw new QuotaExceeded('Valid request but quota exceeded.');
122
                case 403:
123
                    throw new InvalidCredentials('Invalid or missing api key.');
124
            }
125
        }
126
127
        if (!isset($json['total_results']) || 0 == $json['total_results']) {
128
            return new AddressCollection([]);
129
        }
130
131
        $locations = $json['results'];
132
133
        if (empty($locations)) {
134
            return new AddressCollection([]);
135
        }
136
137
        $results = [];
138
        foreach ($locations as $location) {
139
            $bounds = [
140
                'south' => null,
141
                'west' => null,
142
                'north' => null,
143
                'east' => null,
144
            ];
145
            if (isset($location['bounds'])) {
146
                $bounds = [
147
                    'south' => $location['bounds']['southwest']['lat'],
148
                    'west' => $location['bounds']['southwest']['lng'],
149
                    'north' => $location['bounds']['northeast']['lat'],
150
                    'east' => $location['bounds']['northeast']['lng'],
151
                ];
152
            }
153
154
            $comp = $location['components'];
155
156
            $adminLevels = [];
157
            foreach (['state', 'county'] as $i => $component) {
158
                if (isset($comp[$component])) {
159
                    $adminLevels[] = ['name' => $comp[$component], 'level' => $i + 1];
160
                }
161
            }
162
163
            $results[] = Address::createFromArray([
164
                'providedBy' => $this->getName(),
165
                'latitude' => $location['geometry']['lat'],
166
                'longitude' => $location['geometry']['lng'],
167
                'bounds' => $bounds ?: [],
168
                'streetNumber' => isset($comp['house_number']) ? $comp['house_number'] : null,
169
                'streetName' => $this->guessStreetName($comp),
170
                'subLocality' => $this->guessSubLocality($comp),
171
                'locality' => $this->guessLocality($comp),
172
                'postalCode' => isset($comp['postcode']) ? $comp['postcode'] : null,
173
                'adminLevels' => $adminLevels,
174
                'country' => isset($comp['country']) ? $comp['country'] : null,
175
                'countryCode' => isset($comp['country_code']) ? strtoupper($comp['country_code']) : null,
176
                'timezone' => isset($location['annotations']['timezone']['name']) ? $location['annotations']['timezone']['name'] : null,
177
            ]);
178
        }
179
180
        return new AddressCollection($results);
181
    }
182
183
    /**
184
     * @param array $components
185
     *
186
     * @return null|string
187
     */
188
    protected function guessLocality(array $components)
189
    {
190
        $localityKeys = ['city', 'town', 'village', 'hamlet'];
191
192
        return $this->guessBestComponent($components, $localityKeys);
193
    }
194
195
    /**
196
     * @param array $components
197
     *
198
     * @return null|string
199
     */
200
    protected function guessStreetName(array $components)
201
    {
202
        $streetNameKeys = ['road', 'street', 'street_name', 'residential'];
203
204
        return $this->guessBestComponent($components, $streetNameKeys);
205
    }
206
207
    /**
208
     * @param array $components
209
     *
210
     * @return null|string
211
     */
212
    protected function guessSubLocality(array $components)
213
    {
214
        $subLocalityKeys = ['suburb', 'neighbourhood', 'city_district'];
215
216
        return $this->guessBestComponent($components, $subLocalityKeys);
217
    }
218
219
    /**
220
     * @param array $components
221
     * @param array $keys
222
     *
223
     * @return null|string
224
     */
225
    protected function guessBestComponent(array $components, array $keys)
226
    {
227
        foreach ($keys as $key) {
228
            if (isset($components[$key]) && !empty($components[$key])) {
229
                return $components[$key];
230
            }
231
        }
232
233
        return null;
234
    }
235
}
236