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.
Completed
Pull Request — master (#840)
by
unknown
04:36 queued 01:11
created

MapQuest::addOptionsForPostQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
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\MapQuest;
14
15
use Geocoder\Collection;
16
use Geocoder\Exception\InvalidArgument;
17
use Geocoder\Exception\InvalidCredentials;
18
use Geocoder\Exception\UnsupportedOperation;
19
use Geocoder\Http\Provider\AbstractHttpProvider;
20
use Geocoder\Location;
21
use Geocoder\Model\Address;
22
use Geocoder\Model\AddressCollection;
23
use Geocoder\Model\AdminLevel;
24
use Geocoder\Model\Bounds;
25
use Geocoder\Model\Country;
26
use Geocoder\Query\GeocodeQuery as CommonGeocodeQuery;
27
use Geocoder\Query\ReverseQuery;
28
use Geocoder\Provider\Provider;
29
use Http\Client\HttpClient;
30
31
/**
32
 * @author William Durand <[email protected]>
33
 */
34
final class MapQuest extends AbstractHttpProvider implements Provider
35
{
36
    const KEY_API_KEY = 'key';
37
38
    const KEY_LOCATION = 'location';
39
40
    const KEY_OUT_FORMAT = 'outFormat';
41
42
    const KEY_MAX_RESULTS = 'maxResults';
43
44
    const KEY_THUMB_MAPS = 'thumbMaps';
45
46
    const KEY_INTL_MODE = 'intlMode';
47
48
    const KEY_BOUNDING_BOX = 'boundingBox';
49
50
    const KEY_LAT = 'lat';
51
52
    const KEY_LNG = 'lng';
53
54
    const MODE_5BOX = '5BOX';
55
56
    const OPEN_BASE_URL = 'https://open.mapquestapi.com/geocoding/v1/';
57
58
    const LICENSED_BASE_URL = 'https://www.mapquestapi.com/geocoding/v1/';
59
60
    const GEOCODE_ENDPOINT = 'address';
61
62
    const DEFAULT_GEOCODE_PARAMS = [
63
        self::KEY_LOCATION => '',
64
        self::KEY_OUT_FORMAT => 'json',
65
        self::KEY_API_KEY => '',
66
    ];
67
68
    const DEFAULT_GEOCODE_OPTIONS = [
69
        self::KEY_MAX_RESULTS => 3,
70
        self::KEY_THUMB_MAPS => false,
71
    ];
72
73
    const REVERSE_ENDPOINT = 'reverse';
74
75
    const ADMIN_LEVEL_STATE = 1;
76
77
    const ADMIN_LEVEL_COUNTY = 2;
78
79
    /**
80
     * MapQuest offers two geocoding endpoints one commercial (true) and one open (false)
81
     * More information: http://developer.mapquest.com/web/tools/getting-started/platform/licensed-vs-open.
82
     *
83
     * @var bool
84
     */
85
    private $licensed;
86
87
    /**
88
     * @var bool
89
     */
90
    private $useRoadPosition;
91
92
    /**
93
     * @var string
94
     */
95
    private $apiKey;
96
97
    /**
98
     * @param HttpClient $client          an HTTP adapter
99
     * @param string     $apiKey          an API key
100
     * @param bool       $licensed        true to use MapQuest's licensed endpoints, default is false to use the open endpoints (optional)
101
     * @param bool       $useRoadPosition true to use nearest point on a road for the entrance, false to use map display position
102
     */
103
    public function __construct(HttpClient $client, string $apiKey, bool $licensed = false, bool $useRoadPosition = false)
104
    {
105
        if (empty($apiKey)) {
106
            throw new InvalidCredentials('No API key provided.');
107
        }
108
109
        $this->apiKey = $apiKey;
110
        $this->licensed = $licensed;
111
        $this->useRoadPosition = $useRoadPosition;
112
        parent::__construct($client);
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118
    public function geocodeQuery(CommonGeocodeQuery $query): Collection
119
    {
120
        $params = static::DEFAULT_GEOCODE_PARAMS;
121
        $params[static::KEY_API_KEY] = $this->apiKey;
122
123
        $options = static::DEFAULT_GEOCODE_OPTIONS;
124
        $options[static::KEY_MAX_RESULTS] = $query->getLimit();
125
126
        $useGetQuery = true;
127
128
        $address = $this->extractAddressFromQuery($query);
129
        if ($address instanceof Location) {
130
            $params[static::KEY_LOCATION] = $this->mapAddressToArray($address);
131
            $options[static::KEY_INTL_MODE] = static::MODE_5BOX;
132
            $useGetQuery = false;
133
        } else {
134
            $addressAsText = $query->getText();
135
136
            if (!$addressAsText) {
137
                throw new InvalidArgument('Cannot geocode empty address');
138
            }
139
140
            // This API doesn't handle IPs
141
            if (filter_var($addressAsText, FILTER_VALIDATE_IP)) {
142
                throw new UnsupportedOperation('The MapQuest provider does not support IP addresses, only street addresses.');
143
            }
144
145
            $params[static::KEY_LOCATION] = $addressAsText;
146
        }
147
148
        $bounds = $query->getBounds();
149
        if ($bounds instanceof Bounds) {
150
            $options[static::KEY_BOUNDING_BOX] = $this->mapBoundsToArray($bounds);
151
            $useGetQuery = false;
152
        }
153
154
        if ($useGetQuery) {
155
            $params = $this->addOptionsForGetQuery($params, $options);
156
157
            return $this->executeGetQuery(static::GEOCODE_ENDPOINT, $params);
158
        } else {
159
            $params = $this->addOptionsForPostQuery($params, $options);
160
161
            return $this->executePostQuery(static::GEOCODE_ENDPOINT, $params);
162
        }
163
    }
164
165
    private function extractAddressFromQuery(CommonGeocodeQuery $query)
166
    {
167
        if ($query instanceof GetAddressInterface) {
168
            return $query->getAddress();
169
        }
170
171
        return $query->getData(GeocodeQuery::DATA_KEY_ADDRESS);
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    public function reverseQuery(ReverseQuery $query): Collection
178
    {
179
        $coordinates = $query->getCoordinates();
180
        $longitude = $coordinates->getLongitude();
181
        $latitude = $coordinates->getLatitude();
182
183
        $params = [
184
            static::KEY_API_KEY => $this->apiKey,
185
            static::KEY_LAT => $latitude,
186
            static::KEY_LNG => $longitude,
187
        ];
188
189
        return $this->executeGetQuery(static::REVERSE_ENDPOINT, $params);
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195
    public function getName(): string
196
    {
197
        return 'map_quest';
198
    }
199
200
    private function getUrl($endpoint): string
201
    {
202
        if ($this->licensed) {
203
            $baseUrl = static::LICENSED_BASE_URL;
204
        } else {
205
            $baseUrl = static::OPEN_BASE_URL;
206
        }
207
208
        return $baseUrl.$endpoint;
209
    }
210
211
    private function addGetQuery(string $url, array $params): string
212
    {
213
        return $url.'?'.http_build_query($params, '', '&', PHP_QUERY_RFC3986);
214
    }
215
216
    private function addOptionsForGetQuery(array $params, array $options): array
217
    {
218
        foreach ($options as $key => $value) {
219
            if (false === $value) {
220
                $value = 'false';
221
            } elseif (true === $value) {
222
                $value = 'true';
223
            }
224
            $params[$key] = $value;
225
        }
226
227
        return $params;
228
    }
229
230
    private function addOptionsForPostQuery(array $params, array $options): array
231
    {
232
        $params['options'] = $options;
233
234
        return $params;
235
    }
236
237
    private function executePostQuery(string $endpoint, array $params)
238
    {
239
        $url = $this->getUrl($endpoint);
240
241
        $appKey = $params[static::KEY_API_KEY];
242
        unset($params[static::KEY_API_KEY]);
243
        $url .= '?key='.$appKey;
244
245
        $requestBody = json_encode($params);
246
        $request = $this->getMessageFactory()->createRequest('POST', $url, [], $requestBody);
247
248
        $response = $this->getHttpClient()->sendRequest($request);
249
        $content = $this->parseHttpResponse($response, $url);
250
251
        return $this->parseResponseContent($content);
252
    }
253
254
    /**
255
     * @param string $url
0 ignored issues
show
Bug introduced by
There is no parameter named $url. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
256
     *
257
     * @return AddressCollection
258
     */
259
    private function executeGetQuery(string $endpoint, array $params): AddressCollection
260
    {
261
        $baseUrl = $this->getUrl($endpoint);
262
        $url = $this->addGetQuery($baseUrl, $params);
263
264
        $content = $this->getUrlContents($url);
265
266
        return $this->parseResponseContent($content);
267
    }
268
269
    private function parseResponseContent(string $content): AddressCollection
270
    {
271
        $json = json_decode($content, true);
272
273
        if (!isset($json['results']) || empty($json['results'])) {
274
            return new AddressCollection([]);
275
        }
276
277
        $locations = $json['results'][0]['locations'];
278
279
        if (empty($locations)) {
280
            return new AddressCollection([]);
281
        }
282
283
        $results = [];
284
        foreach ($locations as $location) {
285
            if ($location['street'] || $location['postalCode'] || $location['adminArea5'] || $location['adminArea4'] || $location['adminArea3']) {
286
                $admins = [];
287
288
                $state = $location['adminArea3'];
289
                if ($state) {
290
                    $code = null;
291
                    if (2 == strlen($state)) {
292
                        $code = $state;
293
                    }
294
                    $admins[] = [
295
                        'name' => $state,
296
                        'code' => $code,
297
                        'level' => static::ADMIN_LEVEL_STATE,
298
                    ];
299
                }
300
301
                if ($location['adminArea4']) {
302
                    $admins[] = ['name' => $location['adminArea4'], 'level' => static::ADMIN_LEVEL_COUNTY];
303
                }
304
305
                $position = $location['latLng'];
306
                if (!$this->useRoadPosition) {
307
                    if ($location['displayLatLng']) {
308
                        $position = $location['displayLatLng'];
309
                    }
310
                }
311
312
                $results[] = Address::createFromArray([
313
                    'providedBy' => $this->getName(),
314
                    'latitude' => $position['lat'],
315
                    'longitude' => $position['lng'],
316
                    'streetName' => $location['street'] ?: null,
317
                    'locality' => $location['adminArea5'] ?: null,
318
                    'subLocality' => $location['adminArea6'] ?: null,
319
                    'postalCode' => $location['postalCode'] ?: null,
320
                    'adminLevels' => $admins,
321
                    'country' => $location['adminArea1'] ?: null,
322
                    'countryCode' => $location['adminArea1'] ?: null,
323
                ]);
324
            }
325
        }
326
327
        return new AddressCollection($results);
328
    }
329
330
    private function mapAddressToArray(Location $address): array
331
    {
332
        $location = [];
333
334
        $streetParts = [
335
            trim($address->getStreetNumber() ?: ''),
336
            trim($address->getStreetName() ?: ''),
337
        ];
338
        $street = implode(' ', array_filter($streetParts));
339
        if ($street) {
340
            $location['street'] = $street;
341
        }
342
343
        if ($address->getSubLocality()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $address->getSubLocality() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
344
            $location['adminArea6'] = $address->getSubLocality();
345
            $location['adminArea6Type'] = 'Neighborhood';
346
        }
347
348
        if ($address->getLocality()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $address->getLocality() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
349
            $location['adminArea5'] = $address->getLocality();
350
            $location['adminArea5Type'] = 'City';
351
        }
352
353
        /** @var AdminLevel $adminLevel */
354
        foreach ($address->getAdminLevels() as $adminLevel) {
355
            switch ($adminLevel->getLevel()) {
356
                case static::ADMIN_LEVEL_STATE:
357
                    $state = $adminLevel->getCode();
358
                    if (!$state) {
359
                        $state = $adminLevel->getName();
360
                    }
361
                    $location['adminArea3'] = $state;
362
                    $location['adminArea3Type'] = 'State';
363
364
                    break;
365
                case static::ADMIN_LEVEL_COUNTY:
366
                    $county = $adminLevel->getName();
367
                    $location['adminArea4'] = $county;
368
                    $location['adminArea4Type'] = 'County';
369
            }
370
        }
371
372
        $country = $address->getCountry();
373
        if ($country instanceof Country) {
374
            $code = $country->getCode();
375
            if (!$code) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $code of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
376
                $code = $country->getName();
377
            }
378
            $location['adminArea1'] = $code;
379
            $location['adminArea1Type'] = 'Country';
380
        }
381
382
        $postalCode = $address->getPostalCode();
383
        if ($postalCode) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $postalCode of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
384
            $location['postalCode'] = $address->getPostalCode();
385
        }
386
387
        return $location;
388
    }
389
390
    private function mapBoundsToArray(Bounds $bounds)
391
    {
392
        return [
393
            'ul' => [static::KEY_LAT => $bounds->getNorth(), static::KEY_LNG => $bounds->getWest()],
394
            'lr' => [static::KEY_LAT => $bounds->getSouth(), static::KEY_LNG => $bounds->getEast()],
395
        ];
396
    }
397
}
398