Completed
Push — master ( 01968e...0ce741 )
by Tobias
06:24
created

Geonames   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 204
Duplicated Lines 3.92 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 95.56%

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 10
dl 8
loc 204
ccs 86
cts 90
cp 0.9556
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A geocodeQuery() 0 13 2
A reverseQuery() 0 10 1
B getCountryInfo() 4 54 7
A getName() 0 4 1
B executeQuery() 4 59 10

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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\Geonames;
14
15
use Geocoder\Collection;
16
use Geocoder\Exception\InvalidCredentials;
17
use Geocoder\Exception\InvalidServerResponse;
18
use Geocoder\Exception\UnsupportedOperation;
19
use Geocoder\Model\AddressBuilder;
20
use Geocoder\Model\AddressCollection;
21
use Geocoder\Model\AdminLevelCollection;
22
use Geocoder\Provider\Geonames\Model\CountryInfo;
23
use Geocoder\Provider\Geonames\Model\GeonamesAddress;
24
use Geocoder\Query\GeocodeQuery;
25
use Geocoder\Query\ReverseQuery;
26
use Geocoder\Http\Provider\AbstractHttpProvider;
27
use Geocoder\Provider\Provider;
28
use Http\Client\HttpClient;
29
30
/**
31
 * @author Giovanni Pirrotta <[email protected]>
32
 */
33
final class Geonames extends AbstractHttpProvider implements Provider
34
{
35
    /**
36
     * @var string
37
     */
38
    const GEOCODE_ENDPOINT_URL = 'http://api.geonames.org/searchJSON?q=%s&maxRows=%d&style=full&username=%s';
39
40
    /**
41
     * @var string
42
     */
43
    const REVERSE_ENDPOINT_URL = 'http://api.geonames.org/findNearbyPlaceNameJSON?lat=%F&lng=%F&style=full&maxRows=%d&username=%s';
44
45
    /**
46
     * @var string
47
     */
48
    const BASE_ENDPOINT_URL = 'http://api.geonames.org/%s?username=%s';
49
50
    /**
51
     * @var string
52
     */
53
    private $username;
54
55
    /**
56
     * @param HttpClient $client   An HTTP adapter
57
     * @param string     $username Username login (Free registration at http://www.geonames.org/login)
58
     */
59 28
    public function __construct(HttpClient $client, string $username)
60
    {
61 28
        if (empty($username)) {
62
            throw new InvalidCredentials('No username provided.');
63
        }
64
65 28
        $this->username = $username;
66 28
        parent::__construct($client);
67 28
    }
68
69
    /**
70
     * {@inheritdoc}
71
     */
72 12
    public function geocodeQuery(GeocodeQuery $query): Collection
73
    {
74 12
        $address = $query->getText();
75
76
        // This API doesn't handle IPs
77 12
        if (filter_var($address, FILTER_VALIDATE_IP)) {
78 2
            throw new UnsupportedOperation('The Geonames provider does not support IP addresses.');
79
        }
80
81 10
        $url = sprintf(self::GEOCODE_ENDPOINT_URL, urlencode($address), $query->getLimit(), $this->username);
82
83 10
        return $this->executeQuery($url, $query->getLocale());
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89 10
    public function reverseQuery(ReverseQuery $query): Collection
90
    {
91 10
        $coordinates = $query->getCoordinates();
92 10
        $longitude = $coordinates->getLongitude();
93 10
        $latitude = $coordinates->getLatitude();
94
95 10
        $url = sprintf(self::REVERSE_ENDPOINT_URL, $latitude, $longitude, $query->getLimit(), $this->username);
96
97 10
        return $this->executeQuery($url, $query->getLocale());
98
    }
99
100
    /**
101
     * @param string|null $country
102
     * @param string|null $locale
103
     *
104
     * @return array
105
     *
106
     * @throws \Geocoder\Exception\Exception
107
     */
108 5
    public function getCountryInfo(string $country = null, string $locale = null): array
109
    {
110 5
        $url = sprintf(self::BASE_ENDPOINT_URL, 'countryInfoJSON', $this->username);
111
112 5
        if (isset($country)) {
113 4
            $url = sprintf('%s&country=%s', $url, $country);
114
        }
115
116 5
        $url = sprintf('%s&style=FULL', $url);
117
118 5 View Code Duplication
        if (null !== $locale) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
119
            // Locale code transformation: for example from it_IT to it
120 1
            $url = sprintf('%s&lang=%s', $url, substr($locale, 0, 2));
121
        }
122
123 5
        $content = $this->getUrlContents($url);
124 5
        if (null === $json = json_decode($content)) {
125
            throw InvalidServerResponse::create($url);
126
        }
127
128 5
        if (!isset($json->geonames)) {
129
            return [];
130
        }
131
132 5
        $data = $json->geonames;
133
134 5
        if (empty($data)) {
135 1
            return [];
136
        }
137
138 4
        $results = [];
139
140 4
        foreach ($data as $item) {
141 4
            $countryInfo = new CountryInfo();
142
143 4
            $results[] = $countryInfo
144 4
                ->setBounds($item->south, $item->west, $item->north, $item->east)
145 4
                ->withContinent($item->continent ?? null)
146 4
                ->withCapital($item->capital ?? null)
147 4
                ->withLanguages($item->langesuages ?? '')
148 4
                ->withGeonameId($item->geonameId ?? null)
149 4
                ->withIsoAlpha3($item->isoAlpha3 ?? null)
150 4
                ->withFipsCode($item->fipsCode ?? null)
151 4
                ->withPopulation($item->population ?? null)
152 4
                ->withIsoNumeric($item->isoNumeric ?? null)
153 4
                ->withAreaInSqKm($item->areaInSqKm ?? null)
154 4
                ->withCountryCode($item->countryCode ?? null)
155 4
                ->withCountryName($item->countryName ?? null)
156 4
                ->withContinentName($item->continentName ?? null)
157 4
                ->withCurrencyCode($item->currencyCode ?? null);
158
        }
159
160 4
        return $results;
161
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166 7
    public function getName(): string
167
    {
168 7
        return 'geonames';
169
    }
170
171
    /**
172
     * @param string      $url
173
     * @param string|null $locale
174
     *
175
     * @return AddressCollection
176
     */
177 20
    private function executeQuery(string $url, string $locale = null): AddressCollection
178
    {
179 20 View Code Duplication
        if (null !== $locale) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
180
            // Locale code transformation: for example from it_IT to it
181 4
            $url = sprintf('%s&lang=%s', $url, substr($locale, 0, 2));
182
        }
183
184 20
        $content = $this->getUrlContents($url);
185 10
        if (null === $json = json_decode($content)) {
186
            throw InvalidServerResponse::create($url);
187
        }
188
189 10
        if (isset($json->totalResultsCount) && empty($json->totalResultsCount)) {
190 2
            return new AddressCollection([]);
191
        }
192
193 8
        $data = $json->geonames;
194
195 8
        if (empty($data)) {
196 2
            return new AddressCollection([]);
197
        }
198
199 6
        $results = [];
200 6
        foreach ($data as $item) {
201 6
            $builder = new AddressBuilder($this->getName());
202
203 6
            if (isset($item->bbox)) {
204 2
                $builder->setBounds($item->bbox->south, $item->bbox->west, $item->bbox->north, $item->bbox->east);
205
            }
206
207 6
            for ($level = 1; $level <= AdminLevelCollection::MAX_LEVEL_DEPTH; ++$level) {
208 6
                $adminNameProp = 'adminName'.$level;
209 6
                $adminCodeProp = 'adminCode'.$level;
210 6
                if (!empty($item->$adminNameProp)) {
211 6
                    $builder->addAdminLevel($level, $item->$adminNameProp, $item->$adminCodeProp ?? null);
212
                }
213
            }
214
215 6
            $builder->setCoordinates($item->lat ?? null, $item->lng ?? null);
216 6
            $builder->setLocality($item->name ?? null);
217 6
            $builder->setCountry($item->countryName ?? null);
218 6
            $builder->setCountryCode($item->countryCode ?? null);
219 6
            $builder->setTimezone($item->timezone->timeZoneId ?? null);
220
221
            /** @var GeonamesAddress $address */
222 6
            $address = $builder->build(GeonamesAddress::class);
223 6
            $address = $address->withName($item->name ?? null);
224 6
            $address = $address->withAsciiName($item->asciiName ?? null);
225 6
            $address = $address->withFclName($item->fclName ?? null);
226 6
            $address = $address->withAlternateNames($item->alternateNames ?? []);
227 6
            $address = $address->withPopulation($item->population ?? null);
228 6
            $address = $address->withGeonameId($item->geonameId ?? null);
229 6
            $address = $address->withFcode($item->fcode ?? null);
230
231 6
            $results[] = $address;
232
        }
233
234 6
        return new AddressCollection($results);
235
    }
236
}
237