Add functions to modify cookies to response class

Currently there is no AppFramework way to modify cookies, which makes it unusable for quite some use-cases or results in untestable code.

This PR adds some basic functionalities to add and invalidate cookies.

Usage:
```php
$response = new TemplateResponse(...);
$response->addCookie('foo', 'bar');
$response->invalidateCookie('foo');
$response->addCookie('bar', 'foo', new \DateTime('2015-01-01 00:00'));
```

Existing cookies can be accessed with the AppFramework using `$this->request->getCookie($name)`.
This commit is contained in:
Lukas Reschke 2014-11-27 14:19:00 +01:00
parent e306b588d2
commit 048139074d
6 changed files with 177 additions and 18 deletions

View File

@ -53,7 +53,7 @@ class App {
// initialize the dispatcher and run all the middleware before the controller // initialize the dispatcher and run all the middleware before the controller
$dispatcher = $container['Dispatcher']; $dispatcher = $container['Dispatcher'];
list($httpHeaders, $responseHeaders, $output) = list($httpHeaders, $responseHeaders, $responseCookies, $output) =
$dispatcher->dispatch($controller, $methodName); $dispatcher->dispatch($controller, $methodName);
if(!is_null($httpHeaders)) { if(!is_null($httpHeaders)) {
@ -64,6 +64,14 @@ class App {
header($name . ': ' . $value); header($name . ': ' . $value);
} }
foreach($responseCookies as $name => $value) {
$expireDate = null;
if($value['expireDate'] instanceof \DateTime) {
$expireDate = $value['expireDate']->getTimestamp();
}
setcookie($name, $value['value'], $expireDate, \OC::$WEBROOT, null, \OC::$server->getConfig()->getSystemValue('forcessl', false), true);
}
if(!is_null($output)) { if(!is_null($output)) {
header('Content-Length: ' . strlen($output)); header('Content-Length: ' . strlen($output));
print($output); print($output);

View File

@ -48,7 +48,7 @@ class Dispatcher {
* @param Http $protocol the http protocol with contains all status headers * @param Http $protocol the http protocol with contains all status headers
* @param MiddlewareDispatcher $middlewareDispatcher the dispatcher which * @param MiddlewareDispatcher $middlewareDispatcher the dispatcher which
* runs the middleware * runs the middleware
* @param ControllerMethodReflector the reflector that is used to inject * @param ControllerMethodReflector $reflector the reflector that is used to inject
* the arguments for the controller * the arguments for the controller
* @param IRequest $request the incoming request * @param IRequest $request the incoming request
*/ */
@ -71,6 +71,7 @@ class Dispatcher {
* @return array $array[0] contains a string with the http main header, * @return array $array[0] contains a string with the http main header,
* $array[1] contains headers in the form: $key => value, $array[2] contains * $array[1] contains headers in the form: $key => value, $array[2] contains
* the response output * the response output
* @throws \Exception
*/ */
public function dispatch(Controller $controller, $methodName) { public function dispatch(Controller $controller, $methodName) {
$out = array(null, array(), null); $out = array(null, array(), null);
@ -102,13 +103,14 @@ class Dispatcher {
// get the output which should be printed and run the after output // get the output which should be printed and run the after output
// middleware to modify the response // middleware to modify the response
$output = $response->render(); $output = $response->render();
$out[2] = $this->middlewareDispatcher->beforeOutput( $out[3] = $this->middlewareDispatcher->beforeOutput(
$controller, $methodName, $output); $controller, $methodName, $output);
// depending on the cache object the headers need to be changed // depending on the cache object the headers need to be changed
$out[0] = $this->protocol->getStatusHeader($response->getStatus(), $out[0] = $this->protocol->getStatusHeader($response->getStatus(),
$response->getLastModified(), $response->getETag()); $response->getLastModified(), $response->getETag());
$out[1] = $response->getHeaders(); $out[1] = array_merge($response->getHeaders());
$out[2] = $response->getCookies();
return $out; return $out;
} }

View File

@ -45,9 +45,16 @@ class Response {
); );
/**
* Cookies that will be need to be constructed as header
* @var array
*/
private $cookies = array();
/** /**
* HTTP status code - defaults to STATUS OK * HTTP status code - defaults to STATUS OK
* @var string * @var int
*/ */
private $status = Http::STATUS_OK; private $status = Http::STATUS_OK;
@ -70,6 +77,7 @@ class Response {
* Caches the response * Caches the response
* @param int $cacheSeconds the amount of seconds that should be cached * @param int $cacheSeconds the amount of seconds that should be cached
* if 0 then caching will be disabled * if 0 then caching will be disabled
* @return $this
*/ */
public function cacheFor($cacheSeconds) { public function cacheFor($cacheSeconds) {
@ -83,13 +91,68 @@ class Response {
return $this; return $this;
} }
/**
* Adds a new cookie to the response
* @param string $name The name of the cookie
* @param string $value The value of the cookie
* @param \DateTime|null $expireDate Date on that the cookie should expire, if set
* to null cookie will be considered as session
* cookie.
* @return $this
*/
public function addCookie($name, $value, \DateTime $expireDate = null) {
$this->cookies[$name] = array('value' => $value, 'expireDate' => $expireDate);
return $this;
}
/**
* Set the specified cookies
* @param array $cookies array('foo' => array('value' => 'bar', 'expire' => null))
* @return $this
*/
public function setCookies(array $cookies) {
$this->cookies = $cookies;
return $this;
}
/**
* Invalidates the specified cookie
* @param string $name
* @return $this
*/
public function invalidateCookie($name) {
$this->addCookie($name, 'expired', new \DateTime('1971-01-01 00:00'));
return $this;
}
/**
* Invalidates the specified cookies
* @param array $cookieNames array('foo', 'bar')
* @return $this
*/
public function invalidateCookies(array $cookieNames) {
foreach($cookieNames as $cookieName) {
$this->invalidateCookie($cookieName);
}
return $this;
}
/**
* Returns the cookies
* @return array
*/
public function getCookies() {
return $this->cookies;
}
/** /**
* Adds a new header to the response that will be called before the render * Adds a new header to the response that will be called before the render
* function * function
* @param string $name The name of the HTTP header * @param string $name The name of the HTTP header
* @param string $value The value, null will delete it * @param string $value The value, null will delete it
* @return Response Reference to this object * @return $this
*/ */
public function addHeader($name, $value) { public function addHeader($name, $value) {
$name = trim($name); // always remove leading and trailing whitespace $name = trim($name); // always remove leading and trailing whitespace
@ -108,10 +171,10 @@ class Response {
/** /**
* Set the headers * Set the headers
* @param array key value header pairs * @param array $headers value header pairs
* @return Response Reference to this object * @return $this
*/ */
public function setHeaders($headers) { public function setHeaders(array $headers) {
$this->headers = $headers; $this->headers = $headers;
return $this; return $this;

View File

@ -63,7 +63,7 @@ class AppTest extends \Test\TestCase {
public function testControllerNameAndMethodAreBeingPassed(){ public function testControllerNameAndMethodAreBeingPassed(){
$return = array(null, array(), null); $return = array(null, array(), array(), null);
$this->dispatcher->expects($this->once()) $this->dispatcher->expects($this->once())
->method('dispatch') ->method('dispatch')
->with($this->equalTo($this->controller), ->with($this->equalTo($this->controller),

View File

@ -227,7 +227,7 @@ class DispatcherTest extends \Test\TestCase {
$this->assertEquals($httpHeaders, $response[0]); $this->assertEquals($httpHeaders, $response[0]);
$this->assertEquals($responseHeaders, $response[1]); $this->assertEquals($responseHeaders, $response[1]);
$this->assertEquals($out, $response[2]); $this->assertEquals($out, $response[3]);
} }
@ -246,7 +246,7 @@ class DispatcherTest extends \Test\TestCase {
$this->assertEquals($httpHeaders, $response[0]); $this->assertEquals($httpHeaders, $response[0]);
$this->assertEquals($responseHeaders, $response[1]); $this->assertEquals($responseHeaders, $response[1]);
$this->assertEquals($out, $response[2]); $this->assertEquals($out, $response[3]);
} }
@ -301,7 +301,7 @@ class DispatcherTest extends \Test\TestCase {
$this->dispatcherPassthrough(); $this->dispatcherPassthrough();
$response = $this->dispatcher->dispatch($controller, 'exec'); $response = $this->dispatcher->dispatch($controller, 'exec');
$this->assertEquals('[3,true,4,1]', $response[2]); $this->assertEquals('[3,true,4,1]', $response[3]);
} }
@ -324,7 +324,7 @@ class DispatcherTest extends \Test\TestCase {
$this->dispatcherPassthrough(); $this->dispatcherPassthrough();
$response = $this->dispatcher->dispatch($controller, 'exec'); $response = $this->dispatcher->dispatch($controller, 'exec');
$this->assertEquals('[3,true,4,7]', $response[2]); $this->assertEquals('[3,true,4,7]', $response[3]);
} }
@ -350,7 +350,7 @@ class DispatcherTest extends \Test\TestCase {
$this->dispatcherPassthrough(); $this->dispatcherPassthrough();
$response = $this->dispatcher->dispatch($controller, 'exec'); $response = $this->dispatcher->dispatch($controller, 'exec');
$this->assertEquals('{"text":[3,false,4,1]}', $response[2]); $this->assertEquals('{"text":[3,false,4,1]}', $response[3]);
} }
@ -375,7 +375,7 @@ class DispatcherTest extends \Test\TestCase {
$this->dispatcherPassthrough(); $this->dispatcherPassthrough();
$response = $this->dispatcher->dispatch($controller, 'execDataResponse'); $response = $this->dispatcher->dispatch($controller, 'execDataResponse');
$this->assertEquals('{"text":[3,false,4,1]}', $response[2]); $this->assertEquals('{"text":[3,false,4,1]}', $response[3]);
} }
@ -401,7 +401,7 @@ class DispatcherTest extends \Test\TestCase {
$this->dispatcherPassthrough(); $this->dispatcherPassthrough();
$response = $this->dispatcher->dispatch($controller, 'exec'); $response = $this->dispatcher->dispatch($controller, 'exec');
$this->assertEquals('{"text":[3,false,4,1]}', $response[2]); $this->assertEquals('{"text":[3,false,4,1]}', $response[3]);
} }
@ -429,7 +429,7 @@ class DispatcherTest extends \Test\TestCase {
$this->dispatcherPassthrough(); $this->dispatcherPassthrough();
$response = $this->dispatcher->dispatch($controller, 'exec'); $response = $this->dispatcher->dispatch($controller, 'exec');
$this->assertEquals('{"text":[3,true,4,1]}', $response[2]); $this->assertEquals('{"text":[3,true,4,1]}', $response[3]);
} }

View File

@ -76,6 +76,92 @@ class ResponseTest extends \Test\TestCase {
} }
public function testAddCookie() {
$this->childResponse->addCookie('foo', 'bar');
$this->childResponse->addCookie('bar', 'foo', new \DateTime('1970-01-01'));
$expectedResponse = array(
'foo' => array(
'value' => 'bar',
'expireDate' => null,
),
'bar' => array(
'value' => 'foo',
'expireDate' => new \DateTime('1970-01-01')
)
);
$this->assertEquals($expectedResponse, $this->childResponse->getCookies());
}
function testSetCookies() {
$expected = array(
'foo' => array(
'value' => 'bar',
'expireDate' => null,
),
'bar' => array(
'value' => 'foo',
'expireDate' => new \DateTime('1970-01-01')
)
);
$this->childResponse->setCookies($expected);
$cookies = $this->childResponse->getCookies();
$this->assertEquals($expected, $cookies);
}
function testInvalidateCookie() {
$this->childResponse->addCookie('foo', 'bar');
$this->childResponse->invalidateCookie('foo');
$expected = array(
'foo' => array(
'value' => 'expired',
'expireDate' => new \DateTime('1971-01-01')
)
);
$cookies = $this->childResponse->getCookies();
$this->assertEquals($expected, $cookies);
}
function testInvalidateCookies() {
$this->childResponse->addCookie('foo', 'bar');
$this->childResponse->addCookie('bar', 'foo');
$expected = array(
'foo' => array(
'value' => 'bar',
'expireDate' => null
),
'bar' => array(
'value' => 'foo',
'expireDate' => null
)
);
$cookies = $this->childResponse->getCookies();
$this->assertEquals($expected, $cookies);
$this->childResponse->invalidateCookies(array('foo', 'bar'));
$expected = array(
'foo' => array(
'value' => 'expired',
'expireDate' => new \DateTime('1971-01-01')
),
'bar' => array(
'value' => 'expired',
'expireDate' => new \DateTime('1971-01-01')
)
);
$cookies = $this->childResponse->getCookies();
$this->assertEquals($expected, $cookies);
}
public function testRenderReturnNullByDefault(){ public function testRenderReturnNullByDefault(){
$this->assertEquals(null, $this->childResponse->render()); $this->assertEquals(null, $this->childResponse->render());
} }