3232 */
3333final class Here extends AbstractHttpProvider implements Provider
3434{
35+ /**
36+ * @var string
37+ */
38+ public const GS7_GEOCODE_ENDPOINT_URL = 'https://geocode.search.hereapi.com/v1/geocode ' ;
39+
40+ /**
41+ * @var string
42+ */
43+ public const GS7_REVERSE_ENDPOINT_URL = 'https://revgeocode.search.hereapi.com/v1/revgeocode ' ;
44+
3545 /**
3646 * @var string
3747 */
@@ -96,43 +106,50 @@ final class Here extends AbstractHttpProvider implements Provider
96106 ];
97107
98108 /**
99- * @var string
109+ * @var string|null
100110 */
101- private $ appId ;
111+ private ? string $ appId ;
102112
103113 /**
104- * @var string
114+ * @var string|null
105115 */
106- private $ appCode ;
116+ private ? string $ appCode ;
107117
108118 /**
109119 * @var bool
110120 */
111- private $ useCIT ;
121+ private bool $ useCIT ;
122+
123+ /**
124+ * @var string|null
125+ */
126+ private ?string $ apiKey = null ;
112127
113128 /**
114129 * @var string
115130 */
116- private $ apiKey ;
131+ private string $ version ;
117132
118133 /**
119134 * @param ClientInterface $client an HTTP adapter
120- * @param string $appId an App ID
121- * @param string $appCode an App code
135+ * @param string|null $appId an App ID
136+ * @param string|null $appCode an App code
122137 * @param bool $useCIT use Customer Integration Testing environment (CIT) instead of production
138+ * @param string $version version of the API to use ('6.2' or '7')
123139 */
124- public function __construct (ClientInterface $ client , ?string $ appId = null , ?string $ appCode = null , bool $ useCIT = false )
140+ public function __construct (ClientInterface $ client , ?string $ appId = null , ?string $ appCode = null , bool $ useCIT = false , string $ version = ' 6.2 ' )
125141 {
126142 $ this ->appId = $ appId ;
127143 $ this ->appCode = $ appCode ;
128144 $ this ->useCIT = $ useCIT ;
145+ $ this ->version = $ version ;
129146
130147 parent ::__construct ($ client );
131148 }
132149
133- public static function createUsingApiKey (ClientInterface $ client , string $ apiKey , bool $ useCIT = false ): self
150+ public static function createUsingApiKey (ClientInterface $ client , string $ apiKey , bool $ useCIT = false , string $ version = ' 7 ' ): self
134151 {
135- $ client = new self ($ client , null , null , $ useCIT );
152+ $ client = new self ($ client , null , null , $ useCIT, $ version );
136153 $ client ->apiKey = $ apiKey ;
137154
138155 return $ client ;
@@ -145,6 +162,10 @@ public function geocodeQuery(GeocodeQuery $query): Collection
145162 throw new UnsupportedOperation ('The Here provider does not support IP addresses, only street addresses. ' );
146163 }
147164
165+ if ('7 ' === $ this ->version && null !== $ this ->apiKey ) {
166+ return $ this ->geocodeQueryGS7 ($ query );
167+ }
168+
148169 $ queryParams = $ this ->withApiCredentials ([
149170 'searchtext ' => $ query ->getText (),
150171 'gen ' => 9 ,
@@ -174,8 +195,44 @@ public function geocodeQuery(GeocodeQuery $query): Collection
174195 return $ this ->executeQuery (sprintf ('%s?%s ' , $ this ->getBaseUrl ($ query ), http_build_query ($ queryParams )), $ query ->getLimit ());
175196 }
176197
198+ private function geocodeQueryGS7 (GeocodeQuery $ query ): Collection
199+ {
200+ $ queryParams = $ this ->withApiCredentials ([
201+ 'q ' => $ query ->getText (),
202+ 'limit ' => $ query ->getLimit (),
203+ ]);
204+
205+ $ qq = [];
206+ if (null !== $ country = $ query ->getData ('country ' )) {
207+ $ qq [] = 'country= ' .$ country ;
208+ }
209+ if (null !== $ state = $ query ->getData ('state ' )) {
210+ $ qq [] = 'state= ' .$ state ;
211+ }
212+ if (null !== $ county = $ query ->getData ('county ' )) {
213+ $ qq [] = 'county= ' .$ county ;
214+ }
215+ if (null !== $ city = $ query ->getData ('city ' )) {
216+ $ qq [] = 'city= ' .$ city ;
217+ }
218+
219+ if (!empty ($ qq )) {
220+ $ queryParams ['qq ' ] = implode ('; ' , $ qq );
221+ }
222+
223+ if (null !== $ query ->getLocale ()) {
224+ $ queryParams ['lang ' ] = $ query ->getLocale ();
225+ }
226+
227+ return $ this ->executeQuery (sprintf ('%s?%s ' , $ this ->getBaseUrl ($ query ), http_build_query ($ queryParams )), $ query ->getLimit ());
228+ }
229+
177230 public function reverseQuery (ReverseQuery $ query ): Collection
178231 {
232+ if ('7 ' === $ this ->version && null !== $ this ->apiKey ) {
233+ return $ this ->reverseQueryGS7 ($ query );
234+ }
235+
179236 $ coordinates = $ query ->getCoordinates ();
180237
181238 $ queryParams = $ this ->withApiCredentials ([
@@ -188,12 +245,34 @@ public function reverseQuery(ReverseQuery $query): Collection
188245 return $ this ->executeQuery (sprintf ('%s?%s ' , $ this ->getBaseUrl ($ query ), http_build_query ($ queryParams )), $ query ->getLimit ());
189246 }
190247
248+ private function reverseQueryGS7 (ReverseQuery $ query ): Collection
249+ {
250+ $ coordinates = $ query ->getCoordinates ();
251+
252+ $ queryParams = $ this ->withApiCredentials ([
253+ 'at ' => sprintf ('%s,%s ' , $ coordinates ->getLatitude (), $ coordinates ->getLongitude ()),
254+ 'limit ' => $ query ->getLimit (),
255+ ]);
256+
257+ if (null !== $ query ->getLocale ()) {
258+ $ queryParams ['lang ' ] = $ query ->getLocale ();
259+ }
260+
261+ return $ this ->executeQuery (sprintf ('%s?%s ' , $ this ->getBaseUrl ($ query ), http_build_query ($ queryParams )), $ query ->getLimit ());
262+ }
263+
191264 private function executeQuery (string $ url , int $ limit ): Collection
192265 {
193266 $ content = $ this ->getUrlContents ($ url );
194267
195268 $ json = json_decode ($ content , true );
196269
270+ if (isset ($ json ['error ' ])) {
271+ if ('Unauthorized ' === $ json ['error ' ]) {
272+ throw new InvalidCredentials ('Invalid or missing api key. ' );
273+ }
274+ }
275+
197276 if (isset ($ json ['type ' ])) {
198277 switch ($ json ['type ' ]['subtype ' ]) {
199278 case 'InvalidInputData ' :
@@ -205,6 +284,10 @@ private function executeQuery(string $url, int $limit): Collection
205284 }
206285 }
207286
287+ if (isset ($ json ['items ' ])) {
288+ return $ this ->parseGS7Response ($ json ['items ' ], $ limit );
289+ }
290+
208291 if (!isset ($ json ['Response ' ]) || empty ($ json ['Response ' ])) {
209292 return new AddressCollection ([]);
210293 }
@@ -215,6 +298,63 @@ private function executeQuery(string $url, int $limit): Collection
215298
216299 $ locations = $ json ['Response ' ]['View ' ][0 ]['Result ' ];
217300
301+ return $ this ->parseV6Response ($ locations , $ limit );
302+ }
303+
304+ private function parseGS7Response (array $ items , int $ limit ): Collection
305+ {
306+ $ results = [];
307+
308+ foreach ($ items as $ item ) {
309+ $ builder = new AddressBuilder ($ this ->getName ());
310+
311+ $ position = $ item ['position ' ];
312+ $ builder ->setCoordinates ($ position ['lat ' ], $ position ['lng ' ]);
313+
314+ if (isset ($ item ['mapView ' ])) {
315+ $ mapView = $ item ['mapView ' ];
316+ $ builder ->setBounds ($ mapView ['south ' ], $ mapView ['west ' ], $ mapView ['north ' ], $ mapView ['east ' ]);
317+ }
318+
319+ $ address = $ item ['address ' ];
320+ $ builder ->setStreetNumber ($ address ['houseNumber ' ] ?? null );
321+ $ builder ->setStreetName ($ address ['street ' ] ?? null );
322+ $ builder ->setPostalCode ($ address ['postalCode ' ] ?? null );
323+ $ builder ->setLocality ($ address ['city ' ] ?? null );
324+ $ builder ->setSubLocality ($ address ['district ' ] ?? null );
325+ $ builder ->setCountryCode ($ address ['countryCode ' ] ?? null );
326+ $ builder ->setCountry ($ address ['countryName ' ] ?? null );
327+
328+ /** @var HereAddress $hereAddress */
329+ $ hereAddress = $ builder ->build (HereAddress::class);
330+ $ hereAddress = $ hereAddress ->withLocationId ($ item ['id ' ] ?? null );
331+ $ hereAddress = $ hereAddress ->withLocationType ($ item ['resultType ' ] ?? null );
332+ $ hereAddress = $ hereAddress ->withLocationName ($ item ['title ' ] ?? null );
333+
334+ $ additionalData = [];
335+ if (isset ($ address ['countryName ' ])) {
336+ $ additionalData [] = ['key ' => 'CountryName ' , 'value ' => $ address ['countryName ' ]];
337+ }
338+ if (isset ($ address ['state ' ])) {
339+ $ additionalData [] = ['key ' => 'StateName ' , 'value ' => $ address ['state ' ]];
340+ }
341+ if (isset ($ address ['county ' ])) {
342+ $ additionalData [] = ['key ' => 'CountyName ' , 'value ' => $ address ['county ' ]];
343+ }
344+
345+ $ hereAddress = $ hereAddress ->withAdditionalData ($ additionalData );
346+ $ results [] = $ hereAddress ;
347+
348+ if (count ($ results ) >= $ limit ) {
349+ break ;
350+ }
351+ }
352+
353+ return new AddressCollection ($ results );
354+ }
355+
356+ private function parseV6Response (array $ locations , int $ limit ): Collection
357+ {
218358 $ results = [];
219359
220360 foreach ($ locations as $ loc ) {
@@ -245,7 +385,7 @@ private function executeQuery(string $url, int $limit): Collection
245385 $ address = $ builder ->build (HereAddress::class);
246386 $ address = $ address ->withLocationId ($ location ['LocationId ' ] ?? null );
247387 $ address = $ address ->withLocationType ($ location ['LocationType ' ]);
248- $ address = $ address ->withAdditionalData (array_merge ($ additionalData , $ extraAdditionalData ));
388+ $ address = $ address ->withAdditionalData (array_merge ($ additionalData ?? [] , $ extraAdditionalData ));
249389 $ address = $ address ->withShape ($ location ['Shape ' ] ?? null );
250390 $ results [] = $ address ;
251391
@@ -289,18 +429,13 @@ private function getAdditionalDataParam(GeocodeQuery $query): string
289429 */
290430 private function withApiCredentials (array $ queryParams ): array
291431 {
292- if (
293- empty ($ this ->apiKey )
294- && (empty ($ this ->appId ) || empty ($ this ->appCode ))
295- ) {
296- throw new InvalidCredentials ('Invalid or missing api key. ' );
297- }
298-
299432 if (null !== $ this ->apiKey ) {
300433 $ queryParams ['apiKey ' ] = $ this ->apiKey ;
301- } else {
434+ } elseif (! empty ( $ this -> appId ) && ! empty ( $ this -> appCode )) {
302435 $ queryParams ['app_id ' ] = $ this ->appId ;
303436 $ queryParams ['app_code ' ] = $ this ->appCode ;
437+ } else {
438+ throw new InvalidCredentials ('Invalid or missing api key. ' );
304439 }
305440
306441 return $ queryParams ;
@@ -310,6 +445,10 @@ public function getBaseUrl(Query $query): string
310445 {
311446 $ usingApiKey = null !== $ this ->apiKey ;
312447
448+ if ('7 ' === $ this ->version && $ usingApiKey ) {
449+ return ($ query instanceof ReverseQuery) ? self ::GS7_REVERSE_ENDPOINT_URL : self ::GS7_GEOCODE_ENDPOINT_URL ;
450+ }
451+
313452 if ($ query instanceof ReverseQuery) {
314453 if ($ this ->useCIT ) {
315454 return $ usingApiKey ? self ::REVERSE_CIT_ENDPOINT_URL_API_KEY : self ::REVERSE_CIT_ENDPOINT_URL_APP_CODE ;
0 commit comments