Skip to content

Commit f41ef12

Browse files
[HttpClient] add doc about extending and AsyncDecoratorTrait
1 parent fcf2eba commit f41ef12

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

http_client.rst

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,110 @@ This allows using them where native PHP streams are needed::
13221322
// later on if you need to, you can access the response from the stream
13231323
$response = stream_get_meta_data($streamResource)['wrapper_data']->getResponse();
13241324

1325+
Extensibility
1326+
-------------
1327+
1328+
In order to extend the behavior of a base HTTP client, decoration is the way to go::
1329+
1330+
class MyExtendedHttpClient implements HttpClientInterface
1331+
{
1332+
private $decoratedClient;
1333+
1334+
public function __construct(HttpClientInterface $decoratedClient = null)
1335+
{
1336+
$this->decoratedClient = $decoratedClient ?? HttpClient::create();
1337+
}
1338+
1339+
public function request(string $method, string $url, array $options = []): ResponseInterface
1340+
{
1341+
// do what you want here with $method, $url and/or $options
1342+
1343+
$response = $this->decoratedClient->request();
1344+
1345+
//!\ calling any method on $response here would break async, see below for a better way
1346+
1347+
return $response;
1348+
}
1349+
1350+
public function stream($responses, float $timeout = null): ResponseStreamInterface
1351+
{
1352+
return $this->decoratedClient->stream($responses, $timeout);
1353+
}
1354+
}
1355+
1356+
A decorator like this one is suited for use cases where processing the
1357+
requests' arguments is enough.
1358+
1359+
By decorating the ``on_progress`` option, one can
1360+
even implement basic monitoring of the response. But since calling responses'
1361+
methods forces synchronous operations, doing so in ``request()`` breaks async.
1362+
The solution then is to also decorate the response object itself.
1363+
:class:`Symfony\\Component\\HttpClient\\TraceableHttpClient` and
1364+
:class:`Symfony\\Component\\HttpClient\\Response\\TraceableResponse` are good
1365+
examples as a starting point.
1366+
1367+
.. versionadded:: 5.2
1368+
1369+
``AsyncDecoratorTrait`` was introduced in Symfony 5.2.
1370+
1371+
In order to help writing more advanced response processors, the component provides
1372+
an :class:`Symfony\\Component\\HttpClient\\AsyncDecoratorTrait`. This trait allows
1373+
processing the stream of chunks as they come back from the network::
1374+
1375+
class MyExtendedHttpClient implements HttpClientInterface
1376+
{
1377+
use AsyncDecoratorTrait;
1378+
1379+
public function request(string $method, string $url, array $options = []): ResponseInterface
1380+
{
1381+
// do what you want here with $method, $url and/or $options
1382+
1383+
$passthru = function (ChunkInterface $chunk, AsyncContext $context) {
1384+
1385+
// do what you want with chunks, e.g. split them
1386+
// in smaller chunks, group them, skip some, etc.
1387+
1388+
yield $chunk;
1389+
};
1390+
1391+
return new AsyncResponse($this->client, $method, $url, $options, $passthru);
1392+
}
1393+
}
1394+
1395+
Because the trait already implements a constructor and the ``stream()`` method,
1396+
you don't need to add them. The ``request()`` method should still be defined;
1397+
it shall return an
1398+
:class:`Symfony\\Component\\HttpClient\\Response\\AsyncResponse`.
1399+
1400+
The custom processing of chunks should happen in ``$passthru``: this generator
1401+
is where you need to write your logic. It will be called for each chunk yielded by
1402+
the underlying client. A ``$passthru`` that does nothing would just ``yield $chunk;``.
1403+
Of course, you could also yield a modified chunk, split the chunk into many
1404+
ones by yielding several times, or even skip a chunk altogether by issuing a
1405+
``return;`` instead of yielding.
1406+
1407+
In order to control the stream, the chunk passthru receives an
1408+
:class:`Symfony\\Component\\HttpClient\\Response\\AsyncContext` as second
1409+
argument. This context object has methods to read the current state of the
1410+
response. It also allows altering the response stream with methods to create new
1411+
chunks of content, pause the stream, cancel the stream, change the info of the
1412+
response, replace the current request by another one or change the chunk passthru
1413+
itself.
1414+
1415+
Checking the test cases implemented in
1416+
:class:`Symfony\\Component\\HttpClient\\Response\\Tests\\AsyncDecoratorTraitTest`
1417+
might be a good start to get various working examples for a better understanding.
1418+
Here are the use cases that it simulates:
1419+
1420+
* retry a failed request;
1421+
* send a preflight request, e.g. for authentication needs;
1422+
* issue subrequests and include their content in the main response's body.
1423+
1424+
The logic in :class:`Symfony\\Component\\HttpClient\\Response\\AsyncResponse` has
1425+
many safety checks that will throw a ``LogicException`` if the chunk passthru
1426+
doesn't behave correctly; e.g. if a chunk is yielded after an ``isLast()`` one,
1427+
or if a content chunk is yielded before an ``isFirst()`` one, etc.
1428+
13251429
Testing HTTP Clients and Responses
13261430
----------------------------------
13271431

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy