<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<!-- EN-Revision: 15103 -->
<sect1 id="zend.http.client.adapters">
    <title>Zend_Http_Client - 接続アダプタ</title>

    <sect2 id="zend.http.client.adapters.overview">
        <title>概要</title>
        <para>
            Zend_Http_Client は、接続アダプタとして設計されています。
            接続アダプタは実際にサーバへの接続を行うオブジェクトで、
            リクエストの書き込みやレスポンスの読み込みも行います。
            この接続アダプタは置き換えることができます。
            つまり、デフォルトの接続アダプタを継承して自分の好みにあうように変更することができます。
            HTTP クライアントクラス全体を書き換える必要はありません。
            同じインターフェイスを実装しているだけでいいのです。
        </para>
        <para>
            現在、Zend_Http_Client クラスは三つの組み込み接続アダプタを提供しています。
            <itemizedlist>
                <listitem>
                    <para>
                        <classname>Zend_Http_Client_Adapter_Socket</classname> (デフォルト)
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <classname>Zend_Http_Client_Adapter_Proxy</classname>
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <classname>Zend_Http_Client_Adapter_Test</classname>
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <classname>Zend_Http_Client_Adapter_Curl</classname>
                    </para>
                </listitem>
            </itemizedlist>
        </para>
        <para>
            Zend_Http_Client オブジェクトの接続アダプタを指定するには、
            設定オプション 'adapter' を使用します。
            クライアントオブジェクトのインスタンスを作成する際に、オプション
            'adapter' にアダプタの名前 (たとえば 'Zend_Http_Client_Adapter_Socket' など)
            を指定することができます。あるいは、アダプタオブジェクトの変数
            (たとえば <code>new Zend_Http_Client_Adapter_Test</code> など) を指定することもできます。
            Zend_Http_Client->setConfig() メソッドを使用し、
            アダプタを後で設定することも可能です。
        </para>
    </sect2>

    <sect2 id="zend.http.client.adapters.socket">
        <title>ソケットアダプタ</title>
        <para>
            デフォルトの接続アダプタは Zend_Http_Client_Adapter_Socket
            です。明示的に接続アダプタを指定しない場合は、これが使用されます。
            Socket アダプタは PHP の組み込み関数 fsockopen()
            を使用しており、特別な拡張モジュールやコンパイルオプションは必要ありません。
        </para>
        <para>
            ソケットアダプタには、追加の設定オプションを指定することができます。これは
            <classname>Zend_Http_Client->setConfig()</classname> で指定するか、
            あるいはクライアントのコンストラクタに渡します。
            <table id="zend.http.client.adapter.socket.configuration.table">
                <title>Zend_Http_Client_Adapter_Socket の設定パラメータ</title>
                <tgroup cols="4">
                    <thead>
                        <row>
                            <entry>パラメータ</entry>
                            <entry>説明</entry>
                            <entry>予期する型</entry>
                            <entry>デフォルト値</entry>
                        </row>
                    </thead>
                    <tbody>
                        <row>
                            <entry>persistent</entry>
                            <entry>持続的な TCP 接続を使用するかどうか</entry>
                            <entry>boolean</entry>
                            <entry>false</entry>
                        </row>
                        <row>
                            <entry>ssltransport</entry>
                            <entry>SSL トランスポート層 (たとえば 'sslv2'、'tls')</entry>
                            <entry>文字列</entry>
                            <entry>ssl</entry>
                        </row>
                        <row>
                            <entry>sslcert</entry>
                            <entry>PEM でエンコードした、SSL 証明書ファイルへのパス</entry>
                            <entry>文字列</entry>
                            <entry>null</entry>
                        </row>
                        <row>
                            <entry>sslpassphrase</entry>
                            <entry>SSL 証明書ファイルのパスフレーズ</entry>
                            <entry>文字列</entry>
                            <entry>null</entry>
                        </row>
                    </tbody>
                </tgroup>
            </table>

            <note>
                <title>持続的な TCP 接続</title>
                <para>
                    持続的な TCP 接続を使用すると、HTTP
                    リクエストの処理速度が向上する可能性があります。
                    しかし、たいていの場合はその効果はごくわずかで、
                    接続先の HTTP サーバにかかる負荷が大きくなります。
                </para>
                <para>
                    持続的な TCP 接続を使用するのは、
                    同じサーバに頻繁に接続する場合で
                    そのサーバが同時に多数の接続を処理できることがわかっている場合のみにすることを推奨します。
                    いずれにせよ、このオプションを使用する前には
                    クライアント側の速度だけでなくサーバ側の負荷についてもベンチマークをとるようにしましょう。
                </para>
                <para>
                    さらに、持続的な接続を使用するときには
                    <xref linkend="zend.http.client.configuration" />
                    で説明した Keep-Alive HTTP リクエストも有効にしておくことを推奨します。
                    そうしないと、持続的な接続の効果はほとんどといっていいほどなくなってしまいます。
                </para>
            </note>

            <note>
                <title>HTTPS SSL ストリームパラメータ</title>
                <para>
                    <code>ssltransport, sslcert</code> および <code>sslpassphrase</code>
                    は、HTTPS 接続で使用する SSL レイヤーにのみ関連するものです。
                </para>
                <para>
                    たいていの場合はデフォルトの SSL 設定でうまく動作するでしょうが、
                    接続先のサーバが特別なクライアント設定を要求している場合は
                    それにあわせた設定をする必要があるかもしれません。
                    その場合は、
                    <ulink url="http://www.php.net/manual/ja/transports.php#transports.inet">ここ</ulink>
                    で SSL トランスポート層やオプションについての説明を参照ください。
                </para>
            </note>
        </para>
        <example id="zend.http.client.adapters.socket.example-1">
            <title>HTTPS トランスポート層の変更</title>
            <programlisting role="php"><![CDATA[
// 設定パラメータを指定します
$config = array(
    'adapter'      => 'Zend_Http_Client_Adapter_Socket',
    'ssltransport' => 'tls'
);

// クライアントオブジェクトのインスタンスを作成します
$client = new Zend_Http_Client('https://www.example.com', $config);

// これ以降のリクエストは、TLS セキュア接続上で行われます
$response = $client->request();
]]>
            </programlisting>
        </example>
        <para>
            上の例の結果は、次の PHP コマンドで TCP
            接続をオープンした場合と同じになります。
        </para>
        <para>
            <code>fsockopen('tls://www.example.com', 443)</code>
        </para>
    </sect2>

    <sect2 id="zend.http.client.adapters.proxy">
        <title>プロキシアダプタ</title>
        <para>
            Zend_Http_Client_Adapter_Proxy アダプタはデフォルトのソケットアダプタとほぼ同じです。
            ただし、対象となるサーバに直接接続するのではなく
            HTTP プロキシサーバを経由して接続するという点が異なります。
            これにより、Zend_Http_Client をプロキシサーバの中から使用できるようになります。
            セキュリティやパフォーマンス上の理由により、これが必要となる場合もあるでしょう。
        </para>

        <para>
            プロキシアダプタを使用するには、
            デフォルトの 'adapter' オプション以外に
            いくつか追加のパラメータを設定する必要があります。
            <table id="zend.http.client.adapters.proxy.table">
                <title>Zend_Http_Client の設定パラメータ</title>
                <tgroup cols="4">
                    <thead>
                        <row>
                            <entry>パラメータ</entry>
                            <entry>説明</entry>
                            <entry>想定している型</entry>
                            <entry>値の例</entry>
                        </row>
                    </thead>
                    <tbody>
                        <row>
                            <entry>proxy_host</entry>
                            <entry>プロキシサーバのアドレス</entry>
                            <entry>string</entry>
                            <entry>'proxy.myhost.com' あるいは '10.1.2.3'</entry>
                        </row>
                        <row>
                            <entry>proxy_port</entry>
                            <entry>プロキシサーバの TCP ポート</entry>
                            <entry>integer</entry>
                            <entry>8080 (デフォルト) あるいは 81</entry>
                        </row>
                        <row>
                            <entry>proxy_user</entry>
                            <entry>必要に応じて、プロキシのユーザ名</entry>
                            <entry>string</entry>
                            <entry>'shahar' あるいは指定しない場合は '' (デフォルト)</entry>
                        </row>
                        <row>
                            <entry>proxy_pass</entry>
                            <entry>必要に応じて、プロキシのパスワード</entry>
                            <entry>string</entry>
                            <entry>'secret' あるいは指定しない場合は '' (デフォルト)</entry>
                        </row>
                        <row>
                            <entry>proxy_auth</entry>
                            <entry>プロキシの HTTP 認証形式</entry>
                            <entry>string</entry>
                            <entry>Zend_Http_Client::AUTH_BASIC (デフォルト)</entry>
                        </row>
                    </tbody>
                </tgroup>
            </table>
        </para>
        <para>
            proxy_host は常に設定しなければなりません。指定しなかった場合は、
            自動的に Zend_Http_Client_Adapter_Socket による直接接続に切り替わります。
            proxy_port のデフォルトは '8080' です。もし別のポートをプロキシで使用している場合は、
            適切に設定する必要があります。
        </para>
        <para>
            proxy_user および proxy_pass は、
            プロキシサーバが認証を必要とする場合にのみ設定します。
            これらを指定すると、'Proxy-Authentication'
            ヘッダがリクエストに追加されます。プロキシで認証を必要としない場合は、
            このふたつのオプションはそのままにしておきます。
        </para>
        <para>
            proxy_auth は、プロキシが認証を必要としている場合に、
            その認証形式を指定します。設定できる値は
            Zend_Http_Client::setAuth() メソッドと同じです。現在はベーシック認証
            (Zend_Http_Client::AUTH_BASIC) のみをサポートしています。
        </para>
        <example id="zend.http.client.adapters.proxy.example-1">
            <title>プロキシサーバを使用した Zend_Http_Client の使用法</title>
            <programlisting role="php"><![CDATA[
// 接続パラメータを設定します
$config = array(
    'adapter'    => 'Zend_Http_Client_Adapter_Proxy',
    'proxy_host' => 'proxy.int.zend.com',
    'proxy_port' => 8000,
    'proxy_user' => 'shahar.e',
    'proxy_pass' => 'bananashaped'
);

// クライアントオブジェクトのインスタンスを作成します
$client = new Zend_Http_Client('http://www.example.com', $config);

// 作業を続けます...
]]>
            </programlisting>
        </example>
        <para>
            説明したとおり、もし proxy_host を省略したり空文字列を設定したりすると、
            自動的に直接接続に切り替わります。これにより、設定パラメータによって
            オプションでプロキシを使用できるようなアプリケーションを書くことが可能となります。
        </para>
    </sect2>

    <sect2 id="zend.http.client.adapters.test">
        <title>テストアダプタ</title>
        <para>
            HTTP 接続に依存するテストコードを書くのは非常に難しいものです。
            たとえば、リモートサーバから RSS を取得するアプリケーションをテストするには、
            ネットワークにつながっている必要があります。常にネットワークが使用できるとは限りません。
        </para>
        <para>
            このようなときのためにあるのが Zend_Http_Client_Adapter_Test アダプタです。
            Zend_Http_Client を使用するアプリケーションを作成し、それをテストしたい場合には、
            デフォルトのアダプタを Test アダプタ (モックオブジェクト) に変更します。
            これで、サーバに接続せずにテストを行えるようになります。
        </para>
        <para>
            Zend_Http_Client_Adapter_Test には setResponse() というメソッドがあります。
            このメソッドのパラメータには、HTTP レスポンスをテキストか
            Zend_Http_Response オブジェクトで指定することができます。
            レスポンスを設定すると、Test アダプタは常にこのレスポンスを返すようになります。
            実際の HTTP リクエストは行いません。
        </para>
        <example id="zend.http.client.adapters.test.example-1">
            <title>HTTP レスポンススタブを使用したテスト</title>
            <programlisting role="php"><![CDATA[
// 新しいアダプタとクライアントのインスタンスを作成します
$adapter = new Zend_Http_Client_Adapter_Test();
$client = new Zend_Http_Client('http://www.example.com', array(
    'adapter' => $adapter
));

// 想定するレスポンスを設定します
$adapter->setResponse(
    "HTTP/1.1 200 OK"        . "\r\n" .
    "Content-type: text/xml" . "\r\n" .
                               "\r\n" .
    '<?xml version="1.0" encoding="UTF-8"?>' .
    '<rss version="2.0" ' .
    '     xmlns:content="http://purl.org/rss/1.0/modules/content/"' .
    '     xmlns:wfw="http://wellformedweb.org/CommentAPI/"' .
    '     xmlns:dc="http://purl.org/dc/elements/1.1/">' .
    '  <channel>' .
    '    <title>Premature Optimization</title>' .
    // などなど...
    '</rss>');

$response = $client->request('GET');
// .. $response の処理を続けます...
]]>
            </programlisting>
        </example>
        <para>
            上の例のようにすると、HTTP クライアントにお望みのレスポンスを返させることができます。
            その際にネットワーク接続は使用しません。また、実際のサーバからのレスポンスも使用しません。
            この場合、このテストでテストするのは、
            レスポンス本文の XML をアプリケーションが正しくパースできるかどうかということです。
        </para>

        <para>
            時には、オブジェクトに対するひとつのメソッド呼び出しの中で複数の
            HTTP トランザクションを行うこともあるでしょう。そのような場合は
            setResponse() を単独で使うことはできません。なぜなら、
            結果が呼び出し元に返ってくるまで次のレスポンスを設定できないからです。
        </para>
        <example id="zend.http.client.adapters.test.example-2">
            <title>複数の HTTP レスポンススタブを使用したテスト</title>
            <programlisting role="php"><![CDATA[
// 新しいアダプタおよびクライアントのインスタンスを作成します
$adapter = new Zend_Http_Client_Adapter_Test();
$client = new Zend_Http_Client('http://www.example.com', array(
    'adapter' => $adapter
));

// 最初の応答として期待する値を設定します
$adapter->setResponse(
    "HTTP/1.1 302 Found"      . "\r\n" .
    "Location: /"             . "\r\n" .
    "Content-Type: text/html" . "\r\n" .
                                "\r\n" .
    '<html>' .
    '  <head><title>Moved</title></head>' .
    '  <body><p>This page has moved.</p></body>' .
    '</html>');

// それに続くレスポンスを設定します
$adapter->addResponse(
    "HTTP/1.1 200 OK"         . "\r\n" .
    "Content-Type: text/html" . "\r\n" .
                                "\r\n" .
    '<html>' .
    '  <head><title>My Pet Store Home Page</title></head>' .
    '  <body><p>...</p></body>' .
    '</html>');

// HTTP クライアントオブジェクト ($client) をテスト対象の
// オブジェクトに注入し、オブジェクトの動きを以下でテストします
]]>
            </programlisting>
        </example>
        <para>
            setResponse() メソッドは、
            Zend_Http_Client_Adapter_Test のバッファにあるレスポンスをすべて削除し、
            最初に返されるレスポンスを設定します。addResponse()
            メソッドは、それに続くレスポンスを追加します。
        </para>
        <para>
            レスポンスは、それを追加した順に再生されます。
            登録したよりも多くのリクエストが発生した場合は、
            返されるレスポンスは最初のものに戻り、そこからまた順に返されるようになります。
        </para>
        <para>
            上の例で、このアダプタがテストするように設定されているのは、
            302 リダイレクトが発生した場合のオブジェクトの挙動です。
            アプリケーションの内容によって、リダイレクトさせるべきなのかそうでないのかは異なるでしょう。
            この例ではリダイレクトさせることを想定しているので、
            テストアダプタもそれにあわせて設定しています。
            最初の 302 レスポンスを setResponse() メソッドで設定し、
            次に返される 200 レスポンスを addResponse() メソッドで設定します。
            テストアダプタを設定し終えたら、そのアダプタを含む HTTP
            クライアントをテスト対象オブジェクトに注入し、その挙動をテストします。
        </para>
    </sect2>

<sect2 id="zend.http.client.adapters.curl">
        <title>cURL アダプタ</title>
        <para>
            cURL は標準的な HTTP クライアントライブラリで、
            多くの OS に含まれています。また PHP からは cURL
            拡張モジュールで使用することができます。
            HTTP クライアントで起こりうる多くの特別な例にも対応することができるので、
            HTTP アダプタとしては完璧な選択肢といえるでしょう。
            セキュアな接続やプロキシ、そしてあらゆる種類の認証にも対応しており、
            大きなファイルをサーバ間で移動させるときなどにも使用できます。
        </para>

        <example id="zend.http.client.adapters.curl.example-1">
            <title>cURL オプションの設定</title>
            <programlisting role="php"><![CDATA[
$config = array(
    'adapter'   => 'Zend_Http_Client_Adapter_Curl',
    'curloptions' => array(CURLOPT_FOLLOWLOCATION => true),
);
$client = new Zend_Http_Client($uri, $config);
]]>
            </programlisting>
        </example>

        <para>
            デフォルトでは、cURL アダプタは
            Socket アダプタとまったく同じ挙動となるように設定されています。
            cURL のオプションを変更するには、アダプタのコンストラクタでキー
            'curloptions' を指定するか、あるいは
            <code>setCurlOption($name, $value)</code> をコールします。
            <code>$name</code> は、cURL 拡張モジュールの
            CURL_* 定数に対応します。
        </para>

        <example id="zend.http.client.adapters.curl.example-2">
            <title>ハンドルによるファイル転送</title>

            <para>
                cURL を使用して、巨大なファイルを HTTP 越しに転送することができます。
            </para>

            <programlisting role="php"><![CDATA[
$putFileSize   = filesize("filepath");
$putFileHandle = fopen("filepath", "r");

$adapter = new Zend_Http_Client_Adapter_Curl();
$client = new Zend_Http_Client();
$client->setAdapter($adapter);
$adapter->setConfig(array(
    'curloptions' => array(
        CURLOPT_INFILE => $putFileHandle,
        CURLOPT_INFILESIZE => $putFileSize
    )
));
$client->request("PUT");
]]>
            </programlisting>
        </example>
    </sect2>

    <sect2 id="zend.http.client.adapters.extending">
        <title>独自の接続アダプタの作成</title>
        <para>
            独自の接続アダプタを作成し、それを使用することもできます。
            たとえば持続的なソケットを使用するアダプタを作成したり、
            キャッシュ機能を追加したアダプタを作成したりなど、
            作成するアプリケーションの要件にあわせたものを作成することが可能です。
        </para>
        <para>
            そのためには、Zend_Http_Client_Adapter_Interface
            を実装したクラスを作成する必要があります。
            以下の例は、ユーザ定義のアダプタクラスの雛形となります。
            この例で定義されているすべてのパブリック関数を、
            アダプタで定義する必要があります。
        </para>
        <example id="zend.http.client.adapters.extending.example-1">
            <title>独自の接続アダプタの作成</title>
            <programlisting role="php"><![CDATA[
class MyApp_Http_Client_Adapter_BananaProtocol
    implements Zend_Http_Client_Adapter_Interface
{
    /**
     * アダプタの設定配列を設定する
     *
     * @param array $config
     */
    public function setConfig($config = array())
    {
        // ここはほとんど変更することはありません -
        // 通常は Zend_Http_Client_Adapter_Socket の実装をコピーします
    }

    /**
     * リモートサーバに接続する
     *
     * @param string  $host
     * @param int     $port
     * @param boolean $secure
     */
    public function connect($host, $port = 80, $secure = false)
    {
        // リモートサーバとの接続を確立します
    }

    /**
     * リクエストをリモートサーバに送信する
     *
     * @param string        $method
     * @param Zend_Uri_Http $url
     * @param string        $http_ver
     * @param array         $headers
     * @param string        $body
     * @return string Request as text
     */
    public function write($method,
                          $url,
                          $http_ver = '1.1',
                          $headers = array(),
                          $body = '')
    {
        // リクエストをリモートサーバに送信します。
        // この関数は、リクエスト全体 (ヘッダおよび本文)
        // を文字列で返します。
    }

    /**
     * サーバからのレスポンスを読み込む
     *
     * @return string
     */
    public function read()
    {
        // リモートサーバからのレスポンスを読み込み、それを文字列で返します。
    }

    /**
     * サーバとの接続を閉じる
     *
     */
    public function close()
    {
        // リモートサーバとの接続を閉じます。最後にコールされます。
    }
}

// そして、このアダプタを使用します
$client = new Zend_Http_Client(array(
    'adapter' => 'MyApp_Http_Client_Adapter_BananaProtocol'
));
]]>
            </programlisting>
        </example>
    </sect2>
</sect1>
