Skip to content

Commit 4703ad1

Browse files
committed
Merge branch '4.4'
* 4.4: [HttpClient] improve doc again [Mailer] Sort providers alphabetically
2 parents 3c7a8c1 + 06ef5a3 commit 4703ad1

File tree

2 files changed

+179
-41
lines changed

2 files changed

+179
-41
lines changed

components/http_client.rst

Lines changed: 178 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@ The HttpClient Component
99
PHP stream wrappers and cURL. It provides utilities to consume APIs and
1010
supports synchronous and asynchronous operations.
1111

12-
.. TODO
13-
.. tell about implementation vs abstraction
14-
.. tell there are more options
15-
.. tell chunked + compression are supported out of the box
16-
1712
Installation
1813
------------
1914

@@ -67,17 +62,16 @@ When using this component in a full-stack Symfony application, this behavior is
6762
not configurable and cURL will be used automatically if the cURL PHP extension
6863
is installed and enabled. Otherwise, the native PHP streams will be used.
6964

70-
Enabling HTTP/2 Support
71-
-----------------------
65+
HTTP/2 Support
66+
--------------
7267

73-
HTTP/2 is only supported when using the cURL-based transport and the libcurl
74-
version is >= 7.36.0. If you meet these requirements, HTTP/2 will be used by
75-
default when the request protocol is ``https``. If you need it for ``http``,
76-
you must enable it explicitly via the ``http_version`` option::
68+
When requesting an ``https`` URL, HTTP/2 is enabled by default if libcurl >= 7.36
69+
is used. To force HTTP/2 for ``http`` URLs, you need to enable it explicitly via
70+
the ``http_version`` option::
7771

7872
$httpClient = HttpClient::create(['http_version' => '2.0']);
7973

80-
Support for HTTP/2 PUSH works out of the box when libcurl >= 7.61.0 is used with
74+
Support for HTTP/2 PUSH works out of the box when libcurl >= 7.61 is used with
8175
PHP >= 7.2.17 / 7.3.4: pushed responses are put into a temporary cache and are
8276
used when a subsequent request is triggered for the corresponding URLs.
8377

@@ -108,6 +102,11 @@ immediately instead of waiting to receive the response::
108102
This component also supports :ref:`streaming responses <http-client-streaming-responses>`
109103
for full asynchronous applications.
110104

105+
.. note::
106+
107+
HTTP compression and chunked transfer encoding are automatically enabled when
108+
both your PHP runtime and the remote server support them.
109+
111110
Authentication
112111
~~~~~~~~~~~~~~
113112

@@ -229,13 +228,12 @@ making a request. Use the ``max_redirects`` setting to configure this behavior
229228
'max_redirects' => 0,
230229
]);
231230

232-
.. Concurrent Requests
233-
.. ~~~~~~~~~~~~~~~~~~~
234-
..
235-
..
236-
.. TODO
237-
..
238-
..
231+
Advanced Options
232+
~~~~~~~~~~~~~~~~
233+
234+
The :class:`Symfony\\Contracts\\HttpClient\\HttpClientInterface` defines all the
235+
options you might need to take full control of the way the request is performed,
236+
including progress monitoring, DNS pre-resolution, timeout, SSL parameters, etc.
239237

240238
Processing Responses
241239
--------------------
@@ -261,6 +259,12 @@ following methods::
261259
// you can get individual info too
262260
$startTime = $response->getInfo('start_time');
263261

262+
.. note::
263+
264+
``$response->getInfo()`` is non-blocking: it returns *live* information
265+
about the response. Some of them might not be known yet (e.g. ``http_code``)
266+
when you'll call it.
267+
264268
.. tip::
265269

266270
Call ``$response->getInfo('debug')`` to get detailed logs about the HTTP transaction.
@@ -313,6 +317,146 @@ When the HTTP status code of the response is in the 300-599 range (i.e. 3xx,
313317
// instead the original response content (even if it's an error message)
314318
$content = $response->getContent(false);
315319

320+
Concurrent Requests
321+
-------------------
322+
323+
Thanks to responses being lazy, requests are always managed concurrently.
324+
On a fast enough network, the following code makes 379 requests in less than
325+
half a second when cURL is used::
326+
327+
use Symfony\Component\HttpClient\CurlHttpClient;
328+
329+
$client = new CurlHttpClient();
330+
331+
$responses = [];
332+
333+
for ($i = 0; $i < 379; ++$i) {
334+
$uri = "https://http2.akamai.com/demo/tile-$i.png";
335+
$responses[] = $client->request('GET', $uri);
336+
}
337+
338+
foreach ($responses as $response) {
339+
$content = $response->getContent();
340+
// ...
341+
}
342+
343+
As you can read in the first "for" loop, requests are issued but are not consumed
344+
yet. That's the trick when concurrency is desired: requests should be sent
345+
first and be read later on. This will allow the client to monitor all pending
346+
requests while your code waits for a specific one, as done in each iteration of
347+
the above "foreach" loop.
348+
349+
Multiplexing Responses
350+
~~~~~~~~~~~~~~~~~~~~~~
351+
352+
If you look again at the snippet above, responses are read in requests' order.
353+
But maybe the 2nd response came back before the 1st? Fully asynchronous operations
354+
require being able to deal with the responses in whatever order they come back.
355+
356+
In order to do so, the ``stream()`` method of HTTP clients accepts a list of
357+
responses to monitor. As mentioned :ref:`previously <http-client-streaming-responses>`,
358+
this method yields response chunks as they arrive from the network. By replacing
359+
the "foreach" in the snippet with this one, the code becomes fully async::
360+
361+
foreach ($client->stream($responses) as $response => $chunk) {
362+
if ($chunk->isFirst()) {
363+
// headers of $response just arrived
364+
// $response->getHeaders() is now a non-blocking call
365+
} elseif ($chunk->isLast()) {
366+
// the full content of $response just completed
367+
// $response->getContent() is now a non-blocking call
368+
} else {
369+
// $chunk->getContent() will return a piece
370+
// of the response body that just arrived
371+
}
372+
}
373+
374+
.. tip::
375+
376+
Use the ``user_data`` option combined with ``$response->getInfo('user_data')``
377+
to track the identity of the responses in your foreach loops.
378+
379+
Dealing with Network Timeouts
380+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
381+
382+
This component allows dealing with both request and response timeouts.
383+
384+
A timeout can happen when e.g. DNS resolution takes too much time, when the TCP
385+
connection cannot be opened in the given time budget, or when the response
386+
content pauses for too long. This can be configured with the ``timeout`` request
387+
option::
388+
389+
// A TransportExceptionInterface will be issued if nothing
390+
// happens for 2.5 seconds when accessing from the $response
391+
$response = $client->request('GET', 'https://...', ['timeout' => 2.5]);
392+
393+
The ``default_socket_timeout`` PHP ini setting is used if the option is not set.
394+
395+
The option can be overridden by using the 2nd argument of the ``stream()`` method.
396+
This allows monitoring several responses at once and applying the timeout to all
397+
of them in a group. If all responses become inactive for the given duration, the
398+
method will yield a special chunk whose ``isTimeout()`` will return ``true``::
399+
400+
foreach ($client->stream($responses, 1.5) as $response => $chunk) {
401+
if ($chunk->isTimeout()) {
402+
// $response staled for more than 1.5 seconds
403+
}
404+
}
405+
406+
A timeout is not necessarily an error: you can decide to stream again the
407+
response and get remaining contents that might come back in a new timeout, etc.
408+
409+
.. tip::
410+
411+
Passing ``0`` as timeout allows monitoring responses in a non-blocking way.
412+
413+
.. note::
414+
415+
Timeouts control how long one is willing to wait *while the HTTP transaction
416+
is idle*. Big responses can last as long as needed to complete, provided they
417+
remain active during the transfer and never pause for longer than specified.
418+
419+
Dealing with Network Errors
420+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
421+
422+
Network errors (broken pipe, failed DNS resolution, etc.) are thrown as instances
423+
of :class:`Symfony\\Contracts\\HttpClient\\Exception\\TransportExceptionInterface`.
424+
425+
First of all, you don't *have* to deal with them: letting errors bubble to your
426+
generic exception-handling stack might be really fine in most use cases.
427+
428+
If you want to handle them, here is what you need to know:
429+
430+
To catch errors, you need to wrap calls to ``$client->request()`` but also calls
431+
to any methods of the returned responses. This is because responses are lazy, so
432+
that network errors can happen when calling e.g. ``getStatusCode()`` too::
433+
434+
try {
435+
// both lines can potentially throw
436+
$response = $client->request(...);
437+
$headers = $response->getHeaders();
438+
// ...
439+
} catch (TransportExceptionInterface $e) {
440+
// ...
441+
}
442+
443+
.. note::
444+
445+
Because ``$response->getInfo()`` is non-blocking, it shouldn't throw by design.
446+
447+
When multiplexing responses, you can deal with errors for individual streams by
448+
catching ``TransportExceptionInterface`` in the foreach loop::
449+
450+
foreach ($client->stream($responses) as $response => $chunk) {
451+
try {
452+
if ($chunk->isLast()) {
453+
// ... do something with $response
454+
}
455+
} catch (TransportExceptionInterface $e) {
456+
// ...
457+
}
458+
}
459+
316460
Caching Requests and Responses
317461
------------------------------
318462

@@ -431,8 +575,9 @@ the available config options:
431575
framework:
432576
# ...
433577
http_client:
434-
max_redirects: 7
435578
max_host_connections: 10
579+
default_options:
580+
max_redirects: 7
436581
437582
If you want to define multiple HTTP clients, use this other expanded configuration:
438583

@@ -444,16 +589,16 @@ If you want to define multiple HTTP clients, use this other expanded configurati
444589
http_client:
445590
scoped_clients:
446591
crawler.client:
447-
headers: [{ 'X-Powered-By': 'ACME App' }]
592+
headers: { 'X-Powered-By': 'ACME App' }
448593
http_version: '1.0'
449594
some_api.client:
450-
max_redirects: 7
595+
max_redirects: 5
451596
452-
Injecting the HTTP Client Into Services
597+
Injecting the HTTP Client into Services
453598
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
454599

455-
If your application only defines one HTTP client, you can inject it into any
456-
service by type-hinting a constructor argument with the
600+
If your application only needs one HTTP client, you can inject the default one
601+
into any services by type-hinting a constructor argument with the
457602
:class:`Symfony\\Contracts\\HttpClient\\HttpClientInterface`::
458603

459604
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -469,21 +614,14 @@ service by type-hinting a constructor argument with the
469614
}
470615

471616
If you have several clients, you must use any of the methods defined by Symfony
472-
to ref:`choose a specific service <services-wire-specific-service>`. Each client
617+
to :ref:`choose a specific service <services-wire-specific-service>`. Each client
473618
has a unique service named after its configuration.
474619

475-
.. code-block:: yaml
476-
477-
# config/services.yaml
478-
services:
479-
# ...
480-
481-
# whenever a service type-hints HttpClientInterface, inject the GitHub client
482-
Symfony\Contracts\HttpClient\HttpClientInterface: '@some_api.client'
483-
484-
# inject the HTTP client called 'crawler' into this argument of this service
485-
App\Some\Service:
486-
$someArgument: '@crawler.client'
620+
Each scoped client also defines a corresponding named autowiring alias.
621+
If you use for example
622+
``Symfony\Contracts\HttpClient\HttpClientInterface $myApiClient``
623+
as the type and name of an argument, autowiring will inject the ``my_api.client``
624+
service into your autowired classes.
487625

488626
Testing HTTP Clients and Responses
489627
----------------------------------
@@ -492,8 +630,8 @@ This component includes the ``MockHttpClient`` and ``MockResponse`` classes to
492630
use them in tests that need an HTTP client which doesn't make actual HTTP
493631
requests.
494632

495-
The first way of using ``MockHttpClient`` is to configure the set of responses
496-
to return using its constructor::
633+
The first way of using ``MockHttpClient`` is to pass a list of responses to its
634+
constructor. These will be yielded in order when requests are made::
497635

498636
use Symfony\Component\HttpClient\MockHttpClient;
499637
use Symfony\Component\HttpClient\Response\MockResponse;

mailer.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ several - install whichever you want:
4242
Service Install with
4343
================== =============================================
4444
Amazon SES ``composer require symfony/amazon-mailer``
45+
Gmail ``composer require symfony/google-mailer``
4546
MailChimp ``composer require symfony/mailchimp-mailer``
4647
Mailgun ``composer require symfony/mailgun-mailer``
4748
Postmark ``composer require symfony/postmark-mailer``
4849
SendGrid ``composer require symfony/sendgrid-mailer``
49-
Gmail ``composer require symfony/google-mailer``
5050
================== =============================================
5151

5252
Each library includes a :ref:`Flex recipe <flex-recipe>` that will add example configuration

0 commit comments

Comments
 (0)