Skip to end of metadata
Go to start of metadata

<p>Currently we have Zend\Mvc\Application attaching 3 Listeners:<br />
RouteListener, DisptachListener and ViewManager.<br />
The ViewManager registers the SendResponseListener and the latter does:</p>
<ac:macro ac:name="code"><ac:parameter ac:name="language">php</ac:parameter><ac:plain-text-body><![CDATA[
if (is_callable(array($response,'send'))) {
return $response->send();
}
]]></ac:plain-text-body></ac:macro>
<p>As Zend\Http\PhpEnvironment\Response and Console\Response are the only response objects, which have a send() method, these are the only responses you can send in zf2.</p>

<p>However you can return any response object in your controller, the mvc dispatch will get short-circuited and you end up in the MvcEvent::EVENT_FINISH event. No response will be send. So when you return a StreamResponse in your controller (for file download in secured area f.e.), the stream will not get send.</p>

<p>The whole work-flow is a bit ugly. First the SendResponseListener should not be in Zend\Mvc\View and instead in Zend\Mvc. Sending a response is not part of the view layer. It's part of the mvc layer. The response object should not be responsible for sending itself. We have a SendResponseListener, it should be it's job.</p>

<p>Implementation changes:</p>

<p>1) Move the SendResponseListener one level up (Mvc Package, no View subpackage).<br />
2) Mark the send() method from Environment\Response and Console\Response as deprecated<br />
3) SendResponseListener triggers sendResponse event, additional listener (PhpEnvironmentResponseSender, CliResponseSender) will get attached to the sendrepsonselistener by default.<br />
4) Remove the registration of the SendResponseListener from ViewManager<br />
5) Add the SendResponseListener registration in Zend\Mvc\Application</p>

<p>Example use case:</p>

<ac:macro ac:name="code"><ac:parameter ac:name="language">php</ac:parameter><ac:plain-text-body><![CDATA[
<?php

namespace Application\Controller;

use Zend\Http\Headers;
use Zend\Http\Response\Stream;
use Zend\Mvc\Controller\AbstractActionController;

class FileController extends AbstractActionController
{
public function fileAction()

Unknown macro: { // some more code $response = new Stream(); $response->setStream($file->getResource()); $headers = new Headers(); $headers->addHeaders(array( 'Content-Disposition' => 'attachment; filename="' . $file->getFilename() .'"', 'Content-Type' => 'application/octet-stream' )); $response->setHeaders($headers); return $response; }

}
]]></ac:plain-text-body></ac:macro>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Nov 28, 2012

    <p><ac:emoticon ac:name="thumbs-up" /> It sounds plausible.</p>

  2. Nov 28, 2012

    <p>Some of this is already fixed due to: <a href="https://github.com/zendframework/zf2/pull/2981">Controller action HttpResponse is not used by SendResponseListener</a></p>

    <p>See <a href="https://github.com/zendframework/zf2/blob/master/library/Zend/Mvc/Application.php">https://github.com/zendframework/zf2/blob/master/library/Zend/Mvc/Application.php</a></p>

    <p>After a little more consideration, I started to wonder if the short circuit method itself should inject the returned response into the event object instead.</p>

    <p>An issue regarding moving the listener was created from Matthew's request: <a href="https://github.com/zendframework/zf2/issues/2992">Remove SendResponseListener from View Manager</a>.</p>

    <p>Another thought that comes to mind, is that the Application run() method returns a response. How do we easily control whether run() should automatically send the response, otherwise what's the point in returning it?</p>

    1. Nov 28, 2012

      <p>The application run() method returns a response object that can be used otherwise. e.g. you can put it in a http client and send it somewhere else, instead of giving output to the browser. You can ommit sending the response, by detaching the sendresponselistener.</p>

  3. Nov 29, 2012

    <p>I submitted an incomplete PR at <a class="external-link" href="https://github.com/zendframework/zf2/pull/3105">https://github.com/zendframework/zf2/pull/3105</a>.<br />
    It handles default http response sending.<br />
    Todo:</p>
    <ul class="alternate">
    <li>send stream response</li>
    <li>send console response</li>
    <li>add tests</li>
    </ul>

  4. Dec 03, 2012

    <p>First: I agree that the <code>SendResponseListener</code> should not be managed by the <code>ViewManager</code>. In fact, most of the services managed by the <code>ViewManager</code> should be moved to separate services.</p>

    <p>Second, I also agree with moving the <code>send()</code> functionality into the listener itself. As you note, it solves several problems, such as using an MVC endpoint as a proxy to another web service.</p>

    <p>That said, there are a few issues in how this is proposed, and how you've implemented it in the pull request.</p>

    <p>First, the <code>PhpEnvironment</code> response object is meant to encapsulate the response as implemented by PHP. This means artifacts such as <code>headersSent()</code> need to remain intact. Also, we can <em>deprecate</em> usage of things like <code>sendHeaders()</code> and <code>sendContent()</code>, but to retain backwards compatibility (particularly as these may be used outside the ZF2 MVC), we need to retain them. As such, the proposed implementation needs to simply mark them as deprecated (<strong>not</strong> remove them).</p>

    <p>Second, because the <code>SendResponseListener</code> class already exists, we cannot simply rename it. What we <em>can</em> do, however, is have it extend from a new class, and mark it as <em>deprecated</em>. This allows others who have already extended it (yes, you <em>can</em> extend it!) to continue to operate without needing to change their code.</p>

    <p>If those changes are made, I will definitely support this RFC – and sooner, rather than later, at that!</p>

    1. Dec 03, 2012

      <p>@Matthew Weier O'Phinney Thank you for your comments. I will integrate the changes in the PR and put an additional (basic) StreamResponseSender implementation very soon (perhabs already this evening).</p>

  5. Dec 03, 2012

    <p>The proposed implementation details changed a little bit in the PR, see: <a class="external-link" href="https://github.com/zendframework/zf2/pull/3105">https://github.com/zendframework/zf2/pull/3105</a>.<br />
    I will update this document accordingly soon.</p>

    <p>Open todos:</p>
    <ul class="alternate">
    <li>send stream response</li>
    </ul>

    <p>Open questions:</p>
    <ul class="alternate">
    <li>How and should we use headers_sent() ?</li>
    <li>Should we also be able to send Zend\Http\Response or only support sending of Zend\Http\PhpEnvironment\Response ?</li>
    </ul>

  6. Dec 17, 2012

    <p>Updated this document today. PR is ready for review. I will add another PR for the stream response sender.</p>