Code

< 40 %
40-60 %
> 60 %
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\Ipstack;
14
15
use Geocoder\Collection;
16
use Geocoder\Exception\InvalidArgument;
17
use Geocoder\Exception\InvalidCredentials;
18
use Geocoder\Exception\QuotaExceeded;
19
use Geocoder\Exception\UnsupportedOperation;
20
use Geocoder\Http\Provider\AbstractHttpProvider;
21
use Geocoder\Model\Address;
22
use Geocoder\Model\AddressCollection;
23
use Geocoder\Provider\Provider;
24
use Geocoder\Query\GeocodeQuery;
25
use Geocoder\Query\ReverseQuery;
26
use Psr\Http\Client\ClientInterface;
27
28
/**
29
 * @author Jonas Gielen <[email protected]>
30
 */
31
final class Ipstack extends AbstractHttpProvider implements Provider
32
{
33
    /**
34
     * @var string
35
     */
36
    public const GEOCODE_ENDPOINT_URL = 'http://api.ipstack.com/%s?access_key=%s';
37
38
    /**
39
     * @var string
40
     */
41
    private $apiKey;
42
43
    /**
44
     * @param ClientInterface $client an HTTP adapter
45
     * @param string          $apiKey an API key
46
     */
47 24
    public function __construct(ClientInterface $client, string $apiKey)
48
    {
49 24
        if (empty($apiKey)) {
50 1
            throw new InvalidCredentials('No API key provided.');
51
        }
52
53 23
        $this->apiKey = $apiKey;
54 23
        parent::__construct($client);
55
    }
56
57 21
    public function geocodeQuery(GeocodeQuery $query): Collection
58
    {
59 21
        $address = $query->getText();
60
61
        // This API doesn't handle IPs
62 21
        if (!filter_var($address, FILTER_VALIDATE_IP)) {
63 1
            throw new UnsupportedOperation('The Ipstack provider does not support street addresses.');
64
        }
65
66 20
        if (in_array($address, ['127.0.0.1', '::1'])) {
67 2
            return new AddressCollection([$this->getLocationForLocalhost()]);
68
        }
69
70 18
        $url = sprintf(sprintf(self::GEOCODE_ENDPOINT_URL, $address, $this->apiKey));
71
72 18
        if (null !== $query->getLocale()) {
73 3
            $url = sprintf('%s&language=%s', $url, $query->getLocale());
74
        }
75
76 18
        $body = $this->getUrlContents($url);
77 8
        $data = json_decode($body, true);
78
79
        // https://ipstack.com/documentation#errors
80 8
        if (isset($data['error'])) {
81 4
            switch ($data['error']['code']) {
82 4
                case 301:
83 1
                    throw new InvalidArgument('Invalid request (a required parameter is missing).');
84 3
                case 303:
85 1
                    throw new InvalidArgument('Bulk requests are not supported on your plan. Please upgrade your subscription.');
86 2
                case 104:
87 1
                    throw new QuotaExceeded('The maximum allowed amount of monthly API requests has been reached.');
88 1
                case 101:
89 1
                    throw new InvalidCredentials('No API Key was specified or an invalid API Key was specified.');
90
            }
91
        }
92
93 4
        if (null === $data['latitude']
94 4
            && null === $data['longitude']
95 4
            && null === $data['city']
96 4
            && null === $data['zip']
97 4
            && null === $data['country_name']
98 4
            && null === $data['country_code']) {
99 1
            return new AddressCollection([]);
100
        }
101
102 3
        $locations[] = Address::createFromArray([
103 3
            'providedBy' => $this->getName(),
104 3
            'latitude' => $data['latitude'] ?: null,
105 3
            'longitude' => $data['longitude'] ?: null,
106 3
            'locality' => $data['city'] ?: null,
107 3
            'postalCode' => $data['zip'] ?: null,
108 3
            'country' => $data['country_name'] ?: null,
109 3
            'countryCode' => $data['country_code'] ?: null,
110 3
        ]);
111
112 3
        return new AddressCollection($locations);
113
    }
114
115 1
    public function reverseQuery(ReverseQuery $query): Collection
116
    {
117 1
        throw new UnsupportedOperation('The Ipstack provider is not able to do reverse geocoding.');
118
    }
119
120 6
    public function getName(): string
121
    {
122 6
        return 'ipstack';
123
    }
124
}
125