<?php
/**
- * Zend Framework (http://framework.zend.com/)
- *
- * @see http://github.com/zendframework/zend-diactoros for the canonical source repository
- * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
+ * @see https://github.com/zendframework/zend-diactoros for the canonical source repository
+ * @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Diactoros\Response;
use Psr\Http\Message\ResponseInterface;
+use RuntimeException;
trait SapiEmitterTrait
{
/**
- * Inject the Content-Length header if is not already present.
+ * Checks to see if content has previously been sent.
*
- * @param ResponseInterface $response
- * @return ResponseInterface
+ * If either headers have been sent or the output buffer contains content,
+ * raises an exception.
+ *
+ * @throws RuntimeException if headers have already been sent.
+ * @throws RuntimeException if output is present in the output buffer.
*/
- private function injectContentLength(ResponseInterface $response)
+ private function assertNoPreviousOutput()
{
- if (! $response->hasHeader('Content-Length')) {
- // PSR-7 indicates int OR null for the stream size; for null values,
- // we will not auto-inject the Content-Length.
- if (null !== $response->getBody()->getSize()) {
- return $response->withHeader('Content-Length', (string) $response->getBody()->getSize());
- }
+ if (headers_sent()) {
+ throw new RuntimeException('Unable to emit response; headers already sent');
}
- return $response;
+ if (ob_get_level() > 0 && ob_get_length() > 0) {
+ throw new RuntimeException('Output has been emitted previously; cannot emit response');
+ }
}
/**
* Emits the status line using the protocol version and status code from
* the response; if a reason phrase is available, it, too, is emitted.
*
+ * It is important to mention that this method should be called after
+ * `emitHeaders()` in order to prevent PHP from changing the status code of
+ * the emitted response.
+ *
* @param ResponseInterface $response
+ *
+ * @see \Zend\Diactoros\Response\SapiEmitterTrait::emitHeaders()
*/
private function emitStatusLine(ResponseInterface $response)
{
$reasonPhrase = $response->getReasonPhrase();
+ $statusCode = $response->getStatusCode();
+
header(sprintf(
'HTTP/%s %d%s',
$response->getProtocolVersion(),
- $response->getStatusCode(),
+ $statusCode,
($reasonPhrase ? ' ' . $reasonPhrase : '')
- ));
+ ), true, $statusCode);
}
/**
*/
private function emitHeaders(ResponseInterface $response)
{
+ $statusCode = $response->getStatusCode();
+
foreach ($response->getHeaders() as $header => $values) {
$name = $this->filterHeader($header);
- $first = true;
+ $first = $name === 'Set-Cookie' ? false : true;
foreach ($values as $value) {
header(sprintf(
'%s: %s',
$name,
$value
- ), $first);
+ ), $first, $statusCode);
$first = false;
}
}
}
- /**
- * Loops through the output buffer, flushing each, before emitting
- * the response.
- *
- * @param int|null $maxBufferLevel Flush up to this buffer level.
- */
- private function flush($maxBufferLevel = null)
- {
- if (null === $maxBufferLevel) {
- $maxBufferLevel = ob_get_level();
- }
-
- while (ob_get_level() > $maxBufferLevel) {
- ob_end_flush();
- }
- }
-
/**
* Filter a header name to wordcase
*