-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[HttpFoundation] added support for streamed responses #2935
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0038d1b
e44b8ba
8717d44
473741b
887c0e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\HttpFoundation; | ||
|
||
/** | ||
* StreamedResponse represents a streamed HTTP response. | ||
* | ||
* A StreamedResponse uses a callback for its content. | ||
* | ||
* The callback should use the standard PHP functions like echo | ||
* to stream the response back to the client. The flush() method | ||
* can also be used if needed. | ||
* | ||
* @see flush() | ||
* | ||
* @author Fabien Potencier <fabien@symfony.com> | ||
* | ||
* @api | ||
*/ | ||
class StreamedResponse extends Response | ||
{ | ||
protected $callback; | ||
protected $streamed; | ||
|
||
/** | ||
* Constructor. | ||
* | ||
* @param mixed $callback A valid PHP callback | ||
* @param integer $status The response status code | ||
* @param array $headers An array of response headers | ||
* | ||
* @api | ||
*/ | ||
public function __construct($callback = null, $status = 200, $headers = array()) | ||
{ | ||
parent::__construct(null, $status, $headers); | ||
|
||
if (null !== $callback) { | ||
$this->setCallback($callback); | ||
} | ||
$this->streamed = false; | ||
} | ||
|
||
/** | ||
* Sets the PHP callback associated with this Response. | ||
* | ||
* @param mixed $callback A valid PHP callback | ||
*/ | ||
public function setCallback($callback) | ||
{ | ||
$this->callback = $callback; | ||
if (!is_callable($this->callback)) { | ||
throw new \LogicException('The Response callback must be a valid PHP callable.'); | ||
} | ||
} | ||
|
||
/** | ||
* @{inheritdoc} | ||
*/ | ||
public function prepare(Request $request) | ||
{ | ||
if ('1.0' != $request->server->get('SERVER_PROTOCOL')) { | ||
$this->setProtocolVersion('1.1'); | ||
$this->headers->set('Transfer-Encoding', 'chunked'); | ||
} | ||
|
||
$this->headers->set('Cache-Control', 'no-cache'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is probably a good reason to avoid caching but I can not figure out, any hint ? semantic: maybe this should be moved after the call to the parent method ? |
||
|
||
parent::prepare($request); | ||
} | ||
|
||
/** | ||
* @{inheritdoc} | ||
* | ||
* This method only sends the content once. | ||
*/ | ||
public function sendContent() | ||
{ | ||
if ($this->streamed) { | ||
return; | ||
} | ||
|
||
$this->streamed = true; | ||
|
||
if (null === $this->callback) { | ||
throw new \LogicException('The Response callback must not be null.'); | ||
} | ||
|
||
call_user_func($this->callback); | ||
} | ||
|
||
/** | ||
* @{inheritdoc} | ||
* | ||
* @throws \LogicException when the content is not null | ||
*/ | ||
public function setContent($content) | ||
{ | ||
if (null !== $content) { | ||
throw new \LogicException('The content cannot be set on a StreamedResponse instance.'); | ||
} | ||
} | ||
|
||
/** | ||
* @{inheritdoc} | ||
* | ||
* @return false | ||
*/ | ||
public function getContent() | ||
{ | ||
return false; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\HttpKernel\EventListener; | ||
|
||
use Symfony\Component\HttpFoundation\StreamedResponse; | ||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent; | ||
use Symfony\Component\HttpKernel\HttpKernelInterface; | ||
use Symfony\Component\HttpKernel\KernelEvents; | ||
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
|
||
/** | ||
* StreamedResponseListener is responsible for sending the Response | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. type: response There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think we do this in other places too when we want to refer to a class instance. |
||
* to the client. | ||
* | ||
* @author Fabien Potencier <fabien@symfony.com> | ||
*/ | ||
class StreamedResponseListener implements EventSubscriberInterface | ||
{ | ||
/** | ||
* Filters the Response. | ||
* | ||
* @param FilterResponseEvent $event A FilterResponseEvent instance | ||
*/ | ||
public function onKernelResponse(FilterResponseEvent $event) | ||
{ | ||
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { | ||
return; | ||
} | ||
|
||
$response = $event->getResponse(); | ||
|
||
if ($response instanceof StreamedResponse) { | ||
$response->send(); | ||
} | ||
} | ||
|
||
static public function getSubscribedEvents() | ||
{ | ||
return array( | ||
KernelEvents::RESPONSE => array('onKernelResponse', -1024), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need this listener at the end of the kernel.response event ? The event is the last step before the end of the request and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It must be the last one as we are sending the content here. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you have this event listener at all actually? Can't you just let the send() method be called as usual by the front controller? Sounds like it's equivalent to me. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The event listener is needed because we need to be in the request scope to be able to generate the response. If you send the response in the front controller, the request is not available anymore. |
||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,7 +28,7 @@ | |
* | ||
* @api | ||
*/ | ||
class PhpEngine implements EngineInterface, \ArrayAccess | ||
class PhpEngine implements EngineInterface, StreamingEngineInterface, \ArrayAccess | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason why the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right. This is a nice side-effect of having another interface for streaming. Fixed here: e462f7b |
||
{ | ||
protected $loader; | ||
protected $current; | ||
|
@@ -107,6 +107,21 @@ public function render($name, array $parameters = array()) | |
return $content; | ||
} | ||
|
||
/** | ||
* Streams a template. | ||
* | ||
* @param mixed $name A template name or a TemplateReferenceInterface instance | ||
* @param array $parameters An array of parameters to pass to the template | ||
* | ||
* @throws \RuntimeException if the template cannot be rendered | ||
* | ||
* @api | ||
*/ | ||
public function stream($name, array $parameters = array()) | ||
{ | ||
throw new \LogicException('The PHP engine does not support streaming.'); | ||
} | ||
|
||
/** | ||
* Returns true if the template exists. | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be
!==
for CS no?