Skip to content

Commit

Permalink
Started implementing the PSR7 response interface
Browse files Browse the repository at this point in the history
  • Loading branch information
Florian Krämer committed Oct 10, 2016
1 parent 3a8b28c commit 25acb4a
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 42 deletions.
175 changes: 133 additions & 42 deletions src/Network/Response.php
Expand Up @@ -21,6 +21,7 @@
use DateTime;
use DateTimeZone;
use InvalidArgumentException;
use Zend\Diactoros\MessageTrait;

/**
* Cake Response is responsible for managing the response text, status and headers of a HTTP response.
Expand All @@ -31,6 +32,8 @@
class Response
{

use MessageTrait;

/**
* Holds HTTP response statuses
*
Expand Down Expand Up @@ -355,13 +358,6 @@ class Response
*/
protected $_contentType = 'text/html';

/**
* Buffer list of headers
*
* @var array
*/
protected $_headers = [];

/**
* Buffer string or callable for response message
*
Expand Down Expand Up @@ -405,6 +401,13 @@ class Response
*/
protected $_cookies = [];

/**
* Reason Phrase
*
* @var string|null
*/
protected $_reasonPhrase = null;

/**
* Constructor
*
Expand Down Expand Up @@ -443,7 +446,7 @@ public function __construct(array $options = [])
*/
public function send()
{
if (isset($this->_headers['Location']) && $this->_status === 200) {
if (isset($this->headers['Location']) && $this->_status === 200) {
$this->statusCode(302);
}

Expand Down Expand Up @@ -481,7 +484,7 @@ public function sendHeaders()
$this->_sendHeader("{$this->_protocol} {$this->_status} {$codeMessage}");
$this->_setContentType();

foreach ($this->_headers as $header => $values) {
foreach ($this->headers as $header => $values) {
foreach ((array)$values as $value) {
$this->_sendHeader($header, $value);
}
Expand Down Expand Up @@ -533,9 +536,9 @@ protected function _setContentType()
}

if ($charset) {
$this->header('Content-Type', "{$this->_contentType}; charset={$this->_charset}");
$this->_setHeader('Content-Type', "{$this->_contentType}; charset={$this->_charset}");
} else {
$this->header('Content-Type', "{$this->_contentType}");
$this->_setHeader('Content-Type', "{$this->_contentType}");
}
}

Expand Down Expand Up @@ -630,8 +633,9 @@ protected function _sendContent($content)
public function header($header = null, $value = null)
{
if ($header === null) {
return $this->_headers;
return $this->headers;
}

$headers = is_array($header) ? $header : [$header => $value];
foreach ($headers as $header => $value) {
if (is_numeric($header)) {
Expand All @@ -640,10 +644,17 @@ public function header($header = null, $value = null)
if ($value === null) {
list($header, $value) = explode(':', $header, 2);
}
$this->_headers[$header] = is_array($value) ? array_map('trim', $value) : trim($value);

if ($this->hasHeader($header)) {
$header = $this->headerNames[strtolower($header)];
} else {
$this->headerNames[strtolower($header)] = $header;
}

$this->headers[$header] = is_array($value) ? array_map('trim', $value) : trim($value);
}

return $this->_headers;
return $this->headers;
}

/**
Expand All @@ -658,15 +669,31 @@ public function header($header = null, $value = null)
public function location($url = null)
{
if ($url === null) {
$headers = $this->header();
$result = $this->getHeaderLine('Location');
if (empty($result)) {
return null;
}

return isset($headers['Location']) ? $headers['Location'] : null;
return $result;
}
$this->header('Location', $url);
$this->_setHeader('Location', $url);

return null;
}

/**
* Sets a header.
*
* @param string $header Header key.
* @param string $value Header value.
*/
protected function _setHeader($header, $value)
{
$normalized = strtolower($header);
$this->headerNames[$normalized] = $header;
$this->headers[$header] = $value;
}

/**
* Buffers the response message to be sent
* if $content is null the current buffer is returned
Expand Down Expand Up @@ -703,6 +730,68 @@ public function statusCode($code = null)
return $this->_status = $code;
}

/**
* Gets the response status code.
*
* The status code is a 3-digit integer result code of the server's attempt
* to understand and satisfy the request.
*
* @return int Status code.
*/
public function getStatusCode()
{
return $this->_status;
}

/**
* Return an instance with the specified status code and, optionally, reason phrase.
*
* If no reason phrase is specified, implementations MAY choose to default
* to the RFC 7231 or IANA recommended reason phrase for the response's
* status code.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* updated status and reason phrase.
*
* @link http://tools.ietf.org/html/rfc7231#section-6
* @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
* @param int $code The 3-digit integer result code to set.
* @param string $reasonPhrase The reason phrase to use with the
* provided status code; if none is provided, implementations MAY
* use the defaults as suggested in the HTTP specification.
* @return static
* @throws \InvalidArgumentException For invalid status code arguments.
*/
public function withStatus($code, $reasonPhrase = '')
{
$new = clone $this;
$new->_status = $code;
if (empty($reasonPhrase) && isset($new->_statusCodes[$code])) {
$reasonPhrase = $new->_statusCodes[$code];
}
$new->_reasonPhrase = $reasonPhrase;
return $new;
}

/**
* Gets the response reason phrase associated with the status code.
*
* Because a reason phrase is not a required element in a response
* status line, the reason phrase value MAY be null. Implementations MAY
* choose to return the default RFC 7231 recommended reason phrase (or those
* listed in the IANA HTTP Status Code Registry) for the response's
* status code.
*
* @link http://tools.ietf.org/html/rfc7231#section-6
* @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
* @return string Reason phrase; must return an empty string if none present.
*/
public function getReasonPhrase()
{
return $this->_reasonPhrase;
}

/**
* Queries & sets valid HTTP response codes & messages.
*
Expand Down Expand Up @@ -875,11 +964,9 @@ public function charset($charset = null)
*/
public function disableCache()
{
$this->header([
'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT',
'Last-Modified' => gmdate("D, d M Y H:i:s") . " GMT",
'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
]);
$this->_setHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT');
$this->_setHeader('Last-Modified', gmdate("D, d M Y H:i:s") . " GMT");
$this->_setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
}

/**
Expand All @@ -894,9 +981,9 @@ public function cache($since, $time = '+1 day')
if (!is_int($time)) {
$time = strtotime($time);
}
$this->header([
'Date' => gmdate("D, j M Y G:i:s ", time()) . 'GMT'
]);

$this->_setHeader('Date', gmdate("D, j M Y G:i:s ", time()) . 'GMT');

$this->modified($since);
$this->expires($time);
$this->sharable(true);
Expand Down Expand Up @@ -1026,7 +1113,7 @@ protected function _setCacheControl()
$control .= ', ';
}
$control = rtrim($control, ', ');
$this->header('Cache-Control', $control);
$this->_setHeader('Cache-Control', $control);
}

/**
Expand All @@ -1046,10 +1133,11 @@ public function expires($time = null)
{
if ($time !== null) {
$date = $this->_getUTCDate($time);
$this->_headers['Expires'] = $date->format('D, j M Y H:i:s') . ' GMT';
$this->_setHeader('Expires', $date->format('D, j M Y H:i:s') . ' GMT');
}
if (isset($this->_headers['Expires'])) {
return $this->_headers['Expires'];

if ($this->hasHeader('Expires')) {
return $this->getHeaderLine('Expires');
}

return null;
Expand All @@ -1072,10 +1160,10 @@ public function modified($time = null)
{
if ($time !== null) {
$date = $this->_getUTCDate($time);
$this->_headers['Last-Modified'] = $date->format('D, j M Y H:i:s') . ' GMT';
$this->headers['Last-Modified'] = $date->format('D, j M Y H:i:s') . ' GMT';
}
if (isset($this->_headers['Last-Modified'])) {
return $this->_headers['Last-Modified'];
if (isset($this->headers['Last-Modified'])) {
return $this->headers['Last-Modified'];
}

return null;
Expand All @@ -1102,7 +1190,7 @@ public function notModified()
'Last-Modified'
];
foreach ($remove as $header) {
unset($this->_headers[$header]);
unset($this->headers[$header]);
}
}

Expand All @@ -1120,10 +1208,11 @@ public function vary($cacheVariances = null)
{
if ($cacheVariances !== null) {
$cacheVariances = (array)$cacheVariances;
$this->_headers['Vary'] = implode(', ', $cacheVariances);
$this->_setHeader('Vary', implode(', ', $cacheVariances));
}
if (isset($this->_headers['Vary'])) {
return explode(', ', $this->_headers['Vary']);

if ($this->hasHeader('Vary')) {
return explode(', ', $this->getHeaderLine('Vary'));
}

return null;
Expand Down Expand Up @@ -1153,10 +1242,11 @@ public function vary($cacheVariances = null)
public function etag($hash = null, $weak = false)
{
if ($hash !== null) {
$this->_headers['Etag'] = sprintf('%s"%s"', ($weak) ? 'W/' : null, $hash);
$this->_setHeader('Etag', sprintf('%s"%s"', ($weak) ? 'W/' : null, $hash));
}
if (isset($this->_headers['Etag'])) {
return $this->_headers['Etag'];

if ($this->hasHeader('Etag')) {
return $this->getHeaderLine('Etag');
}

return null;
Expand Down Expand Up @@ -1246,10 +1336,11 @@ public function protocol($protocol = null)
public function length($bytes = null)
{
if ($bytes !== null) {
$this->_headers['Content-Length'] = $bytes;
$this->_setHeader('Content-Length', $bytes);
}
if (isset($this->_headers['Content-Length'])) {
return $this->_headers['Content-Length'];

if ($this->hasHeader('Content-Length')) {
return $this->getHeaderLine('Content-Length');
}

return null;
Expand Down
61 changes: 61 additions & 0 deletions tests/TestCase/Network/ResponseTest.php
Expand Up @@ -2096,4 +2096,65 @@ public function testLocation()
$this->assertNull($response->location('http://example.org'), 'Setting a location should return null');
$this->assertEquals('http://example.org', $response->location(), 'Reading a location should return the value.');
}

/**
* Test get protocol version.
*
* @return void
*/
public function getProtocolVersion()
{
$response = new Response();
$version = $response->getProtocolVersion();
$this->assertEquals('1.1', $version);
}
/**
* Test with protocol.
*
* @return void
*/
public function testWithProtocol()
{
$response = new Response();
$version = $response->getProtocolVersion();
$this->assertEquals('1.1', $version);
$response2 = $response->withProtocolVersion('1.0');
$version = $response2->getProtocolVersion();
$this->assertEquals('1.0', $version);
$version = $response->getProtocolVersion();
$this->assertEquals('1.1', $version);
$this->assertNotEquals($response, $response2);
}
/**
* Test with protocol.
*
* @return void
*/
public function testWithStatusCode()
{
$response = new Response();
$statusCode = $response->getStatusCode();
$this->assertEquals(200, $statusCode);
$response2 = $response->withStatus(404);
$statusCode = $response2->getStatusCode();
$this->assertEquals(404, $statusCode);
$statusCode = $response->getStatusCode();
$this->assertEquals(200, $statusCode);
$this->assertNotEquals($response, $response2);
}

/**
* Test get reason phrase.
*
* @return void
*/
public function testGetReasonPhrase()
{
$response = new Response();
$reasonPhrase = $response->getReasonPhrase();
$this->assertNull($reasonPhrase);
$response = $response->withStatus(404);
$reasonPhrase = $response->getReasonPhrase();
$this->assertEquals('Not Found', $reasonPhrase);
}
}

0 comments on commit 25acb4a

Please sign in to comment.