HTTP Client

Installation

The HttpClient component is a low-level HTTP client with support for both PHP stream wrappers and cURL. It provides utilities to consume APIs and supports synchronous and asynchronous operations. You can install it with:

HttpClient コンポーネントは、PHP ストリーム ラッパーと cURL の両方をサポートする低レベルの HTTP クライアントです。 API を使用するためのユーティリティを提供し、同期操作と非同期操作をサポートします。次の方法でインストールできます。
1
$ composer require symfony/http-client

Basic Usage

Use the HttpClient class to make requests. In the Symfony framework, this class is available as the http_client service. This service will be autowired automatically when type-hinting for HttpClientInterface:

HttpClient クラスを使用してリクエストを作成します。 Symfony フレームワークでは、このクラスは http_client サービスとして利用できます。このサービスは、HttpClientInterface のタイプヒント時に自動的に自動配線されます。
  • Framework Use
    フレームワークの使用
  • Standalone Use
    スタンドアロン使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use Symfony\Contracts\HttpClient\HttpClientInterface;

class SymfonyDocs
{
    private $client;

    public function __construct(HttpClientInterface $client)
    {
        $this->client = $client;
    }

    public function fetchGitHubInformation(): array
    {
        $response = $this->client->request(
            'GET',
            'https://api.github.com/repos/symfony/symfony-docs'
        );

        $statusCode = $response->getStatusCode();
        // $statusCode = 200
        $contentType = $response->getHeaders()['content-type'][0];
        // $contentType = 'application/json'
        $content = $response->getContent();
        // $content = '{"id":521583, "name":"symfony-docs", ...}'
        $content = $response->toArray();
        // $content = ['id' => 521583, 'name' => 'symfony-docs', ...]

        return $content;
    }
}

Tip

ヒント

The HTTP client is interoperable with many common HTTP client abstractions in PHP. You can also use any of these abstractions to profit from autowirings. See Interoperability for more information.

HTTP クライアントは、PHP の多くの一般的な HTTP クライアントの抽象化と相互運用できます。これらの抽象化のいずれかを使用して、オートワイヤリングを利用することもできます。詳細については、相互運用性を参照してください。

Configuration

The HTTP client contains many options you might need to take full control of the way the request is performed, including DNS pre-resolution, SSL parameters, public key pinning, etc. They can be defined globally in the configuration (to apply it to all requests) and to each request (which overrides any global configuration).

HTTP クライアントには、DNS 事前解決、SSL パラメータ、公開鍵のピン留めなど、リクエストの実行方法を完全に制御するために必要な多くのオプションが含まれています。これらは構成でグローバルに定義できます (すべてのリクエストに適用するため)。および各リクエストに対して (グローバル構成をオーバーライドします)。

You can configure the global options using the default_options option:

default_options オプションを使用して、グローバル オプションを設定できます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
  • Standalone Use
    スタンドアロン使用
1
2
3
4
5
# config/packages/framework.yaml
framework:
    http_client:
        default_options:
            max_redirects: 7

You can also use the withOptions() method to retrieve a new instance of the client with new default options:

withOptions() メソッドを使用して、新しいデフォルト オプションでクライアントの新しいインスタンスを取得することもできます。
1
2
3
4
$this->client = $client->withOptions([
    'base_uri' => 'https://...',
    'headers' => ['header-name' => 'header-value']
]);

Some options are described in this guide:

このガイドでは、いくつかのオプションについて説明します。

Check out the full http_client config reference to learn about all the options.

すべてのオプションについては、完全な http_client 構成リファレンスを参照してください。

The HTTP client also has one configuration option called max_host_connections, this option can not be overridden by a request:

HTTP クライアントには、max_host_connections という 1 つの構成オプションもあります。このオプションは、要求によってオーバーライドすることはできません。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
  • Standalone Use
    スタンドアロン使用
1
2
3
4
5
# config/packages/framework.yaml
framework:
    http_client:
        max_host_connections: 10
        # ...

Scoping Client

It's common that some of the HTTP client options depend on the URL of the request (e.g. you must set some headers when making requests to GitHub API but not for other hosts). If that's your case, the component provides scoped clients (using ScopingHttpClient) to autoconfigure the HTTP client based on the requested URL:

一部の HTTP クライアント オプションがリクエストの URL に依存することはよくあることです (たとえば、GitHub API にリクエストを行うときはいくつかのヘッダーを設定する必要がありますが、他のホストには設定しないでください)。その場合、コンポーネントはスコープ指定されたクライアント (ScopingHttpClient を使用) を提供し、要求された URL に基づいて HTTP クライアントを自動構成します。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
  • Standalone Use
    スタンドアロン使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# config/packages/framework.yaml
framework:
    http_client:
        scoped_clients:
            # only requests matching scope will use these options
            github.client:
                scope: 'https://api\.github\.com'
                headers:
                    Accept: 'application/vnd.github.v3+json'
                    Authorization: 'token %env(GITHUB_API_TOKEN)%'
                # ...

            # using base_uri, relative URLs (e.g. request("GET", "/repos/symfony/symfony-docs"))
            # will default to these options
            github.client:
                base_uri: 'https://api.github.com'
                headers:
                    Accept: 'application/vnd.github.v3+json'
                    Authorization: 'token %env(GITHUB_API_TOKEN)%'
                # ...

You can define several scopes, so that each set of options is added only if a requested URL matches one of the regular expressions set by the scope option.

複数のスコープを定義して、要求された URL がスコープ オプションによって設定された正規表現のいずれかに一致する場合にのみ、オプションの各セットが追加されるようにすることができます。

If you use scoped clients in the Symfony framework, you must use any of the methods defined by Symfony to choose a specific service. Each client has a unique service named after its configuration.

Symfony フレームワークでスコープ指定されたクライアントを使用する場合は、Symfony で定義されたメソッドのいずれかを使用して、特定のサービスを選択する必要があります。各クライアントには、構成に基づいて名前が付けられた一意のサービスがあります。

Each scoped client also defines a corresponding named autowiring alias. If you use for example Symfony\Contracts\HttpClient\HttpClientInterface $githubClient as the type and name of an argument, autowiring will inject the github.client service into your autowired classes.

スコープ指定された各クライアントは、対応する名前付きオートワイヤー エイリアスも定義します。たとえば、Symfony\Contracts\HttpClient\HttpClientInterface $githubClient を引数の型と名前として使用すると、オートワイヤーは github.clientservice をオートワイヤーされたクラスに挿入します。

Note

ノート

Read the base_uri option docs to learn the rules applied when merging relative URIs into the base URI of the scoped client.

base_uri オプションのドキュメントを読んで、相対 URI をスコープ指定されたクライアントのベース URI にマージするときに適用されるルールを確認してください。

Making Requests

The HTTP client provides a single request() method to perform all kinds of HTTP requests:

HTTP クライアントは、すべての種類の HTTP 要求を実行する単一の request() メソッドを提供します。
1
2
3
4
5
6
7
8
9
10
11
$response = $client->request('GET', 'https://...');
$response = $client->request('POST', 'https://...');
$response = $client->request('PUT', 'https://...');
// ...

// you can add request options (or override global ones) using the 3rd argument
$response = $client->request('GET', 'https://...', [
    'headers' => [
        'Accept' => 'application/json',
    ],
]);

Responses are always asynchronous, so that the call to the method returns immediately instead of waiting to receive the response:

応答は常に非同期であるため、メソッドへの呼び出しは、応答の受信を待つのではなく、すぐに返されます。
1
2
3
4
5
6
7
8
9
// code execution continues immediately; it doesn't wait to receive the response
$response = $client->request('GET', 'http://releases.ubuntu.com/18.04.2/ubuntu-18.04.2-desktop-amd64.iso');

// getting the response headers waits until they arrive
$contentType = $response->getHeaders()['content-type'][0];

// trying to get the response content will block the execution until
// the full response content is received
$content = $response->getContent();

This component also supports streaming responses for full asynchronous applications.

このコンポーネントは、完全な非同期アプリケーションのストリーミング レスポンスもサポートしています。

Authentication

The HTTP client supports different authentication mechanisms. They can be defined globally in the configuration (to apply it to all requests) and to each request (which overrides any global authentication):

HTTP クライアントは、さまざまな認証メカニズムをサポートしています。それらは、構成でグローバルに定義でき (すべてのリクエストに適用するため)、各リクエストに対して (グローバル認証を上書きします):
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
  • Standalone Use
    スタンドアロン使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# config/packages/framework.yaml
framework:
    http_client:
        scoped_clients:
            example_api:
                base_uri: 'https://example.com/'

                # HTTP Basic authentication
                auth_basic: 'the-username:the-password'

                # HTTP Bearer authentication (also called token authentication)
                auth_bearer: the-bearer-token

                # Microsoft NTLM authentication
                auth_ntlm: 'the-username:the-password'
1
2
3
4
5
6
$response = $client->request('GET', 'https://...', [
    // use a different HTTP Basic authentication only for this request
    'auth_basic' => ['the-username', 'the-password'],

    // ...
]);

Note

ノート

The NTLM authentication mechanism requires using the cURL transport. By using HttpClient::createForBaseUri(), we ensure that the auth credentials won't be sent to any other hosts than https://example.com/.

NTLM 認証メカニズムでは、cURL トランスポートを使用する必要があります。HttpClient::createForBaseUri() を使用することで、認証資格情報が https://example.com/ 以外のホストに送信されないようにします。

Query String Parameters

You can either append them manually to the requested URL, or define them as an associative array via the query option, that will be merged with the URL:

これらを要求された URL に手動で追加するか、クエリ オプションを介して連想配列として定義し、URL とマージすることができます。
1
2
3
4
5
6
7
8
// it makes an HTTP GET request to https://httpbin.org/get?token=...&name=...
$response = $client->request('GET', 'https://httpbin.org/get', [
    // these values are automatically encoded before including them in the URL
    'query' => [
        'token' => '...',
        'name' => '...',
    ],
]);

Headers

Use the headers option to define the default headers added to all requests:

headers オプションを使用して、すべてのリクエストに追加されるデフォルトのヘッダーを定義します。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
  • Standalone Use
    スタンドアロン使用
1
2
3
4
5
6
# config/packages/framework.yaml
framework:
    http_client:
        default_options:
            headers:
                'User-Agent': 'My Fancy App'

You can also set new headers or override the default ones for specific requests:

特定のリクエストに対して、新しいヘッダーを設定したり、デフォルトのヘッダーを上書きしたりすることもできます。
1
2
3
4
5
6
7
// this header is only included in this request and overrides the value
// of the same header if defined globally by the HTTP client
$response = $client->request('POST', 'https://...', [
    'headers' => [
        'Content-Type' => 'text/plain',
    ],
]);

Uploading Data

This component provides several methods for uploading data using the body option. You can use regular strings, closures, iterables and resources and they'll be processed automatically when making the requests:

このコンポーネントは、body オプションを使用してデータをアップロードする方法をいくつか提供します。通常の文字列、クロージャー、イテラブル、およびリソースを使用でき、それらはリクエストを行うときに自動的に処理されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$response = $client->request('POST', 'https://...', [
    // defining data using a regular string
    'body' => 'raw data',

    // defining data using an array of parameters
    'body' => ['parameter1' => 'value1', '...'],

    // using a closure to generate the uploaded data
    'body' => function (int $size): string {
        // ...
    },

    // using a resource to get the data from it
    'body' => fopen('/path/to/file', 'r'),
]);

When uploading data with the POST method, if you don't define the Content-Type HTTP header explicitly, Symfony assumes that you're uploading form data and adds the required 'Content-Type: application/x-www-form-urlencoded' header for you.

POST メソッドでデータをアップロードするとき、Content-Type HTTP ヘッダーを明示的に定義しない場合、Symfony はフォーム データをアップロードしていると想定し、必要な「Content-Type: application/x-www-form-urlencoded」ヘッダーを追加します。あなた。

When the body option is set as a closure, it will be called several times until it returns the empty string, which signals the end of the body. Each time, the closure should return a string smaller than the amount requested as argument.

body オプションがクロージャーとして設定されている場合、ボディの終わりを知らせる空の文字列が返されるまで、何度か呼び出されます。クロージャは毎回、引数として要求された量よりも小さい文字列を返す必要があります。

A generator or any Traversable can also be used instead of a closure.

クロージャの代わりに、ジェネレータまたは任意の Traversable を使用することもできます。

Tip

ヒント

When uploading JSON payloads, use the json option instead of body. The given content will be JSON-encoded automatically and the request will add the Content-Type: application/json automatically too:

JSON ペイロードをアップロードするときは、body の代わりに json オプションを使用します。指定されたコンテンツは自動的に JSON エンコードされ、リクエストは theContent-Type: application/json も自動的に追加します。
1
2
3
4
5
$response = $client->request('POST', 'https://...', [
    'json' => ['param1' => 'value1', '...'],
]);

$decodedPayload = $response->toArray();

To submit a form with file uploads, it is your responsibility to encode the body according to the multipart/form-data content-type. The Symfony Mime component makes it a few lines of code:

ファイルのアップロードでフォームを送信するには、マルチパート/フォーム データのコンテンツ タイプに従って本文をエンコードする必要があります。 Symfony の Mime コンポーネントにより、数行のコードになります。
1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\FormDataPart;

$formFields = [
    'regular_field' => 'some value',
    'file_field' => DataPart::fromPath('/path/to/uploaded/file'),
];
$formData = new FormDataPart($formFields);
$client->request('POST', 'https://...', [
    'headers' => $formData->getPreparedHeaders()->toArray(),
    'body' => $formData->bodyToIterable(),
]);

Tip

ヒント

When using multidimensional arrays the FormDataPart class automatically appends [key] to the name of the field:

多次元配列を使用する場合、FormDataPartclass は自動的に [key] をフィールドの名前に追加します。
1
2
3
4
5
6
7
8
9
$formData = new FormDataPart([
    'array_field' => [
        'some value',
        'other value',
    ],
]);

$formData->getParts(); // Returns two instances of TextPart
                       // with the names "array_field[0]" and "array_field[1]"

This behavior can be bypassed by using the following array structure:

この動作は、次の配列構造を使用してバイパスできます。
1
2
3
4
5
6
7
$formData = new FormDataPart([
    ['array_field' => 'some value'],
    ['array_field' => 'other value'],
]);

$formData->getParts(); // Returns two instances of TextPart both
                       // with the name "array_field"

By default, HttpClient streams the body contents when uploading them. This might not work with all servers, resulting in HTTP status code 411 ("Length Required") because there is no Content-Length header. The solution is to turn the body into a string with the following method (which will increase memory consumption when the streams are large):

デフォルトでは、HttpClient は、アップロード時に本文コンテンツをストリーミングします。 Content-Length ヘッダーがないため、これはすべてのサーバーで機能するとは限らず、HTTP ステータス コード 411 ("Length Required") が返されます。解決策は、次のメソッドを使用して本体を文字列に変換することです (ストリームが大きい場合、メモリ消費量が増加します)。
1
2
3
4
$client->request('POST', 'https://...', [
    // ...
    'body' => $formData->bodyToString(),
]);

If you need to add a custom HTTP header to the upload, you can do:

アップロードにカスタム HTTP ヘッダーを追加する必要がある場合は、次の操作を実行できます。
1
2
$headers = $formData->getPreparedHeaders()->toArray();
$headers[] = 'X-Foo: bar';

Cookies

The HTTP client provided by this component is stateless but handling cookies requires a stateful storage (because responses can update cookies and they must be used for subsequent requests). That's why this component doesn't handle cookies automatically.

このコンポーネントによって提供される HTTP クライアントはステートレスですが、Cookie の処理にはステートフル ストレージが必要です (応答によって Cookie が更新され、後続の要求に使用する必要があるため)。そのため、このコンポーネントは Cookie を自動的に処理しません。

You can either handle cookies yourself using the Cookie HTTP header or use the BrowserKit component which provides this feature and integrates seamlessly with the HttpClient component.

Cookie HTTP ヘッダーを使用して自分で Cookie を処理するか、この機能を提供し、HttpClient コンポーネントとシームレスに統合される BrowserKit コンポーネントを使用できます。

Redirects

By default, the HTTP client follows redirects, up to a maximum of 20, when making a request. Use the max_redirects setting to configure this behavior (if the number of redirects is higher than the configured value, you'll get a RedirectionException):

デフォルトでは、HTTP クライアントは、リクエストを行うときに、最大 20 回のリダイレクトに従います。 max_redirects 設定を使用して、この動作を構成します (リダイレクトの数が構成された値より多い場合は、RedirectionException が発生します)。
1
2
3
4
$response = $client->request('GET', 'https://...', [
    // 0 means to not follow any redirect
    'max_redirects' => 0,
]);

Retry Failed Requests

Sometimes, requests fail because of network issues or temporary server errors. Symfony's HttpClient allows to retry failed requests automatically using the retry_failed option.

ネットワークの問題や一時的なサーバー エラーが原因で、リクエストが失敗することがあります。Symfony の HttpClient では、retry_failed オプションを使用して、失敗したリクエストを自動的に再試行できます。

By default, failed requests are retried up to 3 times, with an exponential delay between retries (first retry = 1 second; third retry: 4 seconds) and only for the following HTTP status codes: 423, 425, 429, 502 and 503 when using any HTTP method and 500, 504, 507 and 510 when using an HTTP idempotent method.

デフォルトでは、失敗したリクエストは最大 3 回再試行され、再試行の間に指数関数的な遅延 (最初の再試行 = 1 秒; 3 回目の再試行: 4 秒) があり、次の HTTP ステータス コードに対してのみ: 423、425、429、502、および 503 (任意の HTTP を使用する場合)メソッドと、HTTP べき等メソッドを使用する場合の 500、504、507、および 510。

Check out the full list of configurable retry_failed options to learn how to tweak each of them to fit your application needs.

構成可能な retry_failed オプションの完全なリストを確認して、アプリケーションのニーズに合わせてそれぞれを微調整する方法を学んでください。

When using the HttpClient outside of a Symfony application, use the RetryableHttpClient class to wrap your original HTTP client:

Symfony アプリケーションの外部で HttpClient を使用する場合、RetryableHttpClient クラスを使用して元の HTTP クライアントをラップします。
1
2
3
use Symfony\Component\HttpClient\RetryableHttpClient;

$client = new RetryableHttpClient(HttpClient::create());

The RetryableHttpClient uses a RetryStrategyInterface to decide if the request should be retried, and to define the waiting time between each retry.

RetryableHttpClient は、RetryStrategyInterface を使用して、リクエストを再試行する必要があるかどうかを決定し、各再試行間の待機時間を定義します。

HTTP Proxies

By default, this component honors the standard environment variables that your Operating System defines to direct the HTTP traffic through your local proxy. This means there is usually nothing to configure to have the client work with proxies, provided these env vars are properly configured.

デフォルトでは、このコンポーネントは、ローカル プロキシを介して HTTP トラフィックを送信するためにオペレーティング システムが定義する標準の環境変数を受け入れます。これは、これらの環境変数が適切に設定されていれば、通常、クライアントがプロキシと連携するように設定する必要がないことを意味します。

You can still set or override these settings using the proxy and no_proxy options:

これらの設定は、proxy および no_proxyoptions を使用して引き続き設定またはオーバーライドできます。
  • proxy should be set to the http://... URL of the proxy to get through
    プロキシは、通過するプロキシの http://... URL に設定する必要があります。
  • no_proxy disables the proxy for a comma-separated list of hosts that do not require it to get reached.
    no_proxy は、到達する必要のないホストのコンマ区切りリストのプロキシを無効にします。

Progress Callback

By providing a callable to the on_progress option, one can track uploads/downloads as they complete. This callback is guaranteed to be called on DNS resolution, on arrival of headers and on completion; additionally it is called when new data is uploaded or downloaded and at least once per second:

on_progress オプションに callable を提供することで、完了したアップロード/ダウンロードを追跡できます。このコールバックは、onDNS 解決、ヘッダーの到着時、および完了時に呼び出されることが保証されています。さらに、新しいデータがアップロードまたはダウンロードされたときに、少なくとも 1 秒に 1 回呼び出されます。
1
2
3
4
5
6
7
$response = $client->request('GET', 'https://...', [
    'on_progress' => function (int $dlNow, int $dlSize, array $info): void {
        // $dlNow is the number of bytes downloaded so far
        // $dlSize is the total size to be downloaded or -1 if it is unknown
        // $info is what $response->getInfo() would return at this very time
    },
]);

Any exceptions thrown from the callback will be wrapped in an instance of TransportExceptionInterface and will abort the request.

コールバックからスローされたすべての例外は、TransportExceptionInterface のインスタンスにラップされ、リクエストを中止します。

HTTPS Certificates

HttpClient uses the system's certificate store to validate SSL certificates (while browsers use their own stores). When using self-signed certificates during development, it's recommended to create your own certificate authority (CA) and add it to your system's store.

HttpClient はシステムの証明書ストアを使用して SSL 証明書を検証します (ブラウザーは独自のストアを使用します)。開発中に自己署名証明書を使用する場合は、独自の証明機関 (CA) を作成してシステムのストアに追加することをお勧めします。

Alternatively, you can also disable verify_host and verify_peer (see http_client config reference), but this is not recommended in production.

または、verify_host と verify_peer を無効にすることもできますが (http_client 構成リファレンスを参照)、これは本番環境では推奨されません。

SSRF (Server-side request forgery) Handling

SSRF allows an attacker to induce the backend application to make HTTP requests to an arbitrary domain. These attacks can also target the internal hosts and IPs of the attacked server.

SSRF を使用すると、攻撃者はバックエンド アプリケーションを誘導して、任意のドメインに HTTP リクエストを送信できます。これらの攻撃は、攻撃されたサーバーの内部ホストと IP を標的にすることもできます。

If you use an HttpClient together with user-provided URIs, it is probably a good idea to decorate it with a NoPrivateNetworkHttpClient. This will ensure local networks are made inaccessible to the HTTP client:

HttpClient をユーザー提供の URI と一緒に使用する場合は、NoPrivateNetworkHttpClient で装飾することをお勧めします。これにより、HTTP クライアントがローカル ネットワークにアクセスできなくなります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\NoPrivateNetworkHttpClient;

$client = new NoPrivateNetworkHttpClient(HttpClient::create());
// nothing changes when requesting public networks
$client->request('GET', 'https://example.com/');

// however, all requests to private networks are now blocked by default
$client->request('GET', 'http://localhost/');

// the second optional argument defines the networks to block
// in this example, requests from 104.26.14.0 to 104.26.15.255 will result in an exception
// but all the other requests, including other internal networks, will be allowed
$client = new NoPrivateNetworkHttpClient(HttpClient::create(), ['104.26.14.0/23']);

Profiling

When you are using the TraceableHttpClient, responses content will be kept in memory and may exhaust it.

TraceableHttpClient を使用している場合、応答コンテンツはメモリに保持され、使い果たされる可能性があります。

You can disable this behavior by setting the extra.trace_content option to false in your requests:

この動作を無効にするには、リクエストで extra.trace_content オプションを false に設定します。
1
2
3
$response = $client->request('GET', 'https://...', [
    'extra' => ['trace_content' => false],
]);

This setting won’t affect other clients.

この設定は他のクライアントには影響しません。

Performance

The component is built for maximum HTTP performance. By design, it is compatible with HTTP/2 and with doing concurrent asynchronous streamed and multiplexed requests/responses. Even when doing regular synchronous calls, this design allows keeping connections to remote hosts open between requests, improving performance by saving repetitive DNS resolution, SSL negotiation, etc. To leverage all these design benefits, the cURL extension is needed.

このコンポーネントは、HTTP パフォーマンスを最大化するように構築されています。設計上、HTTP/2 と互換性があり、非同期ストリーミングおよび多重化された要求/応答を同時に実行できます。通常の同期呼び出しを行う場合でも、この設計により、要求間でリモート ホストへの接続を開いたままにしておくことができ、反復的な DNS 解決、SSL ネゴシエーションなどを節約することでパフォーマンスが向上します。これらすべての設計上の利点を活用するには、cURL 拡張が必要です。

Enabling cURL Support

This component supports both the native PHP streams and cURL to make the HTTP requests. Although both are interchangeable and provide the same features, including concurrent requests, HTTP/2 is only supported when using cURL.

このコンポーネントは、ネイティブ PHP ストリームと cURL の両方をサポートして HTTP リクエストを作成します。どちらも互換性があり、同時要求を含む同じ機能を提供しますが、HTTP/2 は cURL を使用する場合にのみサポートされます。

HttpClient::create() selects the cURL transport if the cURL PHP extension is enabled and falls back to PHP streams otherwise. If you prefer to select the transport explicitly, use the following classes to create the client:

HttpClient::create() は、cURL PHP 拡張機能が有効になっている場合は cURL トランスポートを選択し、それ以外の場合は PHP ストリームにフォールバックします。トランスポートを明示的に選択する場合は、次のクラスを使用してクライアントを作成します。
1
2
3
4
5
6
7
8
use Symfony\Component\HttpClient\CurlHttpClient;
use Symfony\Component\HttpClient\NativeHttpClient;

// uses native PHP streams
$client = new NativeHttpClient();

// uses the cURL PHP extension
$client = new CurlHttpClient();

When using this component in a full-stack Symfony application, this behavior is not configurable and cURL will be used automatically if the cURL PHP extension is installed and enabled. Otherwise, the native PHP streams will be used.

このコンポーネントをフルスタックの Symfony アプリケーションで使用する場合、この動作は構成できず、cURL PHP 拡張機能がインストールされて有効になっている場合、cURL が自動的に使用されます。それ以外の場合は、ネイティブ PHP ストリームが使用されます。

Configuring CurlHttpClient Options

PHP allows to configure lots of cURL options via the curl_setopt function. In order to make the component more portable when not using cURL, the CurlHttpClient only uses some of those options (and they are ignored in the rest of clients).

PHP では、curl_setopt 関数を使用して多くの cURL オプションを構成できます。 cURL を使用しない場合にコンポーネントの移植性を高めるために、theCurlHttpClient はこれらのオプションの一部のみを使用します (残りのクライアントでは無視されます)。

Add an extra.curl option in your configuration to pass those extra options:

これらの追加オプションを渡すには、構成に extra.curl オプションを追加します。
1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\HttpClient\CurlHttpClient;

$client = new CurlHttpClient();

$client->request('POST', 'https://...', [
    // ...
    'extra' => [
        'curl' => [
            CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V6,
        ],
    ],
]);

Note

ノート

Some cURL options are impossible to override (e.g. because of thread safety) and you'll get an exception when trying to override them.

一部の cURL オプションは (スレッド セーフなどの理由で) 上書きすることができず、それらを上書きしようとすると例外が発生します。

HTTP Compression

The HTTP header Accept-Encoding: gzip is added automatically if:

HTTP ヘッダー Accept-Encoding: gzip は、次の場合に自動的に追加されます。
  • When using cURL client: cURL was compiled with ZLib support (see php --ri curl)
    cURL クライアントを使用する場合: cURL は ZLib サポート付きでコンパイルされました (php --ri curl を参照)。
  • When using the native HTTP client: Zlib PHP extension is installed
    ネイティブ HTTP クライアントを使用する場合: Zlib PHP 拡張機能がインストールされている

If the server does respond with a gzipped response, it's decoded transparently. To disable HTTP compression, send an Accept-Encoding: identity HTTP header.

サーバーが gzip 圧縮された応答で応答しない場合は、透過的にデコードされます。HTTP 圧縮を無効にするには、Accept-Encoding: identity HTTP ヘッダーを送信します。

Chunked transfer encoding is enabled automatically if both your PHP runtime and the remote server supports it.

PHP ランタイムとリモート サーバーの両方がチャンク転送エンコーディングをサポートしている場合、チャンク転送エンコーディングは自動的に有効になります。

HTTP/2 Support

When requesting an https URL, HTTP/2 is enabled by default if one of the following tools is installed:

次のツールのいずれかがインストールされている場合、https URL を要求するとき、デフォルトで HTTP/2 が有効になります。
  • The libcurl package version 7.36 or higher;
    libcurl パッケージ バージョン 7.36 以降。
  • The amphp/http-client Packagist package version 4.2 or higher.
    amphp/http-client Packagist パッケージ バージョン 4.2 以降。

To force HTTP/2 for http URLs, you need to enable it explicitly via the http_version option:

http URL に対して HTTP/2 を強制するには、http_version オプションを使用して明示的に有効にする必要があります。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
  • Standalone Use
    スタンドアロン使用
1
2
3
4
5
# config/packages/framework.yaml
framework:
    http_client:
        default_options:
            http_version: '2.0'

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

libcurl >= 7.61 が PHP >= 7.2.17 / 7.3.4 で使用されている場合、HTTP/2 PUSH のサポートはすぐに機能します。プッシュされた応答は一時キャッシュに入れられ、対応する URL に対して後続の要求がトリガーされたときに使用されます。

Processing Responses

The response returned by all HTTP clients is an object of type ResponseInterface which provides the following methods:

すべての HTTP クライアントから返される応答は、次のメソッドを提供する typeResponseInterface のオブジェクトです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
$response = $client->request('GET', 'https://...');

// gets the HTTP status code of the response
$statusCode = $response->getStatusCode();

// gets the HTTP headers as string[][] with the header names lower-cased
$headers = $response->getHeaders();

// gets the response body as a string
$content = $response->getContent();

// casts the response JSON content to a PHP array
$content = $response->toArray();

// casts the response content to a PHP stream resource
$content = $response->toStream();

// cancels the request/response
$response->cancel();

// returns info coming from the transport layer, such as "response_headers",
// "redirect_count", "start_time", "redirect_url", etc.
$httpInfo = $response->getInfo();

// you can get individual info too
$startTime = $response->getInfo('start_time');
// e.g. this returns the final response URL (resolving redirections if needed)
$url = $response->getInfo('url');

// returns detailed logs about the requests and responses of the HTTP transaction
$httpLogs = $response->getInfo('debug');

Note

ノート

$response->getInfo() is non-blocking: it returns live information about the response. Some of them might not be known yet (e.g. http_code) when you'll call it.

$response->getInfo() はノンブロッキングです: レスポンスに関するライブ情報を返します。それらのいくつかは、呼び出すときにまだわからない場合があります (例: http_code)。

Streaming Responses

Call the stream() method of the HTTP client to get chunks of the response sequentially instead of waiting for the entire response:

HTTP クライアントの stream() メソッドを呼び出して、応答全体を待つのではなく、応答のチャンクを順番に取得します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$url = 'https://releases.ubuntu.com/18.04.1/ubuntu-18.04.1-desktop-amd64.iso';
$response = $client->request('GET', $url);

// Responses are lazy: this code is executed as soon as headers are received
if (200 !== $response->getStatusCode()) {
    throw new \Exception('...');
}

// get the response content in chunks and save them in a file
// response chunks implement Symfony\Contracts\HttpClient\ChunkInterface
$fileHandler = fopen('/ubuntu.iso', 'w');
foreach ($client->stream($response) as $chunk) {
    fwrite($fileHandler, $chunk->getContent());
}

Note

ノート

By default, text/*, JSON and XML response bodies are buffered in a local php://temp stream. You can control this behavior by using the buffer option: set it to true/false to enable/disable buffering, or to a closure that should return the same based on the response headers it receives as an argument.

デフォルトでは、text/*、JSON、および XML の応答本文は localphp://temp ストリームにバッファリングされます。この動作は、buffer オプションを使用して制御できます。これを true/false に設定してバッファリングを有効/無効にするか、引数として受信した応答ヘッダーに基づいて同じものを返す aclosure に設定します。

Canceling Responses

To abort a request (e.g. because it didn't complete in due time, or you want to fetch only the first bytes of the response, etc.), you can either use the cancel() method of ResponseInterface:

リクエストを中止するには (たとえば、期限内に完了しなかった、またはレスポンスの最初のバイトのみを取得したいなどの理由で)、ResponseInterface の cancel() メソッドを使用できます。
1
$response->cancel();

Or throw an exception from a progress callback:

または、進行状況のコールバックから例外をスローします。
1
2
3
4
5
6
7
$response = $client->request('GET', 'https://...', [
    'on_progress' => function (int $dlNow, int $dlSize, array $info): void {
        // ...

        throw new \MyException();
    },
]);

The exception will be wrapped in an instance of TransportExceptionInterface and will abort the request.

例外は TransportExceptionInterface のインスタンスにラップされ、リクエストを中止します。

In case the response was canceled using $response->cancel(), $response->getInfo('canceled') will return true.

$response->cancel() を使用して応答がキャンセルされた場合、$response->getInfo('canceled') は true を返します。

Handling Exceptions

There are three types of exceptions, all of which implement the ExceptionInterface:

例外には 3 つのタイプがあり、そのすべてが theExceptionInterface を実装しています。
  • Exceptions implementing the HttpExceptionInterface are thrown when your code does not handle the status codes in the 300-599 range.
    コードが 300 ~ 599 の範囲のステータス コードを処理しない場合、HttpExceptionInterface を実装する例外がスローされます。
  • Exceptions implementing the TransportExceptionInterface are thrown when a lower level issue occurs.
    低レベルの問題が発生すると、TransportExceptionInterfaceare を実装する例外がスローされます。
  • Exceptions implementing the DecodingExceptionInterface are thrown when a content-type cannot be decoded to the expected representation.
    コンテンツ タイプを期待される表現にデコードできない場合、DecodingExceptionInterface を実装する例外がスローされます。

When the HTTP status code of the response is in the 300-599 range (i.e. 3xx, 4xx or 5xx), the getHeaders(), getContent() and toArray() methods throw an appropriate exception, all of which implement the HttpExceptionInterface.

応答の HTTP ステータス コードが 300 ~ 599 の範囲 (つまり、3xx、4xx、または 5xx) の場合、getHeaders()、getContent()、および toArray() メソッドは適切な例外をスローします。これらはすべて、HttpExceptionInterface を実装します。

To opt-out from this exception and deal with 300-599 status codes on your own, pass false as the optional argument to every call of those methods, e.g. $response->getHeaders(false);.

この例外からオプトアウトし、自分で 300-599 ステータス コードを処理するには、これらのメソッドのすべての呼び出しにオプションの引数として false を渡します。 $response->getHeaders(false);.

If you do not call any of these 3 methods at all, the exception will still be thrown when the $response object is destructed.

これら 3 つのメソッドをまったく呼び出さない場合でも、$response オブジェクトが破棄されるときに例外がスローされます。

Calling $response->getStatusCode() is enough to disable this behavior (but then don't miss checking the status code yourself).

$response->getStatusCode() を呼び出すだけで、この動作を無効にできます (ただし、自分でステータス コードをチェックすることをお忘れなく)。

While responses are lazy, their destructor will always wait for headers to come back. This means that the following request will complete; and if e.g. a 404 is returned, an exception will be thrown:

応答は遅延しますが、デストラクタは常にヘッダーが戻ってくるのを待ちます。これは、次のリクエストが完了することを意味します。そして、例えば404 が返されると、例外がスローされます。
1
2
3
4
// because the returned value is not assigned to a variable, the destructor
// of the returned response will be called immediately and will throw if the
// status code is in the 300-599 range
$client->request('POST', 'https://...');

This in turn means that unassigned responses will fallback to synchronous requests. If you want to make these requests concurrent, you can store their corresponding responses in an array:

これは、割り当てられていない応答が同期要求にフォールバックすることを意味します。これらの要求を同時に実行したい場合は、対応する応答を配列に格納できます。
1
2
3
4
5
6
7
8
$responses[] = $client->request('POST', 'https://.../path1');
$responses[] = $client->request('POST', 'https://.../path2');
// ...

// This line will trigger the destructor of all responses stored in the array;
// they will complete concurrently and an exception will be thrown in case a
// status code in the 300-599 range is returned
unset($responses);

This behavior provided at destruction-time is part of the fail-safe design of the component. No errors will be unnoticed: if you don't write the code to handle errors, exceptions will notify you when needed. On the other hand, if you write the error-handling code (by calling $response->getStatusCode()), you will opt-out from these fallback mechanisms as the destructor won't have anything remaining to do.

破壊時に提供されるこの動作は、コンポーネントのフェイルセーフ設計の一部です。エラーが見過ごされることはありません。エラーを処理するコードを書かないと、必要なときに例外が通知されます。一方、($response->getStatusCode() を呼び出して) エラー処理コードを記述する場合は、デストラクタが何もする必要がないため、これらのフォールバック メカニズムからオプトアウトします。

Concurrent Requests

Thanks to responses being lazy, requests are always managed concurrently. On a fast enough network, the following code makes 379 requests in less than half a second when cURL is used:

応答が遅延しているため、要求は常に同時に処理されます。十分に高速なネットワークでは、cURL を使用すると、次のコードで 379 の要求が 0.5 秒未満で作成されます。
1
2
3
4
5
6
7
8
9
10
$responses = [];
for ($i = 0; $i < 379; ++$i) {
    $uri = "https://http2.akamai.com/demo/tile-$i.png";
    $responses[] = $client->request('GET', $uri);
}

foreach ($responses as $response) {
    $content = $response->getContent();
    // ...
}

As you can read in the first "for" loop, requests are issued but are not consumed yet. That's the trick when concurrency is desired: requests should be sent first and be read later on. This will allow the client to monitor all pending requests while your code waits for a specific one, as done in each iteration of the above "foreach" loop.

最初の「for」ループで読み取れるように、リクエストは発行されますが、まだ消費されていません。これは、同時実行が必要な場合の秘訣です。リクエストは最初に送信され、後で読み取られる必要があります。これにより、上記の「foreach」ループの各反復で行われるように、コードが特定のリクエストを待機している間、クライアントはすべての保留中のリクエストを監視できます。

Note

ノート

The maximum number of concurrent requests that you can perform depends on the resources of your machine (e.g. your operating system may limit the number of simultaneous reads of the file that stores the certificates file). Make your requests in batches to avoid these issues.

実行できる同時要求の最大数は、マシンのリソースによって異なります (たとえば、オペレーティング システムでは、証明書ファイルを格納するファイルの同時読み取り数が制限されている場合があります)。これらの問題を回避するには、リクエストをバッチで行います。

Multiplexing Responses

If you look again at the snippet above, responses are read in requests' order. But maybe the 2nd response came back before the 1st? Fully asynchronous operations require being able to deal with the responses in whatever order they come back.

上記のスニペットをもう一度見てみると、応答は要求の順序で読み取られます。完全な非同期操作では、応答が返された順序に関係なく処理できる必要があります。

In order to do so, the stream() method of HTTP clients accepts a list of responses to monitor. As mentioned previously, this method yields response chunks as they arrive from the network. By replacing the "foreach" in the snippet with this one, the code becomes fully async:

そのために、HTTP クライアントの stream() メソッドは、監視する応答のリストを受け入れます。前述のように、このメソッドは、ネットワークから到着した応答チャンクを生成します。スニペットの「foreach」を次のコードに置き換えると、コードは完全に非同期になります。
1
2
3
4
5
6
7
8
9
10
11
12
foreach ($client->stream($responses) as $response => $chunk) {
    if ($chunk->isFirst()) {
        // headers of $response just arrived
        // $response->getHeaders() is now a non-blocking call
    } elseif ($chunk->isLast()) {
        // the full content of $response just completed
        // $response->getContent() is now a non-blocking call
    } else {
        // $chunk->getContent() will return a piece
        // of the response body that just arrived
    }
}

Tip

ヒント

Use the user_data option combined with $response->getInfo('user_data') to track the identity of the responses in your foreach loops.

$response->getInfo('user_data') と組み合わせた user_data オプションを使用して、foreach ループで応答の ID を追跡します。

Dealing with Network Timeouts

This component allows dealing with both request and response timeouts.

このコンポーネントを使用すると、リクエスト タイムアウトとレスポンス タイムアウトの両方を処理できます。

A timeout can happen when e.g. DNS resolution takes too much time, when the TCP connection cannot be opened in the given time budget, or when the response content pauses for too long. This can be configured with the timeout request option:

タイムアウトは、たとえば次の場合に発生する可能性があります。指定された時間内に TCP 接続を開くことができない場合、または応答コンテンツの一時停止が長すぎる場合、DNS 解決に時間がかかりすぎます。これは、タイムアウト リクエスト オプションで設定できます。
1
2
3
// A TransportExceptionInterface will be issued if nothing
// happens for 2.5 seconds when accessing from the $response
$response = $client->request('GET', 'https://...', ['timeout' => 2.5]);

The default_socket_timeout PHP ini setting is used if the option is not set.

オプションが設定されていない場合、default_socket_timeout PHP ini 設定が使用されます。

The option can be overridden by using the 2nd argument of the stream() method. This allows monitoring several responses at once and applying the timeout to all of them in a group. If all responses become inactive for the given duration, the method will yield a special chunk whose isTimeout() will return true:

このオプションは、stream() メソッドの 2 番目の引数を使用してオーバーライドできます。これにより、一度に複数の応答を監視し、グループ内のすべての応答にタイムアウトを適用できます。指定された期間、すべての応答が非アクティブになった場合、メソッドは isTimeout() が true を返す特別なチャンクを生成します。
1
2
3
4
5
foreach ($client->stream($responses, 1.5) as $response => $chunk) {
    if ($chunk->isTimeout()) {
        // $response stale for more than 1.5 seconds
    }
}

A timeout is not necessarily an error: you can decide to stream again the response and get remaining contents that might come back in a new timeout, etc.

タイムアウトは必ずしもエラーではありません。応答を再度ストリーミングし、新しいタイムアウトで戻ってくる可能性のある残りのコンテンツを取得することもできます。

Tip

ヒント

Passing 0 as timeout allows monitoring responses in a non-blocking way.

タイムアウトとして 0 を渡すと、非ブロッキングの方法で応答を監視できます。

Note

ノート

Timeouts control how long one is willing to wait while the HTTP transaction is idle. Big responses can last as long as needed to complete, provided they remain active during the transfer and never pause for longer than specified.

タイムアウトは、HTTP トランザクションがアイドル状態のときに待機できる時間を制御します。ビッグ レスポンスは、転送中にアクティブな状態を維持し、指定された時間よりも長く一時停止しない限り、完了するまで必要なだけ持続できます。

Use the max_duration option to limit the time a full request/response can last.

max_duration オプションを使用して、完全なリクエスト/レスポンスが継続できる時間を制限します。

Dealing with Network Errors

Network errors (broken pipe, failed DNS resolution, etc.) are thrown as instances of TransportExceptionInterface.

ネットワーク エラー (パイプの破損、DNS 解決の失敗など) は、TransportExceptionInterface のインスタンスとしてスローされます。

First of all, you don't have to deal with them: letting errors bubble to your generic exception-handling stack might be really fine in most use cases.

まず第一に、それらに対処する必要はありません。エラーを一般的な例外処理スタックにバブルさせることは、ほとんどのユース ケースで本当に問題ない場合があります。

If you want to handle them, here is what you need to know:

それらを処理したい場合は、次のことを知っておく必要があります。

To catch errors, you need to wrap calls to $client->request() but also calls to any methods of the returned responses. This is because responses are lazy, so that network errors can happen when calling e.g. getStatusCode() too:

エラーをキャッチするには、$client->request() への呼び出しだけでなく、返された応答のメソッドへの呼び出しもラップする必要があります。これは、応答が遅延しているためです。たとえば、呼び出し時にネットワーク エラーが発生する可能性があります。 getStatusCode() も:
1
2
3
4
5
6
7
8
9
10
11
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;

// ...
try {
    // both lines can potentially throw
    $response = $client->request(/* ... */);
    $headers = $response->getHeaders();
    // ...
} catch (TransportExceptionInterface $e) {
    // ...
}

Note

ノート

Because $response->getInfo() is non-blocking, it shouldn't throw by design.

$response->getInfo() はノンブロッキングであるため、意図的にスローされるべきではありません。

When multiplexing responses, you can deal with errors for individual streams by catching TransportExceptionInterface in the foreach loop:

応答を多重化する場合、foreach ループで TransportExceptionInterface をキャッチすることにより、個々のストリームのエラーを処理できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
foreach ($client->stream($responses) as $response => $chunk) {
    try {
        if ($chunk->isTimeout()) {
            // ... decide what to do when a timeout occurs
            // if you want to stop a response that timed out, don't miss
            // calling $response->cancel() or the destructor of the response
            // will try to complete it one more time
        } elseif ($chunk->isFirst()) {
            // if you want to check the status code, you must do it when the
            // first chunk arrived, using $response->getStatusCode();
            // not doing so might trigger an HttpExceptionInterface
        } elseif ($chunk->isLast()) {
            // ... do something with $response
        }
    } catch (TransportExceptionInterface $e) {
        // ...
    }
}

Caching Requests and Responses

This component provides a CachingHttpClient decorator that allows caching responses and serving them from the local storage for next requests. The implementation leverages the HttpCache class under the hood so that the HttpKernel component needs to be installed in your application:

このコンポーネントは、応答をキャッシュし、次の要求のためにローカル ストレージからそれらを提供できるようにする CachingHttpClientdecorator を提供します。実装は内部で HttpCache クラスを利用するため、HttpKernel コンポーネントをアプリケーションにインストールする必要があります。
1
2
3
4
5
6
7
8
9
10
use Symfony\Component\HttpClient\CachingHttpClient;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpKernel\HttpCache\Store;

$store = new Store('/path/to/cache/storage/');
$client = HttpClient::create();
$client = new CachingHttpClient($client, $store);

// this won't hit the network if the resource is already in the cache
$response = $client->request('GET', 'https://example.com/cacheable-resource');

CachingHttpClient accepts a third argument to set the options of the HttpCache.

CachingHttpClient は、HttpCache のオプションを設定する 3 番目の引数を受け入れます。

Consuming Server-Sent Events

Server-sent events is an Internet standard used to push data to web pages. Its JavaScript API is built around an EventSource object, which listens to the events sent from some URL. The events are a stream of data (served with the text/event-stream MIME type) with the following format:

サーバー送信イベントは、Web ページにデータをプッシュするために使用されるインターネット標準です。その JavaScript API は、何らかの URL から送信されたイベントをリッスンする EventSource オブジェクトを中心に構築されています。イベントは、次の形式のデータ ストリームです (text/event-stream MIME タイプで提供されます)。
1
2
3
4
5
6
data: This is the first message.

data: This is the second message, it
data: has two lines.

data: This is the third message.

Symfony's HTTP client provides an EventSource implementation to consume these server-sent events. Use the EventSourceHttpClient to wrap your HTTP client, open a connection to a server that responds with a text/event-stream content type and consume the stream as follows:

Symfony の HTTP クライアントは、これらのサーバー送信イベントを消費するための EventSource 実装を提供します。 EventSourceHttpClient を使用して HTTP クライアントをラップし、text/event-stream コンテンツ タイプで応答するサーバーへの接続を開き、次のようにストリームを消費します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use Symfony\Component\HttpClient\Chunk\ServerSentEvent;
use Symfony\Component\HttpClient\EventSourceHttpClient;

// the second optional argument is the reconnection time in seconds (default = 10)
$client = new EventSourceHttpClient($client, 10);
$source = $client->connect('https://localhost:8080/events');
while ($source) {
    foreach ($client->stream($source, 2) as $r => $chunk) {
        if ($chunk->isTimeout()) {
            // ...
            continue;
        }

        if ($chunk->isLast()) {
            // ...

            return;
        }

        // this is a special ServerSentEvent chunk holding the pushed message
        if ($chunk instanceof ServerSentEvent) {
            // do something with the server event ...
        }
    }
}

Interoperability

The component is interoperable with four different abstractions for HTTP clients: Symfony Contracts, PSR-18, HTTPlug v1/v2 and native PHP streams. If your application uses libraries that need any of them, the component is compatible with all of them. They also benefit from autowiring aliases when the framework bundle is used.

このコンポーネントは、HTTP クライアントの 4 つの異なる抽象化 (Symfony コントラクト、PSR-18、HTTPlug v1/v2、およびネイティブ PHP ストリーム) と相互運用できます。アプリケーションがそれらのいずれかを必要とするライブラリを使用している場合、コンポーネントはそれらすべてと互換性があります。また、フレームワーク バンドルが使用されている場合は、オートワイヤー エイリアスの恩恵も受けます。

If you are writing or maintaining a library that makes HTTP requests, you can decouple it from any specific HTTP client implementations by coding against either Symfony Contracts (recommended), PSR-18 or HTTPlug v2.

HTTP リクエストを作成するライブラリを作成または維持している場合は、Symfony コントラクト (推奨)、PSR-18、または HTTPlug v2 のいずれかに対してコーディングすることで、特定の HTTP クライアント実装からライブラリを切り離すことができます。

Symfony Contracts

The interfaces found in the symfony/http-client-contracts package define the primary abstractions implemented by the component. Its entry point is the HttpClientInterface. That's the interface you need to code against when a client is needed:

symfony/http-client-contracts パッケージにあるインターフェースは、コンポーネントによって実装される主要な抽象化を定義します。そのエントリ ポイントは、HttpClientInterface です。これは、クライアントが必要なときにコーディングする必要があるインターフェイスです。
1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Contracts\HttpClient\HttpClientInterface;

class MyApiLayer
{
    private $client;

    public function __construct(HttpClientInterface $client)
    {
        $this->client = $client;
    }

    // [...]
}

All request options mentioned above (e.g. timeout management) are also defined in the wordings of the interface, so that any compliant implementations (like this component) is guaranteed to provide them. That's a major difference with the other abstractions, which provide none related to the transport itself.

上記のすべての要求オプション (タイムアウト管理など) は、インターフェイスの文言でも定義されているため、準拠した実装 (このコンポーネントなど) はそれらを提供することが保証されています。これは、トランスポート自体に関連するものを何も提供しない他の抽象化との大きな違いです。

Another major feature covered by the Symfony Contracts is async/multiplexing, as described in the previous sections.

前のセクションで説明したように、Symfony コントラクトでカバーされるもう 1 つの主要な機能は、非同期/多重化です。

PSR-18 and PSR-17

This component implements the PSR-18 (HTTP Client) specifications via the Psr18Client class, which is an adapter to turn a Symfony HttpClientInterface into a PSR-18 ClientInterface. This class also implements the relevant methods of PSR-17 to ease creating request objects.

このコンポーネントは、Symfony HttpClientInterface を PSR-18 ClientInterface に変換するアダプターである Psr18Client クラスを介して PSR-18 (HTTP クライアント) 仕様を実装します。このクラスは、要求オブジェクトの作成を容易にするために PSR-17 の関連メソッドも実装します。

To use it, you need the psr/http-client package and a PSR-17 implementation:

これを使用するには、psr/http-client パッケージと PSR-17 実装が必要です。
1
2
3
4
5
6
7
8
9
10
# installs the PSR-18 ClientInterface
$ composer require psr/http-client

# installs an efficient implementation of response and stream factories
# with autowiring aliases provided by Symfony Flex
$ composer require nyholm/psr7

# alternatively, install the php-http/discovery package to auto-discover
# any already installed implementations from common vendors:
# composer require php-http/discovery

Now you can make HTTP requests with the PSR-18 client as follows:

これで、次のように PSR-18 クライアントで HTTP リクエストを作成できます。
  • Framework Use
    フレームワークの使用
  • Standalone Use
    スタンドアロン使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use Psr\Http\Client\ClientInterface;

class Symfony
{
    private $client;

    public function __construct(ClientInterface $client)
    {
        $this->client = $client;
    }

    public function getAvailableVersions(): array
    {
        $request = $this->client->createRequest('GET', 'https://symfony.com/versions.json');
        $response = $this->client->sendRequest($request);

        return json_decode($response->getBody()->getContents(), true);
    }
}

HTTPlug

The HTTPlug v1 specification was published before PSR-18 and is superseded by it. As such, you should not use it in newly written code. The component is still interoperable with libraries that require it thanks to the HttplugClient class. Similarly to Psr18Client implementing relevant parts of PSR-17, HttplugClient also implements the factory methods defined in the related php-http/message-factory package.

HTTPlug v1 仕様は PSR-18 より前に公開されており、PSR-18 に取って代わられています。そのため、新しく記述されたコードでは使用しないでください。コンポーネントは、HttplugClient クラスのおかげで、コンポーネントを必要とするライブラリと相互運用できます。 PSR-17 の関連部分を実装する Psr18Client と同様に、HttplugClient も、関連する php-http/message-factory パッケージで定義されたファクトリ メソッドを実装します。
1
2
3
4
5
6
7
8
9
# Let's suppose php-http/httplug is already required by the lib you want to use

# installs an efficient implementation of response and stream factories
# with autowiring aliases provided by Symfony Flex
$ composer require nyholm/psr7

# alternatively, install the php-http/discovery package to auto-discover
# any already installed implementations from common vendors:
# composer require php-http/discovery

Let's say you want to instantiate a class with the following constructor, that requires HTTPlug dependencies:

HTTPlug 依存関係を必要とする次のコンストラクターを使用してクラスをインスタンス化するとします。
1
2
3
4
5
6
7
8
9
10
11
12
13
use Http\Client\HttpClient;
use Http\Message\RequestFactory;
use Http\Message\StreamFactory;

class SomeSdk
{
    public function __construct(
        HttpClient $httpClient,
        RequestFactory $requestFactory,
        StreamFactory $streamFactory
    )
    // [...]
}

Because HttplugClient implements the three interfaces, you can use it this way:

HttplugClient は 3 つのインターフェイスを実装しているため、次のように使用できます。
1
2
3
4
use Symfony\Component\HttpClient\HttplugClient;

$httpClient = new HttplugClient();
$apiClient = new SomeSdk($httpClient, $httpClient, $httpClient);

If you'd like to work with promises, HttplugClient also implements the HttpAsyncClient interface. To use it, you need to install the guzzlehttp/promises package:

Promise を使用する場合、HttplugClient は HttpAsyncClient インターフェイスも実装します。これを使用するには、guzzlehttp/promises パッケージをインストールする必要があります。
1
$ composer require guzzlehttp/promises

Then you're ready to go:

次に、準備が整いました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpClient\HttplugClient;

$httpClient = new HttplugClient();
$request = $httpClient->createRequest('GET', 'https://my.api.com/');
$promise = $httpClient->sendAsyncRequest($request)
    ->then(
        function (ResponseInterface $response) {
            echo 'Got status '.$response->getStatusCode();

            return $response;
        },
        function (\Throwable $exception) {
            echo 'Error: '.$exception->getMessage();

            throw $exception;
        }
    );

// after you're done with sending several requests,
// you must wait for them to complete concurrently

// wait for a specific promise to resolve while monitoring them all
$response = $promise->wait();

// wait maximum 1 second for pending promises to resolve
$httpClient->wait(1.0);

// wait for all remaining promises to resolve
$httpClient->wait();

Native PHP Streams

Responses implementing ResponseInterface can be cast to native PHP streams with createResource(). This allows using them where native PHP streams are needed:

ResponseInterface を実装する応答は、createResource() を使用してネイティブ PHP ストリームにキャストできます。これにより、ネイティブ PHP ストリームが必要な場所でそれらを使用できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\Response\StreamWrapper;

$client = HttpClient::create();
$response = $client->request('GET', 'https://symfony.com/versions.json');

$streamResource = StreamWrapper::createResource($response, $client);

// alternatively and contrary to the previous one, this returns
// a resource that is seekable and potentially stream_select()-able
$streamResource = $response->toStream();

echo stream_get_contents($streamResource); // outputs the content of the response

// later on if you need to, you can access the response from the stream
$response = stream_get_meta_data($streamResource)['wrapper_data']->getResponse();

Extensibility

If you want to extend the behavior of a base HTTP client, you can use service decoration:

基本 HTTP クライアントの動作を拡張したい場合は、サービス装飾を使用できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MyExtendedHttpClient implements HttpClientInterface
{
    private $decoratedClient;

    public function __construct(HttpClientInterface $decoratedClient = null)
    {
        $this->decoratedClient = $decoratedClient ?? HttpClient::create();
    }

    public function request(string $method, string $url, array $options = []): ResponseInterface
    {
        // process and/or change the $method, $url and/or $options as needed
        $response = $this->decoratedClient->request($method, $url, $options);

        // if you call here any method on $response, the HTTP request
        // won't be async; see below for a better way

        return $response;
    }

    public function stream($responses, float $timeout = null): ResponseStreamInterface
    {
        return $this->decoratedClient->stream($responses, $timeout);
    }
}

A decorator like this one is useful in cases where processing the requests' arguments is enough. By decorating the on_progress option, you can even implement basic monitoring of the response. However, since calling responses' methods forces synchronous operations, doing so inside request() will break async.

このようなデコレータは、リクエストの引数を処理するだけで十分な場合に役立ちます。 on_progress オプションを装飾することで、応答の基本的な監視を実装することもできます。ただし、responses のメソッドを呼び出すと同期操作が強制されるため、request() 内でこれを行うと非同期が壊れます。

The solution is to also decorate the response object itself. TraceableHttpClient and TraceableResponse are good examples as a starting point.

解決策は、応答オブジェクト自体も装飾することです。TraceableHttpClient と TraceableResponse は、出発点としての良い例です。

In order to help writing more advanced response processors, the component provides an AsyncDecoratorTrait. This trait allows processing the stream of chunks as they come back from the network:

より高度な応答プロセッサの作成を支援するために、コンポーネントは AsyncDecoratorTrait を提供します。この特性により、チャンクがネットワークから戻ってきたときにチャンクのストリームを処理できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyExtendedHttpClient implements HttpClientInterface
{
    use AsyncDecoratorTrait;

    public function request(string $method, string $url, array $options = []): ResponseInterface
    {
        // process and/or change the $method, $url and/or $options as needed

        $passthru = function (ChunkInterface $chunk, AsyncContext $context) {
            // do what you want with chunks, e.g. split them
            // in smaller chunks, group them, skip some, etc.

            yield $chunk;
        };

        return new AsyncResponse($this->client, $method, $url, $options, $passthru);
    }
}

Because the trait already implements a constructor and the stream() method, you don't need to add them. The request() method should still be defined; it shall return an AsyncResponse.

トレイトはすでにコンストラクターと stream() メソッドを実装しているため、それらを追加する必要はありません。 request() メソッドは引き続き定義する必要があり、AsyncResponse を返す必要があります。

The custom processing of chunks should happen in $passthru: this generator is where you need to write your logic. It will be called for each chunk yielded by the underlying client. A $passthru that does nothing would just yield $chunk;. You could also yield a modified chunk, split the chunk into many ones by yielding several times, or even skip a chunk altogether by issuing a return; instead of yielding.

チャンクのカスタム処理は $passthru で行う必要があります。このジェネレーターは、ロジックを記述する必要がある場所です。基礎となるクライアントによって生成された各チャンクに対して呼び出されます。何もしない $passthru は、単に yield$chunk; になります。また、変更されたチャンクを生成したり、複数回生成してチャンクを複数に分割したり、return を発行してチャンクを完全にスキップしたりすることもできます。譲歩する代わりに。

In order to control the stream, the chunk passthru receives an AsyncContext as second argument. This context object has methods to read the current state of the response. It also allows altering the response stream with methods to create new chunks of content, pause the stream, cancel the stream, change the info of the response, replace the current request by another one or change the chunk passthru itself.

ストリームを制御するために、チャンク パススルーは AsyncContext を 2 番目の引数として受け取ります。このコンテキスト オブジェクトには、応答の現在の状態を読み取るメソッドがあります。また、コンテンツの新しいチャンクを作成する、ストリームを一時停止する、ストリームをキャンセルする、応答の情報を変更する、現在の要求を別のものに置き換える、またはチャンクパススルー自体を変更するメソッドで応答ストリームを変更することもできます。

Checking the test cases implemented in AsyncDecoratorTraitTest might be a good start to get various working examples for a better understanding. Here are the use cases that it simulates:

AsyncDecoratorTraitTest で実装されたテスト ケースを確認することは、理解を深めるためにさまざまな動作例を取得するための良い出発点になる可能性があります。シミュレートする使用例は次のとおりです。
  • retry a failed request;
    失敗した要求を再試行します。
  • send a preflight request, e.g. for authentication needs;
    プリフライト リクエストを送信します。認証が必要な場合。
  • issue subrequests and include their content in the main response's body.
    サブリクエストを発行し、その内容をメイン レスポンスの本文に含めます。

The logic in AsyncResponse has many safety checks that will throw a LogicException if the chunk passthru doesn't behave correctly; e.g. if a chunk is yielded after an isLast() one, or if a content chunk is yielded before an isFirst() one, etc.

AsyncResponse のロジックには、chunkpassthru が正しく動作しない場合に LogicException をスローする多くの安全チェックがあります。例えばisLast() の後にチャンクが生成された場合、または isFirst() の前にコンテンツ チャンクが生成された場合など。

Testing

This component includes the MockHttpClient and MockResponse classes to use in tests that shouldn't make actual HTTP requests. Such tests can be useful, as they will run faster and produce consistent results, since they're not dependent on an external service. By not making actual HTTP requests there is no need to worry about the service being online or the request changing state, for example deleting a resource.

このコンポーネントには、実際の HTTP 要求を行うべきではないテストで使用する MockHttpClient および MockResponse クラスが含まれています。このようなテストは、外部サービスに依存しないため、より高速に実行され、一貫した結果が得られるため、便利です。実際の HTTP リクエストを行わないことで、サービスがオンラインになったり、リソースの削除など、リクエストの状態が変化したりすることを心配する必要がなくなります。

MockHttpClient implements the HttpClientInterface, just like any actual HTTP client in this component. When you type-hint with HttpClientInterface your code will accept the real client outside tests, while replacing it with MockHttpClient in the test.

MockHttpClient は、このコンポーネントの実際の HTTP クライアントと同様に、HttpClientInterface を実装します。 HttpClientInterface でヒントを入力すると、コードはテスト外の実際のクライアントを受け入れ、テストでは MockHttpClient に置き換えます。

When the request method is used on MockHttpClient, it will respond with the supplied MockResponse. There are a few ways to use it, as described below.

request メソッドが MockHttpClient で使用されると、提供された MockResponse で応答します。以下に説明するように、いくつかの使用方法があります。

HTTP Client and Responses

The first way of using MockHttpClient is to pass a list of responses to its constructor. These will be yielded in order when requests are made:

MockHttpClient を使用する最初の方法は、応答のリストをそのコンストラクターに渡すことです。これらは、リクエストが行われたときに順番に生成されます。
1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;

$responses = [
    new MockResponse($body1, $info1),
    new MockResponse($body2, $info2),
];

$client = new MockHttpClient($responses);
// responses are returned in the same order as passed to MockHttpClient
$response1 = $client->request('...'); // returns $responses[0]
$response2 = $client->request('...'); // returns $responses[1]

Another way of using MockHttpClient is to pass a callback that generates the responses dynamically when it's called:

MockHttpClient を使用する別の方法は、呼び出されたときに応答を動的に生成するコールバックを渡すことです。
1
2
3
4
5
6
7
8
9
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;

$callback = function ($method, $url, $options) {
    return new MockResponse('...');
};

$client = new MockHttpClient($callback);
$response = $client->request('...'); // calls $callback to get the response

Tip

ヒント

Instead of using the first argument, you can also set the (list of) responses or callbacks using the setResponseFactory() method:

最初の引数を使用する代わりに、setResponseFactory() メソッドを使用して、応答 (のリスト) またはコールバックを設定することもできます。
1
2
3
4
5
6
7
$responses = [
    new MockResponse($body1, $info1),
    new MockResponse($body2, $info2),
];

$client = new MockHttpClient();
$client->setResponseFactory($responses);

If you need to test responses with HTTP status codes different than 200, define the http_code option:

200 以外の HTTP ステータス コードで応答をテストする必要がある場合は、http_code オプションを定義します。
1
2
3
4
5
6
7
8
9
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;

$client = new MockHttpClient([
    new MockResponse('...', ['http_code' => 500]),
    new MockResponse('...', ['http_code' => 404]),
]);

$response = $client->request('...');

The responses provided to the mock client don't have to be instances of MockResponse. Any class implementing ResponseInterface will work (e.g. $this->createMock(ResponseInterface::class)).

モック クライアントに提供される応答は、MockResponse のインスタンスである必要はありません。 ResponseInterface を実装する任意のクラスが機能します (例: $this->createMock(ResponseInterface::class))。

However, using MockResponse allows simulating chunked responses and timeouts:

ただし、MockResponse を使用すると、チャンクされた応答とタイムアウトをシミュレートできます。
1
2
3
4
5
6
7
8
$body = function () {
    yield 'hello';
    // empty strings are turned into timeouts so that they are easy to test
    yield '';
    yield 'world';
};

$mockResponse = new MockResponse($body());

Finally, you can also create an invokable or iterable class that generates the responses and use it as a callback in functional tests:

最後に、応答を生成する呼び出し可能または反復可能なクラスを作成し、機能テストでコールバックとして使用することもできます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace App\Tests;

use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Contracts\HttpClient\ResponseInterface;

class MockClientCallback
{
    public function __invoke(string $method, string $url, array $options = []): ResponseInterface
    {
        // load a fixture file or generate data
        // ...
        return new MockResponse($data);
    }
}

Then configure Symfony to use your callback:

次に、コールバックを使用するように Symfony を構成します。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
# config/services_test.yaml
services:
    # ...
    App\Tests\MockClientCallback: ~

# config/packages/test/framework.yaml
framework:
    http_client:
        mock_response_factory: App\Tests\MockClientCallback

Testing Request Data

The MockResponse class comes with some helper methods to test the request:

MockResponse クラスには、リクエストをテストするためのいくつかのヘルパー メソッドが付属しています。
  • getRequestMethod() - returns the HTTP method;
    getRequestMethod() - HTTP メソッドを返します。
  • getRequestUrl() - returns the URL the request would be sent to;
    getRequestUrl() - リクエストの送信先 URL を返します。
  • getRequestOptions() - returns an array containing other information about the request such as headers, query parameters, body content etc.
    getRequestOptions() - ヘッダー、クエリ パラメータ、ボディ コンテンツなど、リクエストに関するその他の情報を含む配列を返します。

Usage example:

使用例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$mockResponse = new MockResponse('', ['http_code' => 204]);
$httpClient = new MockHttpClient($mockResponse, 'https://example.com');

$response = $httpClient->request('DELETE', 'api/article/1337', [
    'headers' => [
        'Accept: */*',
        'Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l',
    ],
]);

$mockResponse->getRequestMethod();
// returns "DELETE"

$mockResponse->getRequestUrl();
// returns "https://example.com/api/article/1337"

$mockResponse->getRequestOptions()['headers'];
// returns ["Accept: */*", "Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l"]

Full Example

The following standalone example demonstrates a way to use the HTTP client and test it in a real application:

次のスタンドアロンの例は、HTTP クライアントを使用して実際のアプリケーションでテストする方法を示しています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// ExternalArticleService.php
use Symfony\Contracts\HttpClient\HttpClientInterface;

final class ExternalArticleService
{
    private HttpClientInterface $httpClient;

    public function __construct(HttpClientInterface $httpClient)
    {
        $this->httpClient = $httpClient;
    }

    public function createArticle(array $requestData): array
    {
        $requestJson = json_encode($requestData, JSON_THROW_ON_ERROR);

        $response = $this->httpClient->request('POST', 'api/article', [
            'headers' => [
                'Content-Type: application/json',
                'Accept: application/json',
            ],
            'body' => $requestJson,
        ]);

        if (201 !== $response->getStatusCode()) {
            throw new Exception('Response status code is different than expected.');
        }

        // ... other checks

        $responseJson = $response->getContent();
        $responseData = json_decode($responseJson, true, 512, JSON_THROW_ON_ERROR);

        return $responseData;
    }
}

// ExternalArticleServiceTest.php
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;

final class ExternalArticleServiceTest extends TestCase
{
    public function testSubmitData(): void
    {
        // Arrange
        $requestData = ['title' => 'Testing with Symfony HTTP Client'];
        $expectedRequestData = json_encode($requestData, JSON_THROW_ON_ERROR);

        $expectedResponseData = ['id' => 12345];
        $mockResponseJson = json_encode($expectedResponseData, JSON_THROW_ON_ERROR);
        $mockResponse = new MockResponse($mockResponseJson, [
            'http_code' => 201,
            'response_headers' => ['Content-Type: application/json'],
        ]);

        $httpClient = new MockHttpClient($mockResponse, 'https://example.com');
        $service = new ExternalArticleService($httpClient);

        // Act
        $responseData = $service->createArticle($requestData);

        // Assert
        self::assertSame('POST', $mockResponse->getRequestMethod());
        self::assertSame('https://example.com/api/article', $mockResponse->getRequestUrl());
        self::assertContains(
            'Content-Type: application/json',
            $mockResponse->getRequestOptions()['headers']
        );
        self::assertSame($expectedRequestData, $mockResponse->getRequestOptions()['body']);

        self::assertSame($responseData, $expectedResponseData);
    }
}