| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- Reviewed: no -->
- <sect1 id="zend.controller.response">
- <title>The Response Object</title>
- <sect2 id="zend.controller.response.usage">
- <title>Usage</title>
- <para>
- The response object is the logical counterpart to the <link
- linkend="zend.controller.request">request object</link>. Its
- purpose is to collate content and/or headers so that they may be
- returned en masse. Additionally, the front controller will pass any
- caught exceptions to the response object, allowing the developer to
- gracefully handle exceptions. This functionality may be overridden
- by setting
- <methodname>Zend_Controller_Front::throwExceptions(true)</methodname>:
- </para>
- <programlisting language="php"><![CDATA[
- $front->throwExceptions(true);
- ]]></programlisting>
- <para>
- To send the response output, including headers, use
- <methodname>sendResponse()</methodname>.
- </para>
- <programlisting language="php"><![CDATA[
- $response->sendResponse();
- ]]></programlisting>
- <note>
- <para>
- By default, the front controller calls <methodname>sendResponse()</methodname>
- when it has finished dispatching the request; typically you will
- never need to call it. However, if you wish to manipulate the
- response or use it in testing, you can override this
- behaviour by setting the <property>returnResponse</property> flag with
- <methodname>Zend_Controller_Front::returnResponse(true)</methodname>:
- </para>
- <programlisting language="php"><![CDATA[
- $front->returnResponse(true);
- $response = $front->dispatch();
- // do some more processing, such as logging...
- // and then send the output:
- $response->sendResponse();
- ]]></programlisting>
- </note>
- <para>
- Developers should make use of the response object in their action
- controllers. Instead of directly rendering output and sending
- headers, push them to the response object:
- </para>
- <programlisting language="php"><![CDATA[
- // Within an action controller action:
- // Set a header
- $this->getResponse()
- ->setHeader('Content-Type', 'text/html')
- ->appendBody($content);
- ]]></programlisting>
- <para>
- By doing this, all headers get sent at once, just prior to
- displaying the content.
- </para>
- <note>
- <para>
- If using the action controller <link
- linkend="zend.controller.action.viewintegration">view
- integration</link>, you do not need to set the rendered view
- script content in the response object, as
- <methodname>Zend_Controller_Action::render()</methodname> does this by default.
- </para>
- </note>
- <para>
- Should an exception occur in an application, check the response object's
- <methodname>isException()</methodname> flag, and retrieve the exception using
- <methodname>getException()</methodname>. Additionally, one may create custom
- response objects that redirect to error pages, log exception messages,
- do pretty formatting of exception messages (for development
- environments), etc.
- </para>
- <para>
- You may retrieve the response object following the front controller
- <methodname>dispatch()</methodname>, or request the front controller to return the
- response object instead of rendering output.
- </para>
- <programlisting language="php"><![CDATA[
- // retrieve post-dispatch:
- $front->dispatch();
- $response = $front->getResponse();
- if ($response->isException()) {
- // log, mail, etc...
- }
- // Or, have the front controller dispatch() process return it
- $front->returnResponse(true);
- $response = $front->dispatch();
- // do some processing...
- // finally, echo the response
- $response->sendResponse();
- ]]></programlisting>
- <para>
- By default, exception messages are not displayed. This behaviour may be
- overridden by calling <methodname>renderExceptions()</methodname>, or enabling the
- front controller to <methodname>throwExceptions()</methodname>, as shown above:
- </para>
- <programlisting language="php"><![CDATA[
- $response->renderExceptions(true);
- $front->dispatch($request, $response);
- // or:
- $front->returnResponse(true);
- $response = $front->dispatch();
- $response->renderExceptions();
- $response->sendResponse();
- // or:
- $front->throwExceptions(true);
- $front->dispatch();
- ]]></programlisting>
- </sect2>
- <sect2 id="zend.controller.response.headers">
- <title>Manipulating Headers</title>
- <para>
- As stated previously, one aspect of the response object's duties is
- to collect and emit <acronym>HTTP</acronym> response headers. A variety of methods
- exist for this:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <methodname>canSendHeaders()</methodname> is used to determine if
- headers have already been sent. It takes an optional flag
- indicating whether or not to throw an exception if headers
- have already been sent. This can be overridden by setting
- the property <property>headersSentThrowsException</property> to
- <constant>FALSE</constant>.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>setHeader($name, $value, $replace = false)</methodname> is
- used to set an individual header. By default, it does not
- replace existing headers of the same name in the object;
- however, setting <varname>$replace</varname> to <constant>TRUE</constant> will
- force it to do so.
- </para>
- <para>
- Before setting the header, it checks with
- <methodname>canSendHeaders()</methodname> to see if this operation is
- allowed at this point, and requests that an exception be
- thrown.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>setRedirect($url, $code = 302)</methodname> sets an
- <acronym>HTTP</acronym> Location header for a redirect. If an
- <acronym>HTTP</acronym> status code has been provided, it will use that status
- code.
- </para>
- <para>
- Internally, it calls <methodname>setHeader()</methodname> with the
- <varname>$replace</varname> flag on to ensure only one such header
- is ever sent.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>getHeaders()</methodname> returns an array of all headers.
- Each array element is an array with the keys 'name' and
- 'value'.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>clearHeaders()</methodname> clears all registered headers.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>setRawHeader()</methodname> can be used to set headers that
- are not key and value pairs, such as an <acronym>HTTP</acronym> status header.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>getRawHeaders()</methodname> returns any registered raw
- headers.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>clearRawHeaders()</methodname> clears any registered raw
- headers.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>clearAllHeaders()</methodname> clears both regular key and value
- headers as well as raw headers.
- </para>
- </listitem>
- </itemizedlist>
- <para>
- In addition to the above methods, there are accessors for setting
- and retrieving the <acronym>HTTP</acronym> response code for the current request,
- <methodname>setHttpResponseCode()</methodname> and
- <methodname>getHttpResponseCode()</methodname>.
- </para>
- <sect3 id="zend.controller.response.headers.setcookie">
- <title>Setting Cookie Headers</title>
- <para>
- You can inject <acronym>HTTP</acronym> Set-Cookie headers into the response object
- of an action controller by using the provided header class
- <classname>Zend_Http_Header_SetCookie</classname>
- </para>
- <sect4 id="zend.controller.response.headers.setcookie.constructor">
- <title>Constructor Arguments</title>
- <para>
- The following table lists all constructor arguments of
- <classname>Zend_Http_Header_SetCookie</classname>
- in the order they are accepted. All arguments are optional,
- but name and value must be supplied via their setters if not
- passed in via the constructor or the resulting Set-Cookie header
- be invalid.
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <varname>$name</varname>: The name of the cookie
- </para>
- </listitem>
- <listitem>
- <para>
- <varname>$value</varname>: The value of the cookie
- </para>
- </listitem>
- <listitem>
- <para>
- <varname>$expires</varname>: The time the cookie expires
- </para>
- </listitem>
- <listitem>
- <para>
- <varname>$path</varname>: The path on the server in which
- the cookie will be available
- </para>
- </listitem>
- <listitem>
- <para>
- <varname>$domain</varname>: The domain to restrict cookie to
- </para>
- </listitem>
- <listitem>
- <para>
- <varname>$secure</varname>: boolean indicating whether cookie
- should be sent over an unencrypted connection (false) or via
- <acronym>HTTPS</acronym> only (true)
- </para>
- </listitem>
- <listitem>
- <para>
- <varname>$httpOnly</varname>: boolean indicating whether cookie
- should be transmitted only via the <acronym>HTTP</acronym> protocol
- </para>
- </listitem>
- <listitem>
- <para>
- <varname>$maxAge</varname>: The maximum age of the cookie in seconds
- </para>
- </listitem>
- <listitem>
- <para>
- <varname>$version</varname>: The cookie specification version
- </para>
- </listitem>
- </itemizedlist>
- <example>
- <title>Populate Zend_Http_Header_SetCookie via constructor and add to response</title>
- <programlisting language="php"><![CDATA[
- $this->getResponse()->setRawHeader(new Zend_Http_Header_SetCookie(
- 'foo', 'bar', NULL, '/', 'example.com', false, true
- ));
- ]]></programlisting>
- </example>
- <example>
- <title>Populate Zend_Http_Header_SetCookie via setters and add to response</title>
- <programlisting language="php"><![CDATA[
- $cookie = new Zend_Http_Header_SetCookie();
- $cookie->setName('foo')
- ->setValue('bar')
- ->setDomain('example.com')
- ->setPath('/')
- ->setHttponly(true);
- $this->getResponse()->setRawHeader($cookie);
- ]]></programlisting>
- </example>
- </sect4>
- </sect3>
- </sect2>
- <sect2 id="zend.controller.response.namedsegments">
- <title>Named Segments</title>
- <para>
- The response object has support for "named segments". This allows
- you to segregate body content into different segments and order
- those segments so output is returned in a specific order.
- Internally, body content is saved as an array, and the various
- accessor methods can be used to indicate placement and names within
- that array.
- </para>
- <para>
- As an example, you could use a <methodname>preDispatch()</methodname> hook to
- add a header to the response object, then have the action controller
- add body content, and a <methodname>postDispatch()</methodname> hook add a
- footer:
- </para>
- <programlisting language="php"><![CDATA[
- // Assume that this plugin class is registered with the front controller
- 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'));
- }
- }
- // a sample action controller
- class MyController extends Zend_Controller_Action
- {
- public function fooAction()
- {
- $this->render();
- }
- }
- ]]></programlisting>
- <para>
- In the above example, a call to <filename>/my/foo</filename> will cause the
- final body content of the response object to have the following
- structure:
- </para>
- <programlisting language="php"><![CDATA[
- array(
- 'header' => ..., // header content
- 'default' => ..., // body content from MyController::fooAction()
- 'footer' => ... // footer content
- );
- ]]></programlisting>
- <para>
- When this is rendered, it will render in the order in which elements
- are arranged in the array.
- </para>
- <para>
- A variety of methods can be used to manipulate the named segments:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <methodname>setBody()</methodname>
- allows you to pass a second value, <varname>$name</varname>,
- indicating a named segment. If you provide a segment name
- it will overwrite that specific named segment or
- create it if it does not exist (appending to the body array by
- default). If no named segment is passed to
- <methodname>setBody()</methodname>, it resets the entire body content
- array.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>appendBody()</methodname> also allows you to pass
- a second value, <varname>$name</varname>, indicating a named segment.
- If you provide a segment name it will append the supplied content
- to the existing content in the named segment, or create the segment
- if it does not exist (appending to the body array by
- default). If no named segment is passed to
- <methodname>appendBody()</methodname>, it will append the supplied
- content to the named segment 'default', creating it if it does not
- already exist.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>prepend($name, $content)</methodname> will create a segment
- named <varname>$name</varname> and place it at the beginning of
- the array. If the segment exists already, it will be removed
- prior to the operation (i.e., overwritten and replaced).
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>append($name, $content)</methodname> will create a segment
- named <varname>$name</varname> and place it at the end of
- the array. If the segment exists already, it will be removed
- prior to the operation (i.e., overwritten and replaced).
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>insert($name, $content, $parent = null, $before =
- false)</methodname> will create a segment named
- <varname>$name</varname>. If provided with a <varname>$parent</varname>
- segment, the new segment will be placed either before or
- after that segment (based on the value of
- <varname>$before</varname>) in the array. If the segment exists
- already, it will be removed prior to the operation (i.e.,
- overwritten and replaced).
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>clearBody($name = null)</methodname> will remove a single
- named segment if a <varname>$name</varname> is provided (and the
- entire array otherwise).
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>getBody($spec = false)</methodname> can be used to retrieve a
- single array segment if <varname>$spec</varname> is the name of a named
- segment. If <varname>$spec</varname> is <constant>FALSE</constant>, it returns
- a string formed by concatenating all named segments in order. If
- <varname>$spec</varname> is <constant>TRUE</constant>, it returns the body
- content array.
- </para>
- </listitem>
- </itemizedlist>
- </sect2>
- <sect2 id="zend.controller.response.exceptions">
- <title>Testing for Exceptions in the Response Object</title>
- <para>
- As mentioned earlier, by default, exceptions caught during dispatch
- are registered with the response object. Exceptions are registered
- in a stack, which allows you to keep all exceptions thrown --
- application exceptions, dispatch exceptions, plugin exceptions, etc.
- Should you wish to check for particular exceptions or to log
- exceptions, you'll want to use the response object's exception <acronym>API</acronym>:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <methodname>setException(Exception $e)</methodname> allows you to
- register an exception.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>isException()</methodname> will tell you if an exception has
- been registered.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>getException()</methodname> returns the entire
- exception stack.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>hasExceptionOfType($type)</methodname> allows you to
- determine if an exception of a particular class is in the
- stack.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>hasExceptionOfMessage($message)</methodname> allows you to
- determine if an exception with a specific message is in the
- stack.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>hasExceptionOfCode($code)</methodname> allows you to
- determine if an exception with a specific code is in the
- stack.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>getExceptionByType($type)</methodname> allows you to
- retrieve all exceptions of a specific class from the stack.
- It will return <constant>FALSE</constant> if none are found, and an array of
- exceptions otherwise.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>getExceptionByMessage($message)</methodname> allows you to
- retrieve all exceptions with a specific message from the stack.
- It will return <constant>FALSE</constant> if none are found, and an array of
- exceptions otherwise.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>getExceptionByCode($code)</methodname> allows you to
- retrieve all exceptions with a specific code from the stack.
- It will return <constant>FALSE</constant> if none are found, and an array of
- exceptions otherwise.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>renderExceptions($flag)</methodname> allows you to set a
- flag indicating whether or not exceptions should be emitted
- when the response is sent.
- </para>
- </listitem>
- </itemizedlist>
- </sect2>
- <sect2 id="zend.controller.response.subclassing">
- <title>Subclassing the Response Object</title>
- <para>
- The purpose of the response object is to collect headers and content
- from the various actions and plugins and return them to the client;
- secondarily, it also collects any errors (exceptions) that occur in
- order to process them, return them, or hide them from the end user.
- </para>
- <para>
- The base response class is
- <classname>Zend_Controller_Response_Abstract</classname>, and any subclass you
- create should extend that class or one of its derivatives. The
- various methods available have been listed in the previous sections.
- </para>
- <para>
- Reasons to subclass the response object include modifying how output
- is returned based on the request environment (e.g., not sending
- headers for <acronym>CLI</acronym> or <acronym>PHP</acronym>-<acronym>GTK</acronym>
- requests), adding functionality to return a final view based on content stored in named
- segments, etc.
- </para>
- </sect2>
- </sect1>
- <!--
- vim:se ts=4 sw=4 et:
- -->
|