Skip to content

Commit 625e555

Browse files
committed
new geocodeAsync method
1 parent 49e90ab commit 625e555

4 files changed

Lines changed: 88 additions & 14 deletions

File tree

README.md

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,8 @@ A [PHP](http://php.net/) library to use the [OpenCage geocoding API](https://ope
99
![Mastodon Follow](https://img.shields.io/mastodon/follow/109287663468501769?domain=https%3A%2F%2Fen.osm.town%2F&style=social)
1010

1111
## Overview
12-
This library attempts to use the [CURL](http://www.php.net/manual/en/book.curl.php)
13-
extension to access the OpenCage Geocoding API. If CURL support is not available, the
14-
library falls back to using [fopen wrappers](http://uk3.php.net/manual/en/filesystem.configuration.php#ini.allow-url-fopen).
15-
16-
To use the library you must either have the CURL extension compiled into your version
17-
of PHP. Alternatively configure the use of fopen wrappers via the `allow_url_fopen`
18-
directive in your `php.ini`.
12+
This library uses [Guzzle](https://docs.guzzlephp.org/en/stable/) to access the
13+
OpenCage Geocoding API. Both synchronous and asynchronous requests are supported.
1914

2015
### Authentication
2116

@@ -86,6 +81,28 @@ if ($result && $result['total_results'] > 0) {
8681
}
8782
```
8883

84+
### Async geocoding
85+
86+
```php
87+
$geocoder = new \OpenCage\Geocoder\Geocoder('YOUR-API-KEY');
88+
89+
// single async request
90+
$promise = $geocoder->geocodeAsync('82 Clerkenwell Road, London');
91+
$result = $promise->wait();
92+
print_r($result);
93+
94+
// concurrent requests
95+
$promises = [
96+
'london' => $geocoder->geocodeAsync('London'),
97+
'paris' => $geocoder->geocodeAsync('Paris'),
98+
'tokyo' => $geocoder->geocodeAsync('Tokyo'),
99+
];
100+
$results = \GuzzleHttp\Promise\Utils::unwrap($promises);
101+
print $results['london']['results'][0]['formatted'];
102+
print $results['paris']['results'][0]['formatted'];
103+
print $results['tokyo']['results'][0]['formatted'];
104+
```
105+
89106
### Set a proxy URL
90107

91108
```php

phpstan.neon

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,3 @@ parameters:
44
- src
55
- tests
66
- demo
7-
reportUnmatchedIgnoredErrors: false

src/Geocoder.php

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use GuzzleHttp\Client;
66
use GuzzleHttp\Exception\ConnectException;
7+
use GuzzleHttp\Promise\PromiseInterface;
78

89
class Geocoder
910
{
@@ -70,6 +71,43 @@ public function setHost(string $host): void
7071
* @return array{status: array{code: int, message: string}, results: list<mixed>, total_results: int, ...}
7172
*/
7273
public function geocode(string $query, array $optParams = []): array
74+
{
75+
$url = $this->buildUrl($query, $optParams);
76+
77+
$ret = json_decode($this->getJSON($url), true);
78+
if (!is_array($ret)) {
79+
throw new \Exception('Failed to decode API response');
80+
}
81+
/** @var array{status: array{code: int, message: string}, results: list<mixed>, total_results: int, ...} */
82+
return $ret;
83+
}
84+
85+
/** @param array<string, string> $optParams */
86+
public function geocodeAsync(string $query, array $optParams = []): PromiseInterface
87+
{
88+
$url = $this->buildUrl($query, $optParams);
89+
90+
if ($this->client === null) {
91+
$this->client = $this->buildClient();
92+
}
93+
94+
return $this->client->getAsync($url, ['http_errors' => false])
95+
->then(function (\Psr\Http\Message\ResponseInterface $response) {
96+
$ret = json_decode((string) $response->getBody(), true);
97+
if (!is_array($ret)) {
98+
throw new \Exception('Failed to decode API response');
99+
}
100+
return $ret;
101+
}, function (\Throwable $e) {
102+
if ($e instanceof ConnectException) {
103+
return json_decode($this->generateErrorJSON(498, 'network issue ' . $e->getMessage()), true);
104+
}
105+
throw $e;
106+
});
107+
}
108+
109+
/** @param array<string, string> $optParams */
110+
protected function buildUrl(string $query, array $optParams = []): string
73111
{
74112
$url = $this->url . 'q=' . urlencode($query);
75113

@@ -84,12 +122,7 @@ public function geocode(string $query, array $optParams = []): array
84122
}
85123
$url .= '&key=' . urlencode($this->key);
86124

87-
$ret = json_decode($this->getJSON($url), true);
88-
if (!is_array($ret)) {
89-
throw new \Exception('Failed to decode API response');
90-
}
91-
/** @var array{status: array{code: int, message: string}, results: list<mixed>, total_results: int, ...} */
92-
return $ret;
125+
return $url;
93126
}
94127

95128
protected function isValidHost(string $host): bool

tests/GeocoderTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,31 @@ public function testLondon(): void
7575
$this->assertEquals('OK', $result['status']['message']);
7676
}
7777

78+
public function testAsyncLondon(): void
79+
{
80+
// https://opencagedata.com/api#testingkeys
81+
$geocoder = new Geocoder('6d0e711d72d74daeb2b0bfd2a5cdfdba');
82+
$promise = $geocoder->geocodeAsync("82 Clerkenwell Road, London");
83+
/** @var array{status: array{code: int, message: string}} $result */
84+
$result = $promise->wait();
85+
86+
$this->assertEquals(200, $result['status']['code']);
87+
$this->assertEquals('OK', $result['status']['message']);
88+
}
89+
90+
public function testAsyncNetworkError(): void
91+
{
92+
// https://opencagedata.com/api#testingkeys
93+
$geocoder = new Geocoder('6d0e711d72d74daeb2b0bfd2a5cdfdba');
94+
$geocoder->setHost('doesnotexist.opencagedata.com');
95+
$promise = $geocoder->geocodeAsync('London');
96+
/** @var array{status: array{code: int, message: string}} $result */
97+
$result = $promise->wait();
98+
99+
$this->assertEquals(498, $result['status']['code']);
100+
$this->assertStringContainsString('network issue', $result['status']['message']);
101+
}
102+
78103
public function testProxy(): void
79104
{
80105
$proxy = getenv('PROXY');

0 commit comments

Comments
 (0)