Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[HttpFoundation] added withers to Cookie class #35215

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

Merged
merged 1 commit into from
Jan 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions 3 src/Symfony/Component/HttpFoundation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ CHANGELOG
5.1.0
-----

* added `Cookie::withValue`, `Cookie::withDomain`, `Cookie::withExpires`,
`Cookie::withPath`, `Cookie::withSecure`, `Cookie::withHttpOnly`,
`Cookie::withRaw`, `Cookie::withSameSite`
* Deprecate `Response::create()`, `JsonResponse::create()`,
`RedirectResponse::create()`, and `StreamedResponse::create()` methods (use
`__construct()` instead)
Expand Down
127 changes: 118 additions & 9 deletions 127 src/Symfony/Component/HttpFoundation/Cookie.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,52 @@ public function __construct(string $name, string $value = null, $expire = 0, ?st
throw new \InvalidArgumentException('The cookie name cannot be empty.');
}

$this->name = $name;
nicolas-grekas marked this conversation as resolved.
Show resolved Hide resolved
$this->value = $value;
$this->domain = $domain;
$this->expire = $this->withExpires($expire)->expire;
$this->path = empty($path) ? '/' : $path;
$this->secure = $secure;
$this->httpOnly = $httpOnly;
$this->raw = $raw;
$this->sameSite = $this->withSameSite($sameSite)->sameSite;
}

/**
* Creates a cookie copy with a new value.
*
* @return static
*/
public function withValue(?string $value): self
ns3777k marked this conversation as resolved.
Show resolved Hide resolved
{
$cookie = clone $this;
$cookie->value = $value;

return $cookie;
}

/**
* Creates a cookie copy with a new domain that the cookie is available to.
*
* @return static
*/
public function withDomain(?string $domain): self
{
$cookie = clone $this;
$cookie->domain = $domain;

return $cookie;
}

/**
* Creates a cookie copy with a new time the cookie expires.
*
* @param int|string|\DateTimeInterface $expire
*
* @return static
*/
public function withExpires($expire = 0): self
{
// convert expiration time to a Unix timestamp
if ($expire instanceof \DateTimeInterface) {
$expire = $expire->format('U');
Expand All @@ -110,15 +156,75 @@ public function __construct(string $name, string $value = null, $expire = 0, ?st
}
}

$this->name = $name;
$this->value = $value;
$this->domain = $domain;
$this->expire = 0 < $expire ? (int) $expire : 0;
$this->path = empty($path) ? '/' : $path;
$this->secure = $secure;
$this->httpOnly = $httpOnly;
$this->raw = $raw;
$cookie = clone $this;
$cookie->expire = 0 < $expire ? (int) $expire : 0;

return $cookie;
}

/**
* Creates a cookie copy with a new path on the server in which the cookie will be available on.
*
* @return static
*/
public function withPath(string $path): self
{
$cookie = clone $this;
$cookie->path = '' === $path ? '/' : $path;

return $cookie;
}

/**
* Creates a cookie copy that only be transmitted over a secure HTTPS connection from the client.
*
* @return static
*/
public function withSecure(bool $secure = true): self
{
$cookie = clone $this;
$cookie->secure = $secure;

return $cookie;
}

/**
* Creates a cookie copy that be accessible only through the HTTP protocol.
*
* @return static
*/
public function withHttpOnly(bool $httpOnly = true): self
{
$cookie = clone $this;
$cookie->httpOnly = $httpOnly;

return $cookie;
}

/**
* Creates a cookie copy that uses no url encoding.
*
* @return static
*/
public function withRaw(bool $raw = true): self
{
if ($raw && false !== strpbrk($this->name, self::$reservedCharsList)) {
throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $this->name));
}

$cookie = clone $this;
$cookie->raw = $raw;

return $cookie;
}

/**
* Creates a cookie copy with SameSite attribute.
*
* @return static
*/
public function withSameSite(?string $sameSite): self
{
if ('' === $sameSite) {
$sameSite = null;
} elseif (null !== $sameSite) {
Expand All @@ -129,7 +235,10 @@ public function __construct(string $name, string $value = null, $expire = 0, ?st
throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.');
}

$this->sameSite = $sameSite;
$cookie = clone $this;
$cookie->sameSite = $sameSite;

return $cookie;
}

/**
Expand Down
110 changes: 106 additions & 4 deletions 110 src/Symfony/Component/HttpFoundation/Tests/CookieTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ public function testInstantiationThrowsExceptionIfRawCookieNameContainsSpecialCh
Cookie::create($name, null, 0, null, null, null, false, true);
}

/**
* @dataProvider namesWithSpecialCharacters
*/
public function testWithRawThrowsExceptionIfCookieNameContainsSpecialCharacters($name)
{
$this->expectException('InvalidArgumentException');
Cookie::create($name)->withRaw();
}

/**
* @dataProvider namesWithSpecialCharacters
*/
Expand All @@ -72,6 +81,10 @@ public function testNegativeExpirationIsNotPossible()
$cookie = Cookie::create('foo', 'bar', -100);

$this->assertSame(0, $cookie->getExpiresTime());

$cookie = Cookie::create('foo', 'bar')->withExpires(-100);

$this->assertSame(0, $cookie->getExpiresTime());
}

public function testGetValue()
Expand All @@ -98,13 +111,21 @@ public function testGetExpiresTime()
$cookie = Cookie::create('foo', 'bar', $expire = time() + 3600);

$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');

$cookie = Cookie::create('foo')->withExpires($expire = time() + 3600);

$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}

public function testGetExpiresTimeIsCastToInt()
{
$cookie = Cookie::create('foo', 'bar', 3600.9);

$this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');

$cookie = Cookie::create('foo')->withExpires(3600.6);

$this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');
}

public function testConstructorWithDateTime()
Expand All @@ -113,6 +134,10 @@ public function testConstructorWithDateTime()
$cookie = Cookie::create('foo', 'bar', $expire);

$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');

$cookie = Cookie::create('foo')->withExpires($expire);

$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}

public function testConstructorWithDateTimeImmutable()
Expand All @@ -121,6 +146,10 @@ public function testConstructorWithDateTimeImmutable()
$cookie = Cookie::create('foo', 'bar', $expire);

$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');

$cookie = Cookie::create('foo')->withValue('bar')->withExpires($expire);

$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}

public function testGetExpiresTimeWithStringValue()
Expand All @@ -130,34 +159,54 @@ public function testGetExpiresTimeWithStringValue()
$expire = strtotime($value);

$this->assertEqualsWithDelta($expire, $cookie->getExpiresTime(), 1, '->getExpiresTime() returns the expire date');

$cookie = Cookie::create('foo')->withValue('bar')->withExpires($value);

$this->assertEqualsWithDelta($expire, $cookie->getExpiresTime(), 1, '->getExpiresTime() returns the expire date');
}

public function testGetDomain()
{
$cookie = Cookie::create('foo', 'bar', 0, '/', '.myfoodomain.com');

$this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');

$cookie = Cookie::create('foo')->withDomain('.mybardomain.com');

$this->assertEquals('.mybardomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');
}

public function testIsSecure()
{
$cookie = Cookie::create('foo', 'bar', 0, '/', '.myfoodomain.com', true);

$this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');

$cookie = Cookie::create('foo')->withSecure(true);

$this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');
}

public function testIsHttpOnly()
{
$cookie = Cookie::create('foo', 'bar', 0, '/', '.myfoodomain.com', false, true);

$this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');

$cookie = Cookie::create('foo')->withHttpOnly(true);

$this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');
}

public function testCookieIsNotCleared()
{
$cookie = Cookie::create('foo', 'bar', time() + 3600 * 24);

$this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet');

$cookie = Cookie::create('foo')->withExpires(time() + 3600 * 24);

$this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet');
}

public function testCookieIsCleared()
Expand All @@ -166,6 +215,10 @@ public function testCookieIsCleared()

$this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired');

$cookie = Cookie::create('foo')->withExpires(time() - 20);

$this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired');

$cookie = Cookie::create('foo', 'bar');

$this->assertFalse($cookie->isCleared());
Expand All @@ -177,21 +230,55 @@ public function testCookieIsCleared()
$cookie = Cookie::create('foo', 'bar', -1);

$this->assertFalse($cookie->isCleared());

$cookie = Cookie::create('foo')->withExpires(-1);

$this->assertFalse($cookie->isCleared());
}

public function testToString()
{
$expected = 'foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly';
$cookie = Cookie::create('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, false, null);
$this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie');
$this->assertEquals($expected, (string) $cookie, '->__toString() returns string representation of the cookie');

$cookie = Cookie::create('foo')
->withValue('bar')
->withExpires(strtotime('Fri, 20-May-2011 15:25:52 GMT'))
->withDomain('.myfoodomain.com')
->withSecure(true)
->withSameSite(null);
$this->assertEquals($expected, (string) $cookie, '->__toString() returns string representation of the cookie');

$expected = 'foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly';
$cookie = Cookie::create('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, false, null);
$this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
$this->assertEquals($expected, (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');

$cookie = Cookie::create('foo')
->withValue('bar with white spaces')
->withExpires(strtotime('Fri, 20-May-2011 15:25:52 GMT'))
->withDomain('.myfoodomain.com')
->withSecure(true)
->withSameSite(null);
$this->assertEquals($expected, (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');

$expected = 'foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; Max-Age=0; path=/admin/; domain=.myfoodomain.com; httponly';
$cookie = Cookie::create('foo', null, 1, '/admin/', '.myfoodomain.com', false, true, false, null);
$this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; Max-Age=0; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
$this->assertEquals($expected, (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');

$cookie = Cookie::create('foo')
->withExpires(1)
->withPath('/admin/')
->withDomain('.myfoodomain.com')
->withSameSite(null);
$this->assertEquals($expected, (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');

$expected = 'foo=bar; path=/; httponly; samesite=lax';
$cookie = Cookie::create('foo', 'bar');
$this->assertEquals('foo=bar; path=/; httponly; samesite=lax', (string) $cookie);
$this->assertEquals($expected, (string) $cookie);

$cookie = Cookie::create('foo')->withValue('bar');
$this->assertEquals($expected, (string) $cookie);
}

public function testRawCookie()
Expand All @@ -200,9 +287,21 @@ public function testRawCookie()
$this->assertFalse($cookie->isRaw());
$this->assertEquals('foo=b%20a%20r; path=/', (string) $cookie);

$cookie = Cookie::create('test')->withValue('t e s t')->withHttpOnly(false)->withSameSite(null);
$this->assertFalse($cookie->isRaw());
$this->assertEquals('test=t%20e%20s%20t; path=/', (string) $cookie);

$cookie = Cookie::create('foo', 'b+a+r', 0, '/', null, false, false, true, null);
$this->assertTrue($cookie->isRaw());
$this->assertEquals('foo=b+a+r; path=/', (string) $cookie);

$cookie = Cookie::create('foo')
->withValue('t+e+s+t')
->withHttpOnly(false)
->withRaw(true)
->withSameSite(null);
$this->assertTrue($cookie->isRaw());
$this->assertEquals('foo=t+e+s+t; path=/', (string) $cookie);
}

public function testGetMaxAge()
Expand Down Expand Up @@ -245,6 +344,9 @@ public function testSameSiteAttribute()

$cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, '');
$this->assertNull($cookie->getSameSite());

$cookie = Cookie::create('foo')->withSameSite('Lax');
$this->assertEquals('lax', $cookie->getSameSite());
}

public function testSetSecureDefault()
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.