# レスポンスオブジェクト

## 使用法

レスポンスオブジェクトは、 リクエストオブジェクト と対になるものです。 その目的は、コンテンツやヘッダを収集し、それを返すことです。 さらに、フロントコントローラで捕捉した例外はすべてレスポンスオブジェクトに渡されます。 これにより、例外の処理がやりやすくなります。 この挙動を変更するには `Zend_Controller_Front::throwExceptions(true)` と設定します。

    $front->throwExceptions(true);

ヘッダを含むレスポンス出力を送信するには、 `sendResponse()` を使用します。

    $response->sendResponse();

デフォルトでは、リクエストのディスパッチに終了した時点でフロントコントローラが `sendResponse()` をコールします。通常はこれをコールする必要はありません。 しかし、テスト中などにレスポンスの内容を操作したい場合は、 returnResponse フラグを `Zend_Controller_Front::returnResponse(true)` と設定することでこの振る舞いを変更できます。

    $front->returnResponse(true);
    $response = $front->dispatch();

    // 何かの処理、たとえばログの記録などを行ってから
    // 結果を出力します
    $response->sendResponse();

開発者は、アクションコントローラの中でレスポンスオブジェクトを使用しなければなりません。 出力を直接レンダリングしたり直接ヘッダを送信したりするのではなく、 それらをレスポンスオブジェクトに格納するようにします。

    // アクションコントローラのアクション内で、
    // ヘッダを設定します
    $this->getResponse()
        ->setHeader('Content-Type', 'text/html')
        ->appendBody($content);

こうすることで、すべてのヘッダを一度に送信し、 その後でコンテンツを表示することができます。

アクションコントローラで ビューの統合 を使用する場合は、 レンダリングされたビュースクリプトの内容をレスポンスオブジェクトに設定する必要はありません。 `Zend_Controller_Action::render()` がデフォルトでこれを行います。

アプリケーションで例外が発生したかどうかを調べるには、 レスポンスオブジェクトの `isException()` フラグを調べます。例外を取得するには `getException()` を使用します。さらに、独自のレスポンスオブジェクトを作成して、 エラーページへのリダイレクトや例外メッセージのログ出力、 例外をわかりやすく表示する (開発用) などを行うことができます。

レスポンスオブジェクトは、フロントコントローラの `dispatch()` から受け取ることになります。あるいは、 出力のレンダリングを行わない状態のレスポンスオブジェクトを フロントコントローラから受け取ることもできます。

    // dispatch の後に取得します
    $front->dispatch();
    $response = $front->getResponse();
    if ($response->isException()) {
        // ログへの記録、メール送信など...
    }

    // あるいは、フロントコントローラの dispatch() の返り値を使用します
    $front->returnResponse(true);
    $response = $front->dispatch();

    // 何かの処理...

    // 最後に結果を表示します
    $response->sendResponse();

デフォルトでは、例外メッセージは表示されません。 この挙動をオーバーライドするには `renderExceptions()` メソッドを使用するか、あるいは上で示したようにフロントコントローラで `throwExceptions()` を有効にします。

    $response->renderExceptions(true);
    $front->dispatch($request, $response);

    // あるいは
    $front->returnResponse(true);
    $response = $front->dispatch();
    $response->renderExceptions();
    $response->sendResponse();

    // あるいは
    $front->throwExceptions(true);
    $front->dispatch();

## ヘッダの操作

先ほど説明したように、レスポンスオブジェクトの役割のひとつは HTTP レスポンスヘッダを発行することです。 このために、さまざまなメソッドが用意されています。

- `canSendHeaders()` を使用して、 ヘッダがすでに送信されているかどうかを調べます。 オプションのフラグで、ヘッダが送信済みの場合に例外をスローするかどうかを指定します。 この設定は、プロパティ headersSentThrowsException を `FALSE` にすることで上書きできます。

- `setHeader($name, $value, $replace = false)` を使用して、個々のヘッダを設定します。デフォルトでは、 同名のヘッダがすでに存在した場合に既存のヘッダを置換することはありません。 しかし、`$replace` を `TRUE` に設定すると、 既存のヘッダを上書きするようになります。

  ヘッダを設定する前に、このメソッドは `canSendHeaders()` を使用して ヘッダが現時点で送信済みでないかどうか、例外をスローするかどうかを調べます。

- `setRedirect($url, $code = 302)` は、 リダイレクト用の HTTP Location ヘッダを設定します。 HTTP ステータスコードを指定した場合は、そのコードを使用します。

  内部的には、このメソッドは `$replace` フラグをオンにして `setHeader()` をコールしています。

- `getHeaders()` は、すべてのヘッダを配列で返します。 個々の配列の要素は、'name' および 'value' のふたつのキーを持つ配列となります。

- `clearHeaders()` は登録済みのヘッダをすべて削除します。

- `setRawHeader()` を使用して、キー/値 の組になっていないヘッダを設定します。 たとえば HTTP status ヘッダなどがこれにあたります。

- `getRawHeaders()` は、登録済みの生のヘッダを返します。

- `clearRawHeaders()` は、登録済みの生のヘッダを消去します。

- `clearAllHeaders()` は、キー/値 のペアである通常のヘッダと 生のヘッダの両方を消去します。

これらのメソッドのほかに、現在のリクエストの HTTP レスポンスコードを設定したり取得したりするメソッドとして `setHttpResponseCode()` と `getHttpResponseCode()` が用意されています。

## 名前つきセグメント

レスポンスオブジェクトでは「名前つきセグメント」をサポートしています。 これにより、本文部だけを別のセグメントに切り分けて、 指定した順序で出力したりといったことができるようになります。 内部的にはコンテンツは配列として保存され、 さまざまなメソッドを使用してその配列にアクセスできるようになります。

例として、`preDispatch()` フックメソッドで レスポンスオブジェクトにヘッダを追加し、 アクションコントローラで本文を追加して、 最後に `postDispatch()` フックメソッドでフッタを追加することを考えてみましょう。

    // このプラグインクラスがフロントコントローラで登録済みであると仮定します
    class MyPlugin extends Zend_Controller_Plugin_Abstract
    {
        public function preDispatch(Zend_Controller_Request_Abstract $request)
        {
            $response = $this->getResponse();
            $view = new Zend_View();
            $view->setBasePath('../views/scripts');

            $response->prepend('header', $view->render('header.phtml'));
        }

        public function postDispatch(Zend_Controller_Request_Abstract $request)
        {
            $response = $this->getResponse();
            $view = new Zend_View();
            $view->setBasePath('../views/scripts');

            $response->append('footer', $view->render('footer.phtml'));
        }
    }

    // アクションコントローラの例
    class MyController extends Zend_Controller_Action
    {
        public function fooAction()
        {
            $this->render();
        }
    }

上の例で `/my/foo` をコールすると、 レスポンスオブジェクトに最終的に格納されるコンテンツは次のようになります。

    array(
        'header'  => ..., // ヘッダの内容
        'default' => ..., // MyController::fooAction() が作成した本文
        'footer'  => ...  // フッタの内容
    );

これをレンダリングすると、配列に要素が追加された順に表示されます。

名前つきセグメントを操作するメソッドには、以下のようなものがあります。

- `setBody()` および `appendBody()` の二番目のパラメータである `$name` に、セグメント名を渡すことができます。 これを指定すると、指定したセグメントの内容を上書きします (存在しない場合は新たに作成し、配列に追加します)。 `setBody()` にセグメント名を指定しなかった場合は、 配列全体を初期化します。`appendBody()` でセグメント名を省略した場合は、'default' という名前のセグメントを追加します。

- `prepend($name, $content)` は、 `$name` という名前のセグメントを作成して、 それを配列の先頭に追加します。同じ名前のセグメントが存在する場合は、 まずそれを削除してから追加します(つまり、既存のものを上書きします)。

- `append($name, $content)` は、 `$name` という名前のセグメントを作成して、 それを配列の最後に追加します。同じ名前のセグメントが存在する場合は、 まずそれを削除してから追加します(つまり、既存のものを上書きします)。

- `insert($name, $content, $parent = null, $before = false)` は、`$name` という名前のセグメントを作成します。 `$parent` セグメントを指定すると、 新しいセグメントはそのセグメントの前か後ろ (`$before` の値で決まります) に配置されます。同じ名前のセグメントが存在する場合は、 まずそれを削除してから追加します(つまり、既存のものを上書きします)。

- `clearBody($name = null)` に `$name` を指定すると、その名前のセグメントを消去します (省略した場合は、配列全体を消去します)。

- `getBody($spec = false)` で `$spec` にセグメント名を指定すると、そのセグメントを取得できます。 `$spec` に `FALSE` を指定すると、 すべてのセグメントの内容を順番に連結した結果を文字列で返します。 `$spec` に `TRUE` を指定すると、本文の配列を返します。

## レスポンスオブジェクト内での例外の検査

先ほど説明したように、デフォルトでは ディスパッチ中に発生した例外はレスポンスオブジェクトに登録されます。 例外はスタックに登録されるので、発生した例外はすべて保持することができます。 アプリケーションの例外、ディスパッチ処理の例外、プラグインの例外などなど……。 特定の例外の内容を調べたり例外をログに記録したりしたい場合は、 レスポンスオブジェクトの例外用 API を使用します。

- `setException(Exception $e)` は、例外を登録します。

- `isException()` は、例外が登録されているかどうかを調べます。

- `getException()` は、例外スタック全体を返します。

- `hasExceptionOfType($type)` は、特定のクラスの例外がスタックに登録されているかどうかを調べます。

- `hasExceptionOfMessage($message)` は、指定したメッセージを含む例外が スタックに登録されているかどうかを調べます。

- `hasExceptionOfCode($code)` は、指定したコードを含む例外が スタックに登録されているかどうかを調べます。

- `getExceptionByType($type)` は、指定したクラスの例外をスタックからすべて取り出します。 そのクラスの例外が見つからなかった場合は `FALSE` を返し、 見つかった場合は例外の配列を返します。

- `getExceptionByMessage($message)` は、指定したメッセージを含む例外をスタックからすべて取り出します。 そのクラスの例外が見つからなかった場合は `FALSE` を返し、 見つかった場合は例外の配列を返します。

- `getExceptionByCode($code)` は、指定したコードを含む例外をスタックからすべて取り出します。 そのクラスの例外が見つからなかった場合は `FALSE` を返し、 見つかった場合は例外の配列を返します。

- `renderExceptions($flag)` は、例外が発生したかどうかを表すフラグを設定します。

## レスポンスオブジェクトのサブクラスの作成

レスポンスオブジェクトの役割は、 さまざまなアクションやプラグインからヘッダやコンテンツを収集し、 それをクライアントに返すことです。 さらに、処理中に発生したエラーの内容も収集します。 これはそのまま返すこともありますし、あるいはユーザから見えないように隠すこともあります。

レスポンスクラスの基底クラスは `Zend_Controller_Response_Abstract` です。レスポンスクラスを作成する際には、 このクラスあるいはその派生クラスのいずれかを継承しなければなりません。 このクラスが提供するメソッドについては、先ほど説明しました。

レスポンスオブジェクトのサブクラスを作成する理由としては、 リクエストされた環境に応じて出力内容を変えたり (たとえば CLI や PHP-GTK の場合はヘッダを送信しないなど) 名前つきセグメントに保存された内容の最終結果を返す機能を追加したりといったことが考えられます。