merged branch francisbesset/httpfoundation_responseheaderbag (PR #1640)
Commits -------64e9263
Updated UPDATE.md7cf891a
Renamed variable returned and used self in place of static for constantsf91f4dd
Added the possibility to set cookies with the same name for different domains and paths for Symfony\Component\HttpFoundation\ResponseHeaderBagf08eeb4
Moved managing cookies of HeaderBag in ResponseHeaderBag Discussion ---------- [HttpFoundation] Cookies management in ResponseHeaderBag Fixed cookies management in `Symfony\Component\HttpFoundation\HeaderBag` and `Symfony\Component\HttpFoundation\ResponseHeaderBag`
This commit is contained in:
commit
4004b4411e
13
UPDATE.md
13
UPDATE.md
@ -13,6 +13,19 @@ RC4 to RC5
|
|||||||
`Symfony\Component\Security\Core\User\EntityUserProvider` to
|
`Symfony\Component\Security\Core\User\EntityUserProvider` to
|
||||||
`Symfony\Bridge\Doctrine\Security\User\EntityUserProvider`.
|
`Symfony\Bridge\Doctrine\Security\User\EntityUserProvider`.
|
||||||
|
|
||||||
|
* Moved cookies management of HeaderBag in ResponseHeaderBag of the HttpFoundation.
|
||||||
|
To access a cookie in Request instance, please use Request::$cookies public attribute.
|
||||||
|
|
||||||
|
* It is not possible to know if a cookie is present or get a cookie in ResponseHeaderBag
|
||||||
|
because getCookie() and hasCookie() methods were removed.
|
||||||
|
|
||||||
|
* The method `ResponseHeaderBag::getCookie()` accepts an argument for the return format,
|
||||||
|
the possible values are `ResponseHeaderBag::COOKIES_FLAT` (default value) or `ResponseHeaderBag::COOKIES_ARRAY`
|
||||||
|
* ResponseHeaderBag::COOKIES_FLAT return a simple array
|
||||||
|
* array(0 => `a cookie instance`, 1 => `another cookie instance`)
|
||||||
|
* ResponseHeaderBag::COOKIES_ARRAY return a multidimensional array
|
||||||
|
* array(`the domain` => array(`the path` => array(`the cookie name` => `a cookie instance`)))
|
||||||
|
|
||||||
* Removed the guesser for the Choice constraint as the constraint only knows
|
* Removed the guesser for the Choice constraint as the constraint only knows
|
||||||
about the valid keys, and not their values.
|
about the valid keys, and not their values.
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ namespace Symfony\Component\HttpFoundation;
|
|||||||
class HeaderBag
|
class HeaderBag
|
||||||
{
|
{
|
||||||
protected $headers;
|
protected $headers;
|
||||||
protected $cookies;
|
|
||||||
protected $cacheControl;
|
protected $cacheControl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,7 +29,6 @@ class HeaderBag
|
|||||||
public function __construct(array $headers = array())
|
public function __construct(array $headers = array())
|
||||||
{
|
{
|
||||||
$this->cacheControl = array();
|
$this->cacheControl = array();
|
||||||
$this->cookies = array();
|
|
||||||
$this->headers = array();
|
$this->headers = array();
|
||||||
foreach ($headers as $key => $values) {
|
foreach ($headers as $key => $values) {
|
||||||
$this->set($key, $values);
|
$this->set($key, $values);
|
||||||
@ -200,67 +198,6 @@ class HeaderBag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a cookie.
|
|
||||||
*
|
|
||||||
* @param Cookie $cookie
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setCookie(Cookie $cookie)
|
|
||||||
{
|
|
||||||
$this->cookies[$cookie->getName()] = $cookie;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a cookie from the array, but does not unset it in the browser
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function removeCookie($name)
|
|
||||||
{
|
|
||||||
unset($this->cookies[$name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the array contains any cookie with this name
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return Boolean
|
|
||||||
*/
|
|
||||||
public function hasCookie($name)
|
|
||||||
{
|
|
||||||
return isset($this->cookies[$name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a cookie
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
*
|
|
||||||
* @throws \InvalidArgumentException When the cookie does not exist
|
|
||||||
*
|
|
||||||
* @return Cookie
|
|
||||||
*/
|
|
||||||
public function getCookie($name)
|
|
||||||
{
|
|
||||||
if (!$this->hasCookie($name)) {
|
|
||||||
throw new \InvalidArgumentException(sprintf('There is no cookie with name "%s".', $name));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->cookies[$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array with all cookies
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getCookies()
|
|
||||||
{
|
|
||||||
return $this->cookies;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the HTTP header value converted to a date.
|
* Returns the HTTP header value converted to a date.
|
||||||
*
|
*
|
||||||
|
@ -18,7 +18,11 @@ namespace Symfony\Component\HttpFoundation;
|
|||||||
*/
|
*/
|
||||||
class ResponseHeaderBag extends HeaderBag
|
class ResponseHeaderBag extends HeaderBag
|
||||||
{
|
{
|
||||||
|
const COOKIES_FLAT = 'flat';
|
||||||
|
const COOKIES_ARRAY = 'array';
|
||||||
|
|
||||||
protected $computedCacheControl = array();
|
protected $computedCacheControl = array();
|
||||||
|
protected $cookies = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@ -40,7 +44,7 @@ class ResponseHeaderBag extends HeaderBag
|
|||||||
public function __toString()
|
public function __toString()
|
||||||
{
|
{
|
||||||
$cookies = '';
|
$cookies = '';
|
||||||
foreach ($this->cookies as $cookie) {
|
foreach ($this->getCookies() as $cookie) {
|
||||||
$cookies .= 'Set-Cookie: '.$cookie."\r\n";
|
$cookies .= 'Set-Cookie: '.$cookie."\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +106,69 @@ class ResponseHeaderBag extends HeaderBag
|
|||||||
return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
|
return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a cookie.
|
||||||
|
*
|
||||||
|
* @param Cookie $cookie
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setCookie(Cookie $cookie)
|
||||||
|
{
|
||||||
|
$this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a cookie from the array, but does not unset it in the browser
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $path
|
||||||
|
* @param string $domain
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function removeCookie($name, $path = null, $domain = null)
|
||||||
|
{
|
||||||
|
unset($this->cookies[$domain][$path][$name]);
|
||||||
|
|
||||||
|
if (empty($this->cookies[$domain][$path])) {
|
||||||
|
unset($this->cookies[$domain][$path]);
|
||||||
|
|
||||||
|
if (empty($this->cookies[$domain])) {
|
||||||
|
unset($this->cookies[$domain]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with all cookies
|
||||||
|
*
|
||||||
|
* @param string $format
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException When the $format is invalid
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCookies($format = self::COOKIES_FLAT)
|
||||||
|
{
|
||||||
|
if (!in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY))));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::COOKIES_ARRAY === $format) {
|
||||||
|
return $this->cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
$flattenedCookies = array();
|
||||||
|
foreach ($this->cookies as $path) {
|
||||||
|
foreach ($path as $cookies) {
|
||||||
|
foreach ($cookies as $cookie) {
|
||||||
|
$flattenedCookies[] = $cookie;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $flattenedCookies;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears a cookie in the browser
|
* Clears a cookie in the browser
|
||||||
*
|
*
|
||||||
|
@ -75,4 +75,47 @@ class ResponseHeaderBagTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$this->assertContains("Set-Cookie: foo=deleted; expires=".gmdate("D, d-M-Y H:i:s T", time() - 31536001)."; httponly", explode("\r\n", $bag->__toString()));
|
$this->assertContains("Set-Cookie: foo=deleted; expires=".gmdate("D, d-M-Y H:i:s T", time() - 31536001)."; httponly", explode("\r\n", $bag->__toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCookiesWithSameNames()
|
||||||
|
{
|
||||||
|
$bag = new ResponseHeaderBag();
|
||||||
|
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
|
||||||
|
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'foo.bar'));
|
||||||
|
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'bar.foo'));
|
||||||
|
$bag->setCookie(new Cookie('foo', 'bar'));
|
||||||
|
|
||||||
|
$this->assertEquals(4, count($bag->getCookies()));
|
||||||
|
|
||||||
|
$headers = explode("\r\n", $bag->__toString());
|
||||||
|
$this->assertContains("Set-Cookie: foo=bar; path=/path/foo; domain=foo.bar; httponly", $headers);
|
||||||
|
$this->assertContains("Set-Cookie: foo=bar; path=/path/foo; domain=foo.bar; httponly", $headers);
|
||||||
|
$this->assertContains("Set-Cookie: foo=bar; path=/path/bar; domain=bar.foo; httponly", $headers);
|
||||||
|
$this->assertContains("Set-Cookie: foo=bar; path=/; httponly", $headers);
|
||||||
|
|
||||||
|
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||||
|
$this->assertTrue(isset($cookies['foo.bar']['/path/foo']['foo']));
|
||||||
|
$this->assertTrue(isset($cookies['foo.bar']['/path/bar']['foo']));
|
||||||
|
$this->assertTrue(isset($cookies['bar.foo']['/path/bar']['foo']));
|
||||||
|
$this->assertTrue(isset($cookies['']['/']['foo']));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemoveCookie()
|
||||||
|
{
|
||||||
|
$bag = new ResponseHeaderBag();
|
||||||
|
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
|
||||||
|
$bag->setCookie(new Cookie('bar', 'foo', 0, '/path/bar', 'foo.bar'));
|
||||||
|
|
||||||
|
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||||
|
$this->assertTrue(isset($cookies['foo.bar']['/path/foo']));
|
||||||
|
|
||||||
|
$bag->removeCookie('foo', '/path/foo', 'foo.bar');
|
||||||
|
|
||||||
|
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||||
|
$this->assertFalse(isset($cookies['foo.bar']['/path/foo']));
|
||||||
|
|
||||||
|
$bag->removeCookie('bar', '/path/bar', 'foo.bar');
|
||||||
|
|
||||||
|
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||||
|
$this->assertFalse(isset($cookies['foo.bar']));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Tests\Component\Security\Http\Logout;
|
namespace Symfony\Tests\Component\Security\Http\Logout;
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\Security\Http\Logout\CookieClearingLogoutHandler;
|
use Symfony\Component\Security\Http\Logout\CookieClearingLogoutHandler;
|
||||||
|
|
||||||
@ -25,20 +26,21 @@ class CookieClearingLogoutHandlerTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$handler = new CookieClearingLogoutHandler(array('foo' => array('path' => '/foo', 'domain' => 'foo.foo'), 'foo2' => array('path' => null, 'domain' => null)));
|
$handler = new CookieClearingLogoutHandler(array('foo' => array('path' => '/foo', 'domain' => 'foo.foo'), 'foo2' => array('path' => null, 'domain' => null)));
|
||||||
|
|
||||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
$cookies = $response->headers->getCookies();
|
||||||
|
$this->assertEquals(0, count($cookies));
|
||||||
|
|
||||||
$handler->logout($request, $response, $token);
|
$handler->logout($request, $response, $token);
|
||||||
|
|
||||||
$cookies = $response->headers->getCookies();
|
$cookies = $response->headers->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||||
$this->assertEquals(2, count($cookies));
|
$this->assertEquals(2, count($cookies));
|
||||||
|
|
||||||
$cookie = $cookies['foo'];
|
$cookie = $cookies['foo.foo']['/foo']['foo'];
|
||||||
$this->assertEquals('foo', $cookie->getName());
|
$this->assertEquals('foo', $cookie->getName());
|
||||||
$this->assertEquals('/foo', $cookie->getPath());
|
$this->assertEquals('/foo', $cookie->getPath());
|
||||||
$this->assertEquals('foo.foo', $cookie->getDomain());
|
$this->assertEquals('foo.foo', $cookie->getDomain());
|
||||||
$this->assertTrue($cookie->isCleared());
|
$this->assertTrue($cookie->isCleared());
|
||||||
|
|
||||||
$cookie = $cookies['foo2'];
|
$cookie = $cookies['']['']['foo2'];
|
||||||
$this->assertStringStartsWith('foo2', $cookie->getName());
|
$this->assertStringStartsWith('foo2', $cookie->getName());
|
||||||
$this->assertNull($cookie->getPath());
|
$this->assertNull($cookie->getPath());
|
||||||
$this->assertNull($cookie->getDomain());
|
$this->assertNull($cookie->getDomain());
|
||||||
|
@ -19,6 +19,7 @@ use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
|||||||
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
|
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||||
use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices;
|
use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices;
|
||||||
use Symfony\Component\Security\Core\Exception\TokenNotFoundException;
|
use Symfony\Component\Security\Core\Exception\TokenNotFoundException;
|
||||||
use Symfony\Component\Security\Core\Exception\CookieTheftException;
|
use Symfony\Component\Security\Core\Exception\CookieTheftException;
|
||||||
@ -281,11 +282,13 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
|
|||||||
;
|
;
|
||||||
$service->setTokenProvider($tokenProvider);
|
$service->setTokenProvider($tokenProvider);
|
||||||
|
|
||||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
$cookies = $response->headers->getCookies();
|
||||||
|
$this->assertEquals(0, count($cookies));
|
||||||
|
|
||||||
$service->loginSuccess($request, $response, $token);
|
$service->loginSuccess($request, $response, $token);
|
||||||
|
|
||||||
$cookie = $response->headers->getCookie('foo');
|
$cookies = $response->headers->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||||
|
$cookie = $cookies['myfoodomain.foo']['/foo/path']['foo'];
|
||||||
$this->assertFalse($cookie->isCleared());
|
$this->assertFalse($cookie->isCleared());
|
||||||
$this->assertTrue($cookie->isSecure());
|
$this->assertTrue($cookie->isSecure());
|
||||||
$this->assertTrue($cookie->isHttpOnly());
|
$this->assertTrue($cookie->isHttpOnly());
|
||||||
|
@ -19,6 +19,7 @@ use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
|||||||
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
|
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||||
use Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices;
|
use Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices;
|
||||||
use Symfony\Component\Security\Core\Exception\TokenNotFoundException;
|
use Symfony\Component\Security\Core\Exception\TokenNotFoundException;
|
||||||
use Symfony\Component\Security\Core\Exception\CookieTheftException;
|
use Symfony\Component\Security\Core\Exception\CookieTheftException;
|
||||||
@ -184,11 +185,13 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
|||||||
->will($this->returnValue('foo'))
|
->will($this->returnValue('foo'))
|
||||||
;
|
;
|
||||||
|
|
||||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
$cookies = $response->headers->getCookies();
|
||||||
|
$this->assertEquals(0, count($cookies));
|
||||||
|
|
||||||
$service->loginSuccess($request, $response, $token);
|
$service->loginSuccess($request, $response, $token);
|
||||||
|
|
||||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
$cookies = $response->headers->getCookies();
|
||||||
|
$this->assertEquals(0, count($cookies));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLoginSuccess()
|
public function testLoginSuccess()
|
||||||
@ -215,11 +218,13 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
|||||||
->will($this->returnValue($user))
|
->will($this->returnValue($user))
|
||||||
;
|
;
|
||||||
|
|
||||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
$cookies = $response->headers->getCookies();
|
||||||
|
$this->assertEquals(0, count($cookies));
|
||||||
|
|
||||||
$service->loginSuccess($request, $response, $token);
|
$service->loginSuccess($request, $response, $token);
|
||||||
|
|
||||||
$cookie = $response->headers->getCookie('foo');
|
$cookies = $response->headers->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||||
|
$cookie = $cookies['myfoodomain.foo']['/foo/path']['foo'];
|
||||||
$this->assertFalse($cookie->isCleared());
|
$this->assertFalse($cookie->isCleared());
|
||||||
$this->assertTrue($cookie->isSecure());
|
$this->assertTrue($cookie->isSecure());
|
||||||
$this->assertTrue($cookie->isHttpOnly());
|
$this->assertTrue($cookie->isHttpOnly());
|
||||||
|
Reference in New Issue
Block a user