Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

RFC Send Response Workflow #3105

Merged
merged 9 commits into from

3 participants

@weierophinney weierophinney commented on the diff
library/Zend/Http/PhpEnvironment/Response.php
@@ -30,16 +30,6 @@ class Response extends HttpResponse
@weierophinney Owner

The changes in this file are backwards incompatible and cannot be made in ZF2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ry/Zend/Mvc/ResponseSender/AbstractResponseSender.php
((34 lines not shown))
+
+ /**
+ * Set response
+ *
+ * @param ResponseInterface $response
+ * @return void
+ */
+ public function setResponse(ResponseInterface $response)
+ {
+ $this->response = $response;
+ }
+
+ /**
+ * @return bool
+ */
+ public function headersSent()
@weierophinney Owner

This would be better to remain in Zend\Http\PhpEnvironment\Response, as that class is meant to be an encapsulation of the current HTTP response environment within PHP -- in other words, that class should be checking for whether or not headers are sent already, etc. The response sender would simply check that information in the response object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/Mvc/ResponseSender/HttpResponseSender.php
((2 lines not shown))
+
+namespace Zend\Mvc\ResponseSender;
+
+use Zend\Http\Header\MultipleHeaderInterface;
+
+class HttpResponseSender extends AbstractResponseSender
+{
+ /**
+ * Send HTTP headers
+ *
+ * @return HttpResponseSender
+ */
+ public function sendHeaders()
+ {
+ if ($this->headersSent()) {
+ return $this;
@weierophinney Owner

I'm wondering if we shouldn't trigger an event in this situation, as we potentially might end up omitting some headers if we detect headers have already been sent. A separate flag could track whether this class has sent them, vs. whether the response reports having sent them (via headers_sent).

@weierophinney Owner

Also, this implementation is naive, as it does not check headers_sent(). While I advocate moving that check back to the response object, nevertheless, we need to check it somewhere, and this class does not check it all.

@prolic
prolic added a note

@weierophinney how do you think we should handle this? After calling "header($someHeaderString)" there are still no headers sent and "headers_sent()" returns false. Headers are sent, when content gets sent, too. When we change the implementation to use "headers_sent()", it would no longer mean, that "header($someHeaderString)" was called.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/Mvc/SendResponseListener.php
((10 lines not shown))
}
+
+ $responseSender = $this->getServiceLocator()->get($this->options[$responseType]);
@weierophinney Owner

Why make this class service locator aware? If it is to have a factory in the ServiceManager, why not simply inject the response object instead of pulling it from the SM?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@weierophinney

I've also left comments on the RFC, @prolic

@prolic prolic updated send response workflow
- implemented changes proposed by weierophinney
- reverted changes in Console\Response and PhpEnvironment\Response objects
- added Zend\Mvc\View\SendResponseListener extending the new class
- added ResponseListenerFactory
- Response sender triggers events
c2cf0be
@prolic

This also fixes a so far unknown issue: If your controller returns a custom response object (e.g. return new StreamResponse), this response object was not set in Zend\Mvc\Application and also not updated in Zend\ServiceManager\ServiceManager. When you call $serviceManager->get('Response') after returning a custom response in controller, you will now get the correct response object. (see changes in Zend\Mvc\Application)

...ary/Zend/Mvc/ResponseSender/ConsoleResponseSender.php
((6 lines not shown))
+{
+ /**
+ * Send content
+ *
+ * @return ConsoleResponseSender
+ */
+ public function sendContent()
+ {
+ $this->getEventManager()->trigger(self::EVENT_SEND_CONTENT, $this);
+ $response = $this->getResponse();
+ /* @var $response \Zend\Console\Response */
+ if ($response->contentSent()) {
+ return $this;
+ }
+ echo $response->getContent();
+ $response->setContentSent(true);
@weierophinney Owner

I wouldn't track this in the response. I'd instead do a hash map of splobjectid => flag -- you can then check the given $response object against that to see if it has been sent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...d/Mvc/ResponseSender/PhpEnvironmentResponseSender.php
((39 lines not shown))
+ * Send content
+ *
+ * @triggers sendContent
+ * @return PhpEnvironmentResponseSender
+ */
+ public function sendContent()
+ {
+ $this->getEventManager()->trigger(self::EVENT_SEND_CONTENT, $this);
+ $response = $this->getResponse();
+ /* @var $response \Zend\Http\PhpEnvironment\Response */
+ if ($response->contentSent()) {
+ return $this;
+ }
+
+ echo $response->getContent();
+ $response->setContentSent(true);
@weierophinney Owner

I wouldn't track this in the response. I'd instead do a hash map of splobjectid => flag -- you can then check the given $response object against that to see if it has been sent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/Mvc/SendResponseListener.php
((74 lines not shown))
+ public function detach(EventManagerInterface $events)
+ {
+ foreach ($this->listeners as $index => $listener) {
+ if ($events->detach($listener)) {
+ unset($this->listeners[$index]);
+ }
+ }
+ }
+
+ /**
+ * Inject response sender
+ *
+ * @param MvcEvent $e
+ * @return void
+ */
+ public function injectResponseSender(MvcEvent $e)
@weierophinney Owner

This method does not make sense to me. Why not simply register the ResponseSender, trigger it, and if no response is present, return early? Or is there some advantage to registering late?

@prolic
prolic added a note

When the SendResponseListener gets instantiated, the response object given could change, e.g. the controller returns a newly created response object. The concrete response sender is selected, based on the classname of the current response object. If you return a custom response in your controller, the send response listener would not be able to send the correct response.
Alternative implementation would be: make this class ServiceLocatorAware!

@weierophinney Owner

The MvcEvent has the current response object -- if the controller returned one, then that's what will be in the Event at this point. As such, instead of setting the Response as a property of the ResponseSender, you'd simply pass it as an argument to the ResponseSender's sendResponse() method.

I guess my point is: shouldn't a typical ResponseSender implementation be able to handle any response type, or at least raise an error if it cannot? And if you will be using a custom type, you could potentially inject the SendResponseListener manually with an approriate ResponseSender; we could even create a controller plugin and/or application listeners around this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/Mvc/Application.php
@@ -328,4 +329,19 @@ protected function completeRequest(MvcEvent $event)
$events->trigger(MvcEvent::EVENT_FINISH, $event);
return $event->getResponse();
}
+
+ /**
+ * Set response object in mvc event and service manager
+ *
+ * @param \Zend\Stdlib\ResponseInterface $response
+ */
+ protected function setResponse(ResponseInterface $response)
+ {
+ $serviceManager = $this->getServiceManager();
+ $serviceManager->setAllowOverride(true);
+ $serviceManager->setService('Response', $response);
+ $serviceManager->setAllowOverride(false);
@weierophinney Owner

Why are the above lines necessary?

@prolic
prolic added a note

If the controller returns a custom response object (e.g. return new StreamResponse), the wrong (old one) response object is registered in Zend\Mvc\Application and Zend\ServiceManager\ServiceManager.
When I try to create the correct response sender object, it would get the wrong response object injected.

@weierophinney Owner

How?

The point is that the response composed in the event is what should be sent, not the one in the application object.

@weierophinney Owner

In fact, a number of PRs were merged for 2.0.4 for precisely that reason: to ensure that the Response object composed in the MvcEvent object is the one returned by a listener. That object is the correct one to introspect at any given time, not the one in the service manager or the application instance. (I'd argue that we likely should not register the Request/Response objects in those classes.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/Http/PhpEnvironment/Response.php
@@ -85,9 +85,22 @@ public function contentSent()
}
/**
+ * Set content sent
+ *
+ * @param bool $flag
+ * @return Response
+ */
+ public function setContentSent($flag)
+ {
+ $this->contentSent = (bool) $flag;
+ return $this;
+ }
@weierophinney Owner

Per previous comments, track this in the SendResponseListener.

@prolic
prolic added a note

Really in SendResponseListener and not in ResponseSender?

@weierophinney Owner

Sorry, meant the ResponseSender itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
library/Zend/Console/Response.php
((15 lines not shown))
public function contentSent()
{
return $this->contentSent;
}
/**
+ * Set content sent
+ *
+ * @param $flag
+ * @return Response
+ */
+ public function setContentSent($flag)
+ {
+ $this->contentSent = (bool) $flag;
+ return $this;
+ }
@weierophinney Owner

Per previous comments, track this in the SendResponse listener.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@prolic

About headers_sent():

@weierophinney how do you think we should handle this? After calling "header($someHeaderString)" there are still no headers sent and "headers_sent()" returns false. Headers are sent, when content gets sent, too. When we change the implementation to use "headers_sent()", it would no longer mean, that "header($someHeaderString)" was called.

@prolic

RFC document at http://framework.zend.com/wiki/display/ZFDEV2/RFC+-+Send+response+workflow needs to be updated. However the open questions remain:

  • How and should we use headers_sent() ?
  • Should we also be able to send Zend\Http\Response or only support sending of Zend\Http\PhpEnvironment\Response ?
@weierophinney

@prolic re: headers_sent() -- I'd use a two-step approach:

  1. First, check the status of headers_sent()
  2. Second, check if we've sent the headers from this specific Response object before (this is the bit where I recommended keeping a hash map of the SplObject ID/sent status in a previous comment).

Basically, headers_sent() only returns true if we have also started sending content. So the first check is for that -- because you'll get a PHP notice if that's the case, and we don't want that. The second check is to ensure that if the same object is passed to the routine again, we don't try and send the headers a second time.

...ary/Zend/Mvc/ResponseSender/ConsoleResponseSender.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Zend\Mvc\ResponseSender;
+
+class ConsoleResponseSender extends AbstractResponseSender
+{
+ /**
+ * Send content
+ *
+ * @return ConsoleResponseSender
+ */
+ public function sendContent()
+ {
+ $this->getEventManager()->trigger(self::EVENT_SEND_CONTENT, $this);
@weierophinney Owner

Why is this needed, exactly?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ary/Zend/Mvc/ResponseSender/ConsoleResponseSender.php
((17 lines not shown))
+ if ($response->contentSent()) {
+ return $this;
+ }
+ echo $response->getContent();
+ $response->setContentSent(true);
+ return $this;
+ }
+
+ /**
+ * Send the response
+ *
+ * @return void
+ */
+ public function sendResponse()
+ {
+ $this->getEventManager()->trigger(self::EVENT_SEND_RESPONSE, $this);
@weierophinney Owner

What use cases do you have for this event?

@prolic
prolic added a note

Why should we then even trigger the SEND_HEADERS event? We can also register a listener on MVCs EVENT_FINISH and forget about SEND_HEADERS event. You can also remove there unneeded/ unwished headers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@weierophinney

@prolic So, only one question really remains in your list: if we should allow sending alternate Response types. My answer is yes. There are a couple ways to manage this:

  1. We have the ResponseSender delegate to different methods based on Response type
  2. We use an event, and allow attaching to that event. This way, we can provide some standard listeners for known Response types -- Console, PhpEnvironment, Stream -- and the first one to report having sent the response causes the event to stop propagation.

The first is easier and faster to implement, but less flexible. The second will take more time, and require more objects, but be completely flexible.

@prolic

@weierophinney thanks for your input. I will update this PR and the RFC document tomorrow.

@prolic prolic refactored send response workflow
- added SendResponseEvent
- ResponseSender listen for an event
- first response sender that can send the response, stops propagation
- send response listener just triggers event, sends no response
- SendResponseEvent tracks which response headers and content were sent
- send response listener attaches default listeners (phpenvironmentresponse and consoleresponse, stream response sender is in progress)
- removed AbstractResponseSender
011ecde
@prolic

updated worflow, document still out of date, see last commit message

@coss

Should be EVENT_SEND_RESPONSE to be consistent with the MvcEvent::EVENT_ constants.

@prolic prolic added phpdoc
added default name in SendResponseEvent
removed unused factories
2d48e88
@prolic

todo: add stream response sender, write tests

@weierophinney

@prolic Let me know when this is ready to review again.

@prolic

I will add the stream response sender in a separate PR, as there is probably more to discuss. All tests are running again. Ready for review.

@weierophinney weierophinney merged commit a18deed into zendframework:develop
@weierophinney weierophinney referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 29, 2012
  1. @prolic
  2. @prolic

    cs fixes

    prolic authored
Commits on Dec 3, 2012
  1. @prolic

    updated send response workflow

    prolic authored
    - implemented changes proposed by weierophinney
    - reverted changes in Console\Response and PhpEnvironment\Response objects
    - added Zend\Mvc\View\SendResponseListener extending the new class
    - added ResponseListenerFactory
    - Response sender triggers events
  2. @prolic

    removed debug calls

    prolic authored
Commits on Dec 7, 2012
  1. @prolic

    refactored send response workflow

    prolic authored
    - added SendResponseEvent
    - ResponseSender listen for an event
    - first response sender that can send the response, stops propagation
    - send response listener just triggers event, sends no response
    - SendResponseEvent tracks which response headers and content were sent
    - send response listener attaches default listeners (phpenvironmentresponse and consoleresponse, stream response sender is in progress)
    - removed AbstractResponseSender
Commits on Dec 9, 2012
  1. @prolic

    added phpdoc

    prolic authored
    added default name in SendResponseEvent
    removed unused factories
Commits on Dec 12, 2012
  1. @prolic
Commits on Dec 16, 2012
  1. @prolic

    added tests, some cleanup

    prolic authored
  2. @prolic

    cleanup in SendResponseListener

    prolic authored
This page is out of date. Refresh to see the latest.
View
20 library/Zend/Console/Response.php
@@ -19,8 +19,18 @@
*/
class Response extends Message implements ResponseInterface
{
+
+ /**
+ * @var bool
+ */
protected $contentSent = false;
+ /**
+ * Check if content was sent
+ *
+ * @return bool
+ * @deprecated
+ */
public function contentSent()
{
return $this->contentSent;
@@ -48,6 +58,12 @@ public function getErrorLevel()
return $this->getMetadata('errorLevel', 0);
}
+ /**
+ * Send content
+ *
+ * @return Response
+ * @deprecated
+ */
public function sendContent()
{
if ($this->contentSent()) {
@@ -58,10 +74,14 @@ public function sendContent()
return $this;
}
+ /**
+ * @deprecated
+ */
public function send()
{
$this->sendContent();
$errorLevel = (int)$this->getMetadata('errorLevel',0);
exit($errorLevel);
}
+
}
View
11 library/Zend/Http/PhpEnvironment/Response.php
@@ -32,11 +32,6 @@ class Response extends HttpResponse
/**
* @var bool
*/
- protected $headersSent = false;
-
- /**
- * @var bool
- */
protected $contentSent = false;
/**
@@ -73,11 +68,12 @@ protected function detectVersion()
*/
public function headersSent()
{
- return $this->headersSent;
+ return headers_sent();
}
/**
* @return bool
+ * @deprecated
*/
public function contentSent()
{
@@ -88,6 +84,7 @@ public function contentSent()
* Send HTTP headers
*
* @return Response
+ * @deprecated
*/
public function sendHeaders()
{
@@ -115,6 +112,7 @@ public function sendHeaders()
* Send content
*
* @return Response
+ * @deprecated
*/
public function sendContent()
{
@@ -131,6 +129,7 @@ public function sendContent()
* Send HTTP response
*
* @return Response
+ * @deprecated
*/
public function send()
{
View
1  library/Zend/Mvc/Application.php
@@ -132,6 +132,7 @@ public function bootstrap()
$events->attach($serviceManager->get('RouteListener'));
$events->attach($serviceManager->get('DispatchListener'));
$events->attach($serviceManager->get('ViewManager'));
+ $events->attach($serviceManager->get('SendResponseListener'));
// Setup MVC Event
$this->event = $event = new MvcEvent();
View
56 library/Zend/Mvc/ResponseSender/ConsoleResponseSender.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Mvc
+ */
+
+namespace Zend\Mvc\ResponseSender;
+
+use Zend\Console\Response;
+
+/**
+ * @category Zend
+ * @package Zend_Mvc
+ * @subpackage ResponseSender
+ */
+class ConsoleResponseSender implements ResponseSenderInterface
+{
+ /**
+ * Send content
+ *
+ * @param SendResponseEvent $event
+ * @return ConsoleResponseSender
+ */
+ public function sendContent(SendResponseEvent $event)
+ {
+ if ($event->contentSent()) {
+ return $this;
+ }
+ $response = $event->getResponse();
+ echo $response->getContent();
+ $event->setContentSent();
+ return $this;
+ }
+
+ /**
+ * Send the response
+ *
+ * @param SendResponseEvent $event
+ * @return void
+ */
+ public function __invoke(SendResponseEvent $event)
+ {
+ $response = $event->getResponse();
+ if ($response instanceof Response) {
+ $this->sendContent($response);
+ $errorLevel = (int) $response->getMetadata('errorLevel',0);
+ $event->stopPropagation(true);
+ exit($errorLevel);
+ }
+ }
+
+}
View
84 library/Zend/Mvc/ResponseSender/PhpEnvironmentResponseSender.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Mvc
+ */
+
+namespace Zend\Mvc\ResponseSender;
+
+use Zend\Mvc\ResponseSender\SendResponseEvent;
+use Zend\Http\Header\MultipleHeaderInterface;
+use Zend\Http\PhpEnvironment\Response;
+
+/**
+ * @category Zend
+ * @package Zend_Mvc
+ * @subpackage ResponseSender
+ */
+class PhpEnvironmentResponseSender implements ResponseSenderInterface
+{
+ /**
+ * Send HTTP headers
+ *
+ * @param SendResponseEvent $event
+ * @return PhpEnvironmentResponseSender
+ */
+ public function sendHeaders(SendResponseEvent $event)
+ {
+ $response = $event->getResponse();
+ if ($response->headersSent() || $event->headersSent()) {
+ return $this;
+ }
+ $status = $response->renderStatusLine();
+ header($status);
+ /* @var \Zend\Http\Header\HeaderInterface $header */
+ foreach ($response->getHeaders() as $header) {
+ if ($header instanceof MultipleHeaderInterface) {
+ header($header->toString(), false);
+ continue;
+ }
+ header($header->toString());
+ }
+ $event->setHeadersSent();
+ return $this;
+ }
+
+ /**
+ * Send content
+ *
+ * @param SendResponseEvent $event
+ * @return PhpEnvironmentResponseSender
+ */
+ public function sendContent(SendResponseEvent $event)
+ {
+ if ($event->contentSent()) {
+ return $this;
+ }
+ $response = $event->getResponse();
+ echo $response->getContent();
+ $event->setContentSent();
+ return $this;
+ }
+
+ /**
+ * Send HTTP response
+ *
+ * @param SendResponseEvent $event
+ * @return PhpEnvironmentResponseSender
+ */
+ public function __invoke(SendResponseEvent $event)
+ {
+ $response = $event->getResponse();
+ if ($response instanceof Response) {
+ $this->sendHeaders($event)
+ ->sendContent($event);
+ $event->stopPropagation(true);
+ }
+ return $this;
+ }
+
+}
View
30 library/Zend/Mvc/ResponseSender/ResponseSenderInterface.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Mvc
+ */
+
+namespace Zend\Mvc\ResponseSender;
+
+use Zend\Mvc\ResponseSender\SendResponseEvent;;
+
+/**
+ * @category Zend
+ * @package Zend_Mvc
+ * @subpackage ResponseSender
+ */
+interface ResponseSenderInterface
+{
+ /**
+ * Send the response
+ *
+ * @param SendResponseEvent $event
+ * @return void
+ */
+ public function __invoke(SendResponseEvent $event);
+
+}
View
124 library/Zend/Mvc/ResponseSender/SendResponseEvent.php
@@ -0,0 +1,124 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Mvc
+ */
+
+namespace Zend\Mvc\ResponseSender;
+
+use Zend\EventManager\Event;
+use Zend\Stdlib\ResponseInterface;
+
+/**
+ * @category Zend
+ * @package Zend_Mvc
+ * @subpackage ResponseSender
+ */
+class SendResponseEvent extends Event
+{
+ /**#@+
+ * Send response events triggered by eventmanager
+ */
+ const EVENT_SEND_RESPONSE = 'sendResponse';
+ /**#@-*/
+
+ /**
+ * @var string Event name
+ */
+ protected $name = 'sendResponse';
+
+ /**
+ * @var ResponseInterface
+ */
+ protected $response;
+
+ /**
+ * @var array
+ */
+ protected $headersSent = array();
+
+ /**
+ * @var array
+ */
+ protected $contentSent = array();
+
+ /**
+ * @param ResponseInterface $response
+ * @return SendResponseEvent
+ */
+ public function setResponse(ResponseInterface $response)
+ {
+ $this->setParam('response', $response);
+ $this->response = $response;
+ return $this;
+ }
+
+ /**
+ * @return \Zend\Stdlib\ResponseInterface
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+
+ /**
+ * Set content sent for current response
+ *
+ * @return SendResponseEvent
+ */
+ public function setContentSent()
+ {
+ $response = $this->getResponse();
+ $contentSent = $this->getParam('contentSent', array());
+ $contentSent[spl_object_hash($response)] = true;
+ $this->setParam('contentSent', $contentSent);
+ $this->contentSent[spl_object_hash($response)] = true;
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function contentSent()
+ {
+ $response = $this->getResponse();
+ if (isset($this->contentSent[spl_object_hash($response)])
+ && true === $this->contentSent[spl_object_hash($response)]) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Set headers sent for current response object
+ *
+ * @return SendResponseEvent
+ */
+ public function setHeadersSent()
+ {
+ $response = $this->getResponse();
+ $headersSent = $this->getParam('headersSent', array());
+ $headersSent[spl_object_hash($response)] = true;
+ $this->setParam('headersSent', $headersSent);
+ $this->headersSent[spl_object_hash($response)] = true;
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function headersSent()
+ {
+ $response = $this->getResponse();
+ if (isset($this->headersSent[spl_object_hash($response)])
+ && true === $this->headersSent[spl_object_hash($response)]) {
+ return true;
+ }
+ return false;
+ }
+
+}
View
144 library/Zend/Mvc/SendResponseListener.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Mvc
+ */
+
+namespace Zend\Mvc;
+
+use Zend\EventManager\EventManager;
+use Zend\EventManager\EventManagerAwareInterface;
+use Zend\EventManager\EventManagerInterface;
+use Zend\EventManager\ListenerAggregateInterface;
+use Zend\Mvc\MvcEvent;
+use Zend\Mvc\ResponseSender\ConsoleResponseSender;
+use Zend\Mvc\ResponseSender\PhpEnvironmentResponseSender;
+use Zend\Mvc\ResponseSender\SendResponseEvent;
+use Zend\Stdlib\ResponseInterface as Response;
+
+/**
+ * @category Zend
+ * @package Zend_Mvc
+ */
+class SendResponseListener implements
+ EventManagerAwareInterface,
+ ListenerAggregateInterface
+{
+
+ /**
+ * @var \Zend\Stdlib\CallbackHandler[]
+ */
+ protected $listeners = array();
+
+ /**
+ * @var SendResponseEvent
+ */
+ protected $event;
+
+ /**
+ * @var EventManagerInterface
+ */
+ protected $eventManager;
+
+ /**
+ * Constructor
+ */
+ public function __construct()
+ {
+ $this->event = new SendResponseEvent();
+ }
+
+ /**
+ * Inject an EventManager instance
+ *
+ * @param EventManagerInterface $eventManager
+ * @return SendResponseListener
+ */
+ public function setEventManager(EventManagerInterface $eventManager)
+ {
+ $eventManager->setIdentifiers(array(
+ __CLASS__,
+ get_called_class(),
+ ));
+ $this->eventManager = $eventManager;
+ $this->attachDefaultListeners();
+ return $this;
+ }
+
+ /**
+ * Retrieve the event manager
+ *
+ * Lazy-loads an EventManager instance if none registered.
+ *
+ * @return EventManagerInterface
+ */
+ public function getEventManager()
+ {
+ if (!$this->eventManager instanceof EventManagerInterface) {
+ $this->setEventManager(new EventManager());
+ }
+ return $this->eventManager;
+ }
+
+
+ /**
+ * Attach the aggregate to the specified event manager
+ *
+ * @param EventManagerInterface $events
+ * @return void
+ */
+ public function attach(EventManagerInterface $events)
+ {
+ $this->listeners[] = $events->attach(MvcEvent::EVENT_FINISH, array($this, 'sendResponse'), -10000);
+ }
+
+ /**
+ * Detach aggregate listeners from the specified event manager
+ *
+ * @param EventManagerInterface $events
+ * @return void
+ */
+ public function detach(EventManagerInterface $events)
+ {
+ foreach ($this->listeners as $index => $listener) {
+ if ($events->detach($listener)) {
+ unset($this->listeners[$index]);
+ }
+ }
+ }
+
+ /**
+ * Send the response
+ *
+ * @param MvcEvent $e
+ * @return void
+ */
+ public function sendResponse(MvcEvent $e)
+ {
+ $response = $e->getResponse();
+ if (!$response instanceof Response) {
+ return; // there is no response to send
+ }
+ $event = $this->event;
+ $event->setResponse($response);
+ $event->setTarget($this);
+ $this->getEventManager()->trigger($event);
+ }
+
+ /**
+ * Register the default event listeners
+ *
+ * @return SendResponseListener
+ */
+ protected function attachDefaultListeners()
+ {
+ $events = $this->getEventManager();
+ $events->attach(SendResponseEvent::EVENT_SEND_RESPONSE, new PhpEnvironmentResponseSender(), -1000);
+ $events->attach(SendResponseEvent::EVENT_SEND_RESPONSE, new ConsoleResponseSender(), -2000);
+ }
+
+}
View
5 library/Zend/Mvc/Service/ServiceListenerFactory.php
@@ -41,8 +41,9 @@ class ServiceListenerFactory implements FactoryInterface
*/
protected $defaultServiceConfig = array(
'invokables' => array(
- 'DispatchListener' => 'Zend\Mvc\DispatchListener',
- 'RouteListener' => 'Zend\Mvc\RouteListener',
+ 'DispatchListener' => 'Zend\Mvc\DispatchListener',
+ 'RouteListener' => 'Zend\Mvc\RouteListener',
+ 'SendResponseListener' => 'Zend\Mvc\SendResponseListener'
),
'factories' => array(
'Application' => 'Zend\Mvc\Service\ApplicationFactory',
View
3  library/Zend/Mvc/View/Console/ViewManager.php
@@ -13,7 +13,6 @@
use ArrayAccess;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\View\Http\ViewManager as BaseViewManager;
-use Zend\Mvc\View\SendResponseListener;
/**
* Prepares the view layer for console applications
@@ -53,7 +52,6 @@ public function onBootstrap($event)
$mvcRenderingStrategy = $this->getMvcRenderingStrategy();
$createViewModelListener = new CreateViewModelListener();
$injectViewModelListener = new InjectViewModelListener();
- $sendResponseListener = new SendResponseListener();
$injectParamsListener = new InjectNamedConsoleParamsListener();
$this->registerMvcRenderingStrategies($events);
@@ -63,7 +61,6 @@ public function onBootstrap($event)
$events->attach($exceptionStrategy);
$events->attach(MvcEvent::EVENT_DISPATCH_ERROR, array($injectViewModelListener, 'injectViewModel'), -100);
$events->attach($mvcRenderingStrategy);
- $events->attach($sendResponseListener);
$sharedEvents->attach('Zend\Stdlib\DispatchableInterface', MvcEvent::EVENT_DISPATCH, array($injectParamsListener, 'injectNamedParams'), 1000);
$sharedEvents->attach('Zend\Stdlib\DispatchableInterface', MvcEvent::EVENT_DISPATCH, array($createViewModelListener, 'createViewModelFromArray'), -80);
View
10 library/Zend/Mvc/View/Http/ViewManager.php
@@ -16,7 +16,6 @@
use Zend\EventManager\ListenerAggregateInterface;
use Zend\Mvc\Exception;
use Zend\Mvc\MvcEvent;
-use Zend\Mvc\View\SendResponseListener;
use Zend\ServiceManager\ServiceManager;
use Zend\View\HelperPluginManager as ViewHelperManager;
use Zend\View\Renderer\PhpRenderer as ViewPhpRenderer;
@@ -63,7 +62,12 @@ class ViewManager implements ListenerAggregateInterface
protected $config;
/**
- * @var \Zend\ServiceManager\ServiceManager
+ * @var MvcEvent
+ */
+ protected $event;
+
+ /**
+ * @var ServiceManager
*/
protected $services;
@@ -134,7 +138,6 @@ public function onBootstrap($event)
$createViewModelListener = new CreateViewModelListener();
$injectTemplateListener = new InjectTemplateListener();
$injectViewModelListener = new InjectViewModelListener();
- $sendResponseListener = new SendResponseListener();
$this->registerMvcRenderingStrategies($events);
$this->registerViewStrategies();
@@ -143,7 +146,6 @@ public function onBootstrap($event)
$events->attach($exceptionStrategy);
$events->attach(MvcEvent::EVENT_DISPATCH_ERROR, array($injectViewModelListener, 'injectViewModel'), -100);
$events->attach($mvcRenderingStrategy);
- $events->attach($sendResponseListener);
$sharedEvents->attach('Zend\Stdlib\DispatchableInterface', MvcEvent::EVENT_DISPATCH, array($createViewModelListener, 'createViewModelFromArray'), -80);
$sharedEvents->attach('Zend\Stdlib\DispatchableInterface', MvcEvent::EVENT_DISPATCH, array($routeNotFoundStrategy, 'prepareNotFoundViewModel'), -90);
View
57 library/Zend/Mvc/View/SendResponseListener.php
@@ -10,65 +10,14 @@
namespace Zend\Mvc\View;
-use Zend\EventManager\EventManagerInterface;
-use Zend\EventManager\ListenerAggregateInterface;
-use Zend\Mvc\MvcEvent;
-use Zend\Stdlib\ResponseInterface as Response;
+use Zend\Mvc\SendResponseListener as MvcSendResponseListener;
/**
* @category Zend
* @package Zend_Mvc
* @subpackage View
+ * @deprecated
*/
-class SendResponseListener implements ListenerAggregateInterface
+class SendResponseListener extends MvcSendResponseListener
{
- /**
- * @var \Zend\Stdlib\CallbackHandler[]
- */
- protected $listeners = array();
-
- /**
- * Attach the aggregate to the specified event manager
- *
- * @param EventManagerInterface $events
- * @return void
- */
- public function attach(EventManagerInterface $events)
- {
- $this->listeners[] = $events->attach(MvcEvent::EVENT_FINISH, array($this, 'sendResponse'), -10000);
- }
-
- /**
- * Detach aggregate listeners from the specified event manager
- *
- * @param EventManagerInterface $events
- * @return void
- */
- public function detach(EventManagerInterface $events)
- {
- foreach ($this->listeners as $index => $listener) {
- if ($events->detach($listener)) {
- unset($this->listeners[$index]);
- }
- }
- }
-
- /**
- * Send the response
- *
- * @param MvcEvent $e
- * @return mixed
- */
- public function sendResponse(MvcEvent $e)
- {
- $response = $e->getResponse();
- if (!$response instanceof Response) {
- return false; // there is no response to send
- }
-
- // send the response if possible
- if (is_callable(array($response,'send'))) {
- return $response->send();
- }
- }
}
View
24 tests/ZendTest/Mvc/ApplicationTest.php
@@ -14,15 +14,12 @@
use PHPUnit_Framework_TestCase as TestCase;
use stdClass;
use Zend\Config\Config;
-use Zend\EventManager\EventManager;
use Zend\Http\Request;
use Zend\Http\PhpEnvironment\Response;
-use Zend\Modulemanager\ModuleManager;
use Zend\Mvc\Application;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\Router;
use Zend\Mvc\Service\ServiceManagerConfig;
-use Zend\Mvc\View\Http\ViewManager;
use Zend\ServiceManager\ServiceManager;
use Zend\Uri\UriFactory;
@@ -38,6 +35,11 @@ class ApplicationTest extends TestCase
*/
protected $serviceManager;
+ /**
+ * @var Application
+ */
+ protected $application;
+
public function setUp()
{
$appConfig = array(
@@ -68,7 +70,8 @@ public function setUp()
'Request' => 'Zend\Http\PhpEnvironment\Request',
'Response' => 'Zend\Http\PhpEnvironment\Response',
'RouteListener' => 'Zend\Mvc\RouteListener',
- 'ViewManager' => 'ZendTest\Mvc\TestAsset\MockViewManager'
+ 'ViewManager' => 'ZendTest\Mvc\TestAsset\MockViewManager',
+ 'SendResponseListener' => 'ZendTest\Mvc\TestAsset\MockSendResponseListener'
),
'factories' => array(
'ControllerLoader' => 'Zend\Mvc\Service\ControllerLoaderFactory',
@@ -183,6 +186,18 @@ public function testBootstrapRegistersDispatchListener()
$this->assertSame(array($dispatchListener, 'onDispatch'), $callback);
}
+ public function testBootstrapRegistersSendResponseListener()
+ {
+ $sendResponseListener = $this->serviceManager->get('SendResponseListener');
+ $this->application->bootstrap();
+ $events = $this->application->getEventManager();
+ $listeners = $events->getListeners(MvcEvent::EVENT_FINISH);
+ $this->assertEquals(1, count($listeners));
+ $listener = $listeners->top();
+ $callback = $listener->getCallback();
+ $this->assertSame(array($sendResponseListener, 'sendResponse'), $callback);
+ }
+
public function testBootstrapRegistersViewManagerAsBootstrapListener()
{
$viewManager = $this->serviceManager->get('ViewManager');
@@ -228,7 +243,6 @@ public function setupPathController($addService = true)
),
));
$router->addRoute('path', $route);
-
if ($addService) {
$controllerLoader = $this->serviceManager->get('ControllerLoader');
$controllerLoader->setFactory('path', function() {
View
38 tests/ZendTest/Mvc/TestAsset/MockSendResponseListener.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Mvc
+ */
+
+namespace ZendTest\Mvc\TestAsset;
+
+use Zend\EventManager\EventManagerInterface;
+use Zend\EventManager\ListenerAggregateInterface;
+use Zend\Mvc\MvcEvent;
+
+class MockSendResponseListener implements ListenerAggregateInterface
+{
+ protected $listeners = array();
+
+ public function attach(EventManagerInterface $events)
+ {
+ $this->listeners[] = $events->attach(MvcEvent::EVENT_FINISH, array($this, 'sendResponse'), -10000);
+ }
+
+ public function detach(EventManagerInterface $events)
+ {
+ foreach ($this->listeners as $index => $listener) {
+ if ($events->detach($listener)) {
+ unset($this->listeners[$index]);
+ }
+ }
+ }
+
+ public function sendResponse($e)
+ {
+ }
+}
View
1  tests/ZendTest/View/Helper/UrlIntegrationTest.php
@@ -63,6 +63,7 @@ protected function setUp()
'SharedEventManager' => 'Zend\EventManager\SharedEventManager',
'DispatchListener' => 'Zend\Mvc\DispatchListener',
'RouteListener' => 'Zend\Mvc\RouteListener',
+ 'SendResponseListener' => 'Zend\Mvc\SendResponseListener'
),
'factories' => array(
'Application' => 'Zend\Mvc\Service\ApplicationFactory',
Something went wrong with that request. Please try again.