These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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
|
|||
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 | $data = $json->geonames; |
|
129 | |||
130 | 5 | if (empty($data)) { |
|
131 | 1 | return []; |
|
132 | } |
||
133 | |||
134 | 4 | $results = []; |
|
135 | |||
136 | 4 | foreach ($data as $item) { |
|
137 | 4 | $countryInfo = new CountryInfo(); |
|
138 | |||
139 | 4 | $results[] = $countryInfo |
|
140 | 4 | ->setBounds($item->south, $item->west, $item->north, $item->east) |
|
141 | 4 | ->withContinent($item->continent ?? null) |
|
142 | 4 | ->withCapital($item->capital ?? null) |
|
143 | 4 | ->withLanguages($item->langesuages ?? '') |
|
144 | 4 | ->withGeonameId($item->geonameId ?? null) |
|
145 | 4 | ->withIsoAlpha3($item->isoAlpha3 ?? null) |
|
146 | 4 | ->withFipsCode($item->fipsCode ?? null) |
|
147 | 4 | ->withPopulation($item->population ?? null) |
|
148 | 4 | ->withIsoNumeric($item->isoNumeric ?? null) |
|
149 | 4 | ->withAreaInSqKm($item->areaInSqKm ?? null) |
|
150 | 4 | ->withCountryCode($item->countryCode ?? null) |
|
151 | 4 | ->withCountryName($item->countryName ?? null) |
|
152 | 4 | ->withContinentName($item->continentName ?? null) |
|
153 | 4 | ->withCurrencyCode($item->currencyCode ?? null); |
|
154 | } |
||
155 | |||
156 | 4 | return $results; |
|
157 | } |
||
158 | |||
159 | /** |
||
160 | * {@inheritdoc} |
||
161 | */ |
||
162 | 7 | public function getName(): string |
|
163 | { |
||
164 | 7 | return 'geonames'; |
|
165 | } |
||
166 | |||
167 | /** |
||
168 | * @param string $url |
||
169 | * @param string|null $locale |
||
170 | * |
||
171 | * @return AddressCollection |
||
172 | */ |
||
173 | 20 | private function executeQuery(string $url, string $locale = null): AddressCollection |
|
174 | { |
||
175 | 20 | View Code Duplication | if (null !== $locale) { |
0 ignored issues
–
show
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. ![]() |
|||
176 | // Locale code transformation: for example from it_IT to it |
||
177 | 4 | $url = sprintf('%s&lang=%s', $url, substr($locale, 0, 2)); |
|
178 | } |
||
179 | |||
180 | 20 | $content = $this->getUrlContents($url); |
|
181 | 10 | if (null === $json = json_decode($content)) { |
|
182 | throw InvalidServerResponse::create($url); |
||
183 | } |
||
184 | |||
185 | 10 | if (isset($json->totalResultsCount) && empty($json->totalResultsCount)) { |
|
186 | 2 | return new AddressCollection([]); |
|
187 | } |
||
188 | |||
189 | 8 | $data = $json->geonames; |
|
190 | |||
191 | 8 | if (empty($data)) { |
|
192 | 2 | return new AddressCollection([]); |
|
193 | } |
||
194 | |||
195 | 6 | $results = []; |
|
196 | 6 | foreach ($data as $item) { |
|
197 | 6 | $builder = new AddressBuilder($this->getName()); |
|
198 | |||
199 | 6 | if (isset($item->bbox)) { |
|
200 | 2 | $builder->setBounds($item->bbox->south, $item->bbox->west, $item->bbox->north, $item->bbox->east); |
|
201 | } |
||
202 | |||
203 | 6 | for ($level = 1; $level <= AdminLevelCollection::MAX_LEVEL_DEPTH; ++$level) { |
|
204 | 6 | $adminNameProp = 'adminName'.$level; |
|
205 | 6 | $adminCodeProp = 'adminCode'.$level; |
|
206 | 6 | if (!empty($item->$adminNameProp)) { |
|
207 | 6 | $builder->addAdminLevel($level, $item->$adminNameProp, $item->$adminCodeProp ?? null); |
|
208 | } |
||
209 | } |
||
210 | |||
211 | 6 | $builder->setCoordinates($item->lat ?? null, $item->lng ?? null); |
|
212 | 6 | $builder->setLocality($item->name ?? null); |
|
213 | 6 | $builder->setCountry($item->countryName ?? null); |
|
214 | 6 | $builder->setCountryCode($item->countryCode ?? null); |
|
215 | 6 | $builder->setTimezone($item->timezone->timeZoneId ?? null); |
|
216 | |||
217 | /** @var GeonamesAddress $address */ |
||
218 | 6 | $address = $builder->build(GeonamesAddress::class); |
|
219 | 6 | $address = $address->withName($item->name ?? null); |
|
220 | 6 | $address = $address->withAsciiName($item->asciiName ?? null); |
|
221 | 6 | $address = $address->withFclName($item->fclName ?? null); |
|
222 | 6 | $address = $address->withAlternateNames($item->alternateNames ?? []); |
|
223 | 6 | $address = $address->withPopulation($item->population ?? null); |
|
224 | 6 | $address = $address->withGeonameId($item->geonameId ?? null); |
|
225 | 6 | $address = $address->withFcode($item->fcode ?? null); |
|
226 | |||
227 | 6 | $results[] = $address; |
|
228 | } |
||
229 | |||
230 | 6 | return new AddressCollection($results); |
|
231 | } |
||
232 | } |
||
233 |
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.