Skip to content

Commit 3069d34

Browse files
committed
Add document for async client
1 parent e78c12b commit 3069d34

File tree

3 files changed

+164
-19
lines changed

3 files changed

+164
-19
lines changed

docs/discovery.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ composer require "php-http/discovery"
1919

2020
## HTTP Client Discovery
2121

22-
This type of discovery finds installed HTTP Clients.
22+
This type of discovery finds a HTTPClient implementation.
2323

2424
``` php
2525
use Http\Client\HttpClient;
@@ -42,10 +42,34 @@ class MyClass
4242
}
4343
```
4444

45+
## HTTP Async Client Discovery
46+
47+
This type of discovery finds a HttpAsyncClient implementation.
48+
49+
``` php
50+
use Http\Client\HttpAsyncClient;
51+
use Http\Discovery\HttpAsyncClientDiscovery;
52+
53+
class MyClass
54+
{
55+
/**
56+
* @var HttpAsyncClient
57+
*/
58+
protected $httpAsyncClient;
59+
60+
/**
61+
* @param HttpAsyncClient|null $httpAsyncClient to do HTTP requests.
62+
*/
63+
public function __construct(HttpAsyncClient $httpAsyncClient = null)
64+
{
65+
$this->httpAsyncClient = $httpAsyncClient ?: HttpAsyncClientDiscovery::find();
66+
}
67+
}
68+
```
4569

4670
## PSR-7 Message Factory Discovery
4771

48-
This type of discovery finds installed [PSR-7](http://www.php-fig.org/psr/psr-7/) Message implementations and their [factories](message-factory.md).
72+
This type of discovery finds a [PSR-7](http://www.php-fig.org/psr/psr-7/) Message implementation and their [factories](message-factory.md).
4973

5074
``` php
5175
use Http\Message\MessageFactory;
@@ -71,7 +95,7 @@ class MyClass
7195

7296
## PSR-7 URI Factory Discovery
7397

74-
This type of discovery finds installed [PSR-7](http://www.php-fig.org/psr/psr-7/) URI implementations and their factories.
98+
This type of discovery finds a [PSR-7](http://www.php-fig.org/psr/psr-7/) URI implementation and their factories.
7599

76100
``` php
77101
use Http\Message\UriFactory;
@@ -117,7 +141,7 @@ Classes registered manually are put on top of the list.
117141

118142
### Writing your own discovery
119143

120-
Each discovery service is based on the `ClassDiscovery` and has to specify a `cache` field and a `class` field to specify classes for the corresponding service. The fields need to be redeclared in each discovery class. If `ClassDiscovery` would declare them, they would be shared between the discovery classes which would make no sense.
144+
Each discovery service is based on the `ClassDiscovery` and has to specify a `cache` field and a `class` field to specify classes for the corresponding service. The fields need to be redeclared in each discovery class. If `ClassDiscovery` would declare them, they would be shared between the discovery classes which would make no sense.
121145

122146
Here is an example discovery:
123147

docs/httplug.md

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,35 @@ Httplug is an abstraction for HTTP clients. There are two main use cases:
55
1. Usage in a project
66
2. Usage in a reusable package
77

8-
In both cases, the client provides a `sendRequest` method to send a PSR-7 `RequestInterface` and returns a PSR-7 `ResponseInterface` or throws an exception that implements `Http\Client\Exception`.
8+
In both cases, the client provides a `sendRequest` method to send a PSR-7 `RequestInterface` and returns a PSR-7 `ResponseInterface`
9+
or throws an exception that implements `Http\Client\Exception`.
910

10-
See the [tutorial](tutorial.md) for a concrete example.
11+
There is also the HttpAsyncClient, available in [php-http/httplug-async](https://packagist.org/packages/php-http/httplug-async), which provides the `sendAsyncRequest` method to send a request asynchronously and returns a `Http\Client\Promise`.
12+
It can be used later to retrieve a PSR-7 `ResponseInterface` or an exception that implements `Http\Client\Exception`.
13+
14+
Contract for the HttpAsyncClient is still experimental and will be merged into Httplug repository once we considered it stable.
1115

16+
See the [tutorial](tutorial.md) for a concrete example.
1217

1318
## Httplug implementations
1419

15-
Httplug implementations typically are either HTTP clients of their own, or they are adapters wrapping existing clients like Guzzle 6. In the latter case, they will depend on the required client implementation, so you only need to require the adapter and not the actual client.
20+
Httplug implementations typically are either HTTP clients of their own, or they are adapters wrapping existing clients like Guzzle 6.
21+
In the latter case, they will depend on the required client implementation, so you only need to require the adapter and not the actual client.
1622

17-
See [packagist](https://packagist.org/providers/php-http/client-implementation) for the full list of implementations.
23+
There is two kind of implementation:
24+
25+
* [php-http/client-implementation](https://packagist.org/providers/php-http/client-implementation), the standard implementation, send requests with a synchronous workflow
26+
* [php-http/client-async-implementation](https://packagist.org/providers/php-http/client-async-implementation), send requests with an asynchronous workflow by returning promises
27+
28+
See [https://packagist.org/providers/php-http/client-implementation](https://packagist.org/providers/php-http/client-implementation) or [https://packagist.org/providers/php-http/client-async-implementation](https://packagist.org/providers/php-http/client-async-implementation) for
29+
the full list of implementations.
1830

1931
Note: Until Httplug 1.0 becomes stable, we will focus on the Guzzle6 adapter.
2032

2133
## Usage in a project
2234

23-
When writing an application, you need to require a concrete [client implementation](https://packagist.org/providers/php-http/client-implementation).
35+
When writing an application, you need to require a concrete [client implementation](https://packagist.org/providers/php-http/client-implementation) or
36+
a concrete [async client implementation](https://packagist.org/providers/php-http/client-async-implementation).
2437

2538
See [virtual package](virtual-package.md) for more information on the topic of working with Httplug implementations.
2639

@@ -29,17 +42,20 @@ See [virtual package](virtual-package.md) for more information on the topic of w
2942

3043
In many cases, packages are designed to be reused from the very beginning. For example, API clients are usually used in other packages/applications, not on their own.
3144

32-
In these cases, they should **not rely on a concrete implementation** (like Guzzle 6), but only require any implementation of Httplug. Httplug uses the concept of virtual packages. Instead of depending on only the interfaces, which would be missing an implementation, or depending on one concrete implementation, you should depend on the virtual package `php-http/client-implementation`. There is no package with that name, but all clients and adapters implementing Httplug declare that they provide this virtual package.
45+
In these cases, they should **not rely on a concrete implementation** (like Guzzle 6), but only require any implementation of Httplug.
46+
Httplug uses the concept of virtual packages. Instead of depending on only the interfaces, which would be missing an implementation,
47+
or depending on one concrete implementation, you should depend on the virtual package `php-http/client-implementation` or `php-http/async-client-implementation`.
48+
There is no package with that name, but all clients and adapters implementing Httplug declare that they provide one of this virtual package or both.
3349

3450
You need to edit the `composer.json` of your package to add the virtual package. For development (installing the package standalone, running tests), add a concrete implementation in the `require-dev` section to make the project installable:
3551

3652
``` json
3753
...
38-
"require": {
39-
"php-http/client-implementation": "^1.0"
40-
},
41-
"require-dev": {
42-
"php-http/guzzle6-adapter": "^1.0"
54+
"require": {
55+
"php-http/client-implementation": "^1.0"
56+
},
57+
"require-dev": {
58+
"php-http/guzzle6-adapter": "^1.0"
4359
},
4460
...
4561
```

docs/tutorial.md

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,118 @@ require('vendor/autoload.php');
3232
TODO: create client instance with discovery and do some requests
3333
```
3434

35-
## Handling errors
35+
## Using an asynchronous client
3636

37-
TODO: explain how to handle exceptions, distinction between network exception and HttpException.
37+
When using an asynchronous client, it will use a PSR-7 `RequestInterface` and returns a `Http\Client\Promise` :
38+
39+
```php
40+
$httpAsyncClient = new HttpAsyncClientImplementation();
41+
$promise = $httpAsyncClient->sendAsyncRequest($request);
42+
```
3843

39-
## Doing parallel requests
44+
This promise allows you to :
45+
46+
* Add callbacks for when the response is available or an errors happens by using the then method:
47+
48+
```php
49+
$promise->then(function (ResponseInterface $response) {
50+
// onFulfilled callback
51+
echo 'The response is available';
52+
53+
return $response;
54+
}, function (Exception $e) {
55+
// onRejected callback
56+
echo 'An error happens';
57+
58+
throw $e;
59+
});
60+
```
61+
62+
This method will return another promise so you can manipulate the response and/or exception and
63+
still provide a way to interact with this object for your users:
64+
65+
```php
66+
$promise->then(function (ResponseInterface $response) {
67+
// onFulfilled callback
68+
echo 'The response is available';
69+
70+
return $response;
71+
}, function (Exception $e) {
72+
// onRejected callback
73+
echo 'An error happens';
74+
75+
throw $e;
76+
})->then(function (ResponseInterface $response) {
77+
echo 'Response stil available';
78+
79+
return $response;
80+
}, function (Exception $e) {
81+
throw $e
82+
});
83+
```
84+
85+
In order to achieve the chain callback, if you read previous examples carefully, callbacks provided to the `then` method __must__
86+
return a PSR-7 `ResponseInterface` or throw a `Http\Client\Exception`. For both of the callbacks, if it returns a PSR-7 `ResponseInterface`
87+
it will call the `onFulfilled` callback for the next element in the chain, if it throws a `Http\Client\Exception` it will call the `onRejected`
88+
callback.
89+
90+
i.e. you can inverse the behavior of a call:
91+
92+
```php
93+
$promise->then(function (ResponseInterface $response) use($request) {
94+
// onFulfilled callback
95+
echo 'The response is available, but it\'s not ok...';
96+
97+
throw new HttpException('My error message', $request, $response);
98+
}, function (Exception $e) {
99+
// onRejected callback
100+
echo 'An error happens, but it\'s ok...';
101+
102+
return $exception->getResponse();
103+
});
104+
```
105+
106+
* Get the state of the promise with `$promise->getState()` will return of one `Promise::PENDING`, `Promise::FULFILLED` or `Promise::REJECTED`
107+
* Get the response of the promise if it's in `FULFILLED` state with `$promise->getResponse()` call
108+
* Get the error of the promise if it's in `REJECTED` state with `$promise->getRequest()` call
109+
* wait for the callback to be fulfilled or rejected with the `$promise->wait()` call. The `wait` will return nothing, it will simply call one the callback
110+
pass to the `then` method depending on the result of the call. It the promise has already been fulfilled or rejected it will do nothing.
111+
112+
Here is a full example of a classic usage when using the `sendAsyncRequest` method:
113+
114+
```php
115+
$httpAsyncClient = new HttpAsyncClientImplementation();
116+
117+
$promise = $httpAsyncClient->sendAsyncRequest($request);
118+
$promise->then(function (ResponseInterface $response) {
119+
echo 'The response is available';
120+
121+
return $response;
122+
}, function (Exception $e) {
123+
echo 'An error happens';
124+
125+
throw $e;
126+
});
127+
128+
// Do some stuff not depending on the response, calling another request, etc ..
129+
...
130+
131+
// We need now the response for our final treatment
132+
$promise->wait();
133+
134+
if (Promise::FULFILLED === $promise->getState()) {
135+
$response = $promise->getResponse();
136+
} else {
137+
throw new \Exception('Response not available');
138+
}
139+
140+
// Do your stuff with the response
141+
...
142+
```
143+
144+
## Handling errors
40145

41-
TODO explain sendRequests and how to work with BatchResult and BatchException
146+
TODO: explain how to handle exceptions, distinction between network exception and HttpException.
42147

43148

44149
## Writing a reusable package

0 commit comments

Comments
 (0)