Skip to content

Commit ba24222

Browse files
author
Evert Harmeling
authored
Merge pull request #15 from evertharmeling/postcodes
Make use of HTTPlug and implemented the /postcodes call
2 parents faf2c54 + bf19113 commit ba24222

File tree

10 files changed

+264
-121
lines changed

10 files changed

+264
-121
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
language: php
22

33
php:
4-
- 5.4
54
- 5.5
65
- 5.6
76
- 7.0
7+
- 7.1
88

99
before_script:
10-
- composer install --dev
10+
- composer install
1111

1212
script: phpunit --coverage-text
1313

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# CHANGELOG
22

3+
## 3.0.0
4+
5+
* Dropped direct usage of Guzzle and now make use of [HTTPPlug](http://httplug.io/), see README.
6+
* Added `getPostcodes()` call, to get postcodes based on provided `latitude, longitude`. Note that this call is only available with a premium account.
7+
38
## 2.0.0
49

510
* Rewrite to support postcodeapi.nu version 2

README.md

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ by [Freshheads](https://www.freshheads.com) and will be maintained in sync with
1414
Requirements
1515
------------
1616

17-
FHPostcodeAPIClient works with PHP 5.4.0 or up. This library is dependent on the awesome [Guzzle](http://guzzlephp.org/) HTTP client library. Guzzle 5
18-
version is used instead of the new Guzzle 6, as Guzzle 6 requires the php version to be higher than 5.5.0.
17+
FHPostcodeAPIClient works with PHP 5.5.0 or up. This library depends on the [HTTPPlug](http://httplug.io/), see http://docs.php-http.org/en/latest/httplug/introduction.html.
1918

2019
Installation
2120
------------
@@ -37,11 +36,57 @@ require_once 'vendor/autoload.php';
3736

3837
// initiate client
3938
$apiKey = 'replace_with_your_own_api_key';
40-
$client = new \FH\PostcodeAPI\Client(new \GuzzleHttp\Client(), $apiKey);
39+
// In this example we made use of the Guzzle6 as HTTPClient in combination with an HTTPPlug compatible adapter.
40+
$client = new \FH\PostcodeAPI\Client(
41+
new Http\Adapter\Guzzle6\Client(
42+
new GuzzleHttp\Client([
43+
'headers' => [
44+
'X-Api-Key' => $apiKey
45+
]
46+
])
47+
)
48+
);
4149

4250
// call endpoints
4351
$response = $client->getAddresses('5041EB', 21);
4452
$response = $client->getAddress('0855200000061001');
53+
54+
// Note that this call is only available with a premium account
55+
$response = $client->getPostcodes('51.566405', '5.077171');
56+
```
57+
58+
Note that to be able to run the example above you should have ran the following command, to have Guzzle6 and the Adapter available.
59+
60+
```bash
61+
composer require php-http/guzzle6-adapter
62+
```
63+
64+
Within Symfony project
65+
----------------------
66+
67+
We recommend to use [Guzzle](https://github.com/guzzle/guzzle), to be able to use Guzzle in combination with the PostcodeApiClient you should also make use of the
68+
[Guzzle6Adapter](https://github.com/php-http/guzzle6-adapter). By running the following command you automatically install Guzzle aswel.
69+
70+
```bash
71+
composer require php-http/guzzle6-adapter
72+
```
73+
74+
And add the following service definitions:
75+
```yaml
76+
project.http.guzzle.client:
77+
class: GuzzleHttp\Client
78+
arguments:
79+
- { headers: { X-Api-Key: 'replace_with_your_own_api_key' } }
80+
81+
project.http.adapter.guzzle.client:
82+
class: Http\Adapter\Guzzle6\Client
83+
arguments:
84+
- '@project.http.guzzle.client'
85+
86+
project.client.postal_code:
87+
class: FH\PostcodeAPI\Client
88+
arguments:
89+
- '@project.http.adapter.guzzle.client'
4590
```
4691
47-
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/freshheads/fhpostcodeapiclient/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
92+
You should now be able use the `project.client.postal_code` service to make requests to the PostcodeAPI.

composer.json

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,25 @@
1717
],
1818
"require": {
1919
"php": ">=5.4.0",
20-
"guzzlehttp/guzzle": "^5.3"
20+
"php-http/httplug": "^1.1",
21+
"guzzlehttp/psr7": "^1.3"
22+
},
23+
"require-dev": {
24+
"phpunit/phpunit": "^4.3.5|^5.0",
25+
"php-http/mock-client": "^0.3.0"
26+
},
27+
"suggest": {
28+
"php-http/guzzle6-adapter": "An HTTPlug adapter for the Guzzle 6 HTTP client"
2129
},
2230
"autoload": {
2331
"psr-0": { "FH\\PostcodeAPI": "lib/" }
2432
},
2533
"extra": {
2634
"branch-alias": {
27-
"dev-master": "2.0.x-dev"
35+
"dev-master": "3.0.x-dev"
2836
}
37+
},
38+
"scripts": {
39+
"test": "./vendor/phpunit/phpunit/phpunit --coverage-text"
2940
}
3041
}

lib/FH/PostcodeAPI/Client.php

Lines changed: 83 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,63 +3,58 @@
33
namespace FH\PostcodeAPI;
44

55
use FH\PostcodeAPI\Exception\CouldNotParseResponseException;
6-
use GuzzleHttp\Client as HTTPClient;
7-
use GuzzleHttp\ClientInterface;
8-
use GuzzleHttp\Exception\RequestException;
9-
use GuzzleHttp\Message\Request;
10-
use GuzzleHttp\Message\ResponseInterface;
6+
use FH\PostcodeAPI\Exception\InvalidApiKeyException;
7+
use FH\PostcodeAPI\Exception\ServerErrorException;
8+
use GuzzleHttp\Psr7\Request;
9+
use Http\Client\HttpClient;
10+
use Psr\Http\Message\RequestInterface;
11+
use Psr\Http\Message\ResponseInterface;
1112

1213
/**
1314
* Client library for postcodeapi.nu 2.0 web service.
1415
*
1516
* @author Gijs Nieuwenhuis <gijs.nieuwenhuis@freshheads.com>
17+
* @author Evert Harmeling <evert@freshheads.com>
1618
*/
1719
class Client
1820
{
19-
/** @var string */
20-
const BASE_URI = 'https://postcode-api.apiwise.nl';
21+
const POSTCODES_SORT_DISTANCE = 'distance';
2122

2223
/**
23-
* @var HTTPClient
24+
* @var null|string
2425
*/
25-
private $httpClient;
26+
private $url = 'https://postcode-api.apiwise.nl';
2627

2728
/**
28-
* @param ClientInterface $httpClient
29-
* @param string $apiKey Required API key for authenticating client
29+
* @var string
3030
*/
31-
public function __construct(ClientInterface $httpClient, $apiKey)
32-
{
33-
$this->httpClient = $this->prepareClient($httpClient, $apiKey);
34-
}
31+
private $version = 'v2';
3532

3633
/**
37-
* @param ClientInterface $client
38-
* @param string $apiKey
39-
*
40-
* @return HTTPClient
34+
* @var HttpClient
4135
*/
42-
private function prepareClient(ClientInterface $client, $apiKey)
36+
private $httpClient;
37+
38+
39+
public function __construct(HttpClient $httpClient, $url = null)
4340
{
44-
if ($client->getDefaultOption('timeout') === null) {
45-
$client->setDefaultOption('timeout', 5.0);
41+
if (null !== $url) {
42+
$this->url = $url;
4643
}
4744

48-
$client->setDefaultOption('headers/X-Api-Key', $apiKey);
49-
50-
return $client;
45+
$this->httpClient = $httpClient;
5146
}
5247

5348
/**
5449
* @param string|null $postcode
5550
* @param string|null $number
5651
* @param int $from
5752
*
58-
* @return \StdClass
53+
* @return \stdClass
5954
*/
6055
public function getAddresses($postcode = null, $number = null, $from = 0)
6156
{
62-
return $this->get('/v2/addresses/', [
57+
return $this->get('/addresses/', [
6358
'postcode' => $postcode,
6459
'number' => $number,
6560
'from' => $from
@@ -69,61 +64,95 @@ public function getAddresses($postcode = null, $number = null, $from = 0)
6964
/**
7065
* @param string $id
7166
*
72-
* @return \StdClass
67+
* @return \stdClass
7368
*/
7469
public function getAddress($id)
7570
{
76-
return $this->get("/v2/addresses/{$id}");
71+
return $this->get(sprintf('/addresses/%s', $id));
72+
}
73+
74+
/**
75+
* @param string $latitude
76+
* @param string $longitude
77+
* @param string $sort
78+
*
79+
* @return \stdClass
80+
*/
81+
public function getPostcodesByCoordinates($latitude, $longitude, $sort = self::POSTCODES_SORT_DISTANCE)
82+
{
83+
return $this->get('/postcodes/', [
84+
'coords' => [
85+
'latitude' => $latitude,
86+
'longitude' => $longitude
87+
],
88+
'sort' => $sort
89+
]);
7790
}
7891

7992
/**
8093
* @param string $path
81-
* @param array $queryParams
94+
* @param array $params
8295
*
83-
* @return \StdClass
96+
* @return \stdClass
8497
*
8598
* @throws RequestException
8699
*/
87-
private function get($path, array $queryParams = array())
100+
private function get($path, array $params = [])
88101
{
89-
$url = self::BASE_URI . $path;
102+
$request = $this->createHttpGetRequest($this->buildUrl($path), $params);
90103

91-
$request = $this->createHttpRequest('GET', $url, $queryParams);
104+
$response = $this->httpClient->sendRequest($request);
92105

93-
$response = $this->httpClient->send($request);
94-
95-
return $this->parseResponse($response);
106+
return $this->parseResponse($response, $request);
96107
}
97108

98109
/**
99-
* @param ResponseInterface $response
100-
*
101-
* @return \StdClass
102-
*
103-
* @throws CouldNotParseResponseException
110+
* @param string $path
111+
* @return string
104112
*/
105-
private function parseResponse(ResponseInterface $response)
113+
private function buildUrl($path)
106114
{
107-
$out = json_decode((string) $response->getBody());
108-
109-
if (json_last_error() !== JSON_ERROR_NONE) {
110-
throw new CouldNotParseResponseException('Could not parse resonse', $response);
111-
}
112-
113-
return $out;
115+
return sprintf('%s/%s%s', $this->url, $this->version, $path);
114116
}
115117

116118
/**
117119
* @param string $method
118-
* @param string $path
120+
* @param string $url
119121
* @param array $queryParams
120122
*
121123
* @return Request
122124
*/
123-
private function createHttpRequest($method, $path, array $queryParams = array())
125+
private function createHttpGetRequest($url, array $params = [])
124126
{
125-
$path = $path . (count($queryParams) > 0 ? '?' . http_build_query($queryParams) : '');
127+
$url .= (count($params) > 0 ? '?' . http_build_query($params, null, '&', PHP_QUERY_RFC3986) : '');
128+
129+
return new Request('GET', $url);
130+
}
131+
132+
/**
133+
* @param ResponseInterface $response
134+
*
135+
* @return \stdClass
136+
*
137+
* @throws CouldNotParseResponseException
138+
*/
139+
private function parseResponse(ResponseInterface $response, RequestInterface $request)
140+
{
141+
$result = json_decode((string) $response->getBody()->getContents());
142+
143+
if (json_last_error() !== JSON_ERROR_NONE) {
144+
throw new CouldNotParseResponseException('Could not parse response', $response);
145+
}
146+
147+
if (property_exists($result, 'error')) {
148+
switch ($result->error) {
149+
case 'API key is invalid.':
150+
throw new InvalidApiKeyException();
151+
case 'An unknown server error occured.':
152+
throw ServerErrorException::fromRequest($request);
153+
}
154+
}
126155

127-
return $this->httpClient->createRequest($method, $path);
156+
return $result;
128157
}
129158
}

lib/FH/PostcodeAPI/Exception/CouldNotParseResponseException.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
namespace FH\PostcodeAPI\Exception;
44

5-
use GuzzleHttp\Message\ResponseInterface;
5+
use Psr\Http\Message\ResponseInterface;
66

77
/**
88
* @author Gijs Nieuwenhuis <gijs.nieuwenhuis@freshheads.com>
99
*/
10-
final class CouldNotParseResponseException extends \Exception
10+
final class CouldNotParseResponseException extends \Exception implements PostcodeApiExceptionInterface
1111
{
1212
/**
1313
* @var ResponseInterface
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace FH\PostcodeAPI\Exception;
4+
5+
/**
6+
* @author Evert Harmeling <evert@freshheads.com>
7+
*/
8+
class InvalidApiKeyException extends \Exception implements PostcodeApiExceptionInterface
9+
{
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace FH\PostcodeAPI\Exception;
4+
5+
/**
6+
* @author Evert Harmeling <evert@freshheads.com>
7+
*/
8+
interface PostcodeApiExceptionInterface
9+
{
10+
}

0 commit comments

Comments
 (0)