8000 [HttpFoundation] added withers to Cookie class · symfony/symfony@549afaa · GitHub
[go: up one dir, main page]

Skip to content

Commit 549afaa

Browse files
ns3777kfabpot
authored andcommitted
[HttpFoundation] added withers to Cookie class
1 parent 0c6d64b commit 549afaa

File tree

3 files changed

+227
-13
lines changed

3 files changed

+227
-13
lines changed

src/Symfony/Component/HttpFoundation/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ CHANGELOG
44
5.1.0
55
-----
66

7+
* added `Cookie::withValue`, `Cookie::withDomain`, `Cookie::withExpires`,
8+
`Cookie::withPath`, `Cookie::withSecure`, `Cookie::withHttpOnly`,
9+
`Cookie::withRaw`, `Cookie::withSameSite`
710
* Deprecate `Response::create()`, `JsonResponse::create()`,
811
`RedirectResponse::create()`, and `StreamedResponse::create()` methods (use
912
`__construct()` instead)

src/Symfony/Component/HttpFoundation/Cookie.php

Lines changed: 118 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,52 @@ public function __construct(string $name, string $value = null, $expire = 0, ?st
9999
throw new \InvalidArgumentException('The cookie name cannot be empty.');
100100
}
101101

102+
$this->name = $name;
103+
$this->value = $value;
104+
$this->domain = $domain;
105+
$this->expire = $this->withExpires($expire)->expire;
106+
$this->path = empty($path) ? '/' : $path;
107+
$this->secure = $secure;
108+
$this->httpOnly = $httpOnly;
109+
$this->raw = $raw;
110+
$this->sameSite = $this->withSameSite($sameSite)->sameSite;
111+
}
112+
113+
/**
114+
* Creates a cookie copy with a new value.
115+
*
116+
* @return static
117+
*/
118+
public function withValue(?string $value): self
119+
{
120+
$cookie = clone $this;
121+
$cookie->value = $value;
122+
123+
return $cookie;
124+
}
125+
126+
/**
127+
* Creates a cookie copy with a new domain that the cookie is available to.
128+
*
129+
* @return static
130+
*/
131+
public function withDomain(?string $domain): self
132+
{
133+
$cookie = clone $this;
134+
$cookie->domain = $domain;
135+
136+
return $cookie;
137+
}
138+
139+
/**
140+
* Creates a cookie copy with a new time the cookie expires.
141+
*
142+
* @param int|string|\DateTimeInterface $expire
143+
*
144+
* @return static
145+
*/
146+
public function withExpires($expire = 0): self
147+
{
102148
// convert expiration time to a Unix timestamp
103149
if ($expire instanceof \DateTimeInterface) {
104150
$expire = $expire->format('U');
@@ -110,15 +156,75 @@ public function __con F438 struct(string $name, string $value = null, $expire = 0, ?st
110156
}
111157
}
112158

113-
$this->name = $name;
114-
$this->value = $value;
115-
$this->domain = $domain;
116-
$this->expire = 0 < $expire ? (int) $expire : 0;
117-
$this->path = empty($path) ? '/' : $path;
118-
$this->secure = $secure;
119-
$this->httpOnly = $httpOnly;
120-
$this->raw = $raw;
159+
$cookie = clone $this;
160+
$cookie->expire = 0 < $expire ? (int) $expire : 0;
161+
162+
return $cookie;
163+
}
121164

165+
/**
166+
* Creates a cookie copy with a new path on the server in which the cookie will be available on.
167+
*
168+
* @return static
169+
*/
170+
public function withPath(string $path): self
171+
{
172+
$cookie = clone $this;
173+
$cookie->path = '' === $path ? '/' : $path;
174+
175+
return $cookie;
176+
}
177+
178+
/**
179+
* Creates a cookie copy that only be transmitted over a secure HTTPS connection from the client.
180+
*
181+
* @return static
182+
*/
183+
public function withSecure(bool $secure = true): self
184+
{
185+
$cookie = clone $this;
186+
$cookie->secure = $secure;
187+
188+
return $cookie;
189+
}
190+
191+
/**
192+
* Creates a cookie copy that be accessible only through the HTTP protocol.
193+
*
194+
* @return static
195+
*/
196+
public function withHttpOnly(bool $httpOnly = true): self
197+
{
198+
$cookie = clone $this;
199+
$cookie->httpOnly = $httpOnly;
200+
201+
return $cookie;
202+
}
203+
204+
/**
205+
* Creates a cookie copy that uses no url encoding.
206+
*
207+
* @return static
208+
*/
209+
public function withRaw(bool $raw = true): self
210+
{
211+
if ($raw && false !== strpbrk($this->name, self::$reservedCharsList)) {
212+
throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $this->name));
213+
}
214+
215+
$cookie = clone $this;
216+
$cookie->raw = $raw;
217+
218+
return $cookie;
219+
}
220+
221+
/**
222+
* Creates a cookie copy with SameSite attribute.
223+
*
224+
* @return static
225+
*/
226+
public function withSameSite(?string $sameSite): self
227+
{
122228
if ('' === $sameSite) {
123229
$sameSite = null;
124230
} elseif (null !== $sameSite) {
@@ -129,7 +235,10 @@ public function __construct(string $name, string $value = null, $expire = 0, ?st
129235
throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.');
130236
}
131237

132-
$this->sameSite = $sameSite;
238+
$cookie = clone $this;
239+
$cookie->sameSite = $sameSite;
240+
241+
return $cookie;
133242
}
134243

135244
/**

src/Symfony/Component/HttpFoundation/Tests/CookieTest.php

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ public function testInstantiationThrowsExceptionIfRawCookieNameContainsSpecialCh
4747
Cookie::create($name, null, 0, null, null, null, false, true);
4848
}
4949

50+
/**
51+
* @dataProvider namesWithSpecialCharacters
52+
*/
53+
public function testWithRawThrowsExceptionIfCookieNameContainsSpecialCharacters($name)
54+
{
55+
$this->expectException('InvalidArgumentException');
56+
Cookie::create($name)->withRaw();
57+
}
58+
5059
/**
5160
* @dataProvider namesWithSpecialCharacters
5261
*/
@@ -72,6 +81,10 @@ public function testNegativeExpirationIsNotPossible()
7281
$cookie = Cookie::create('foo', 'bar', -100);
7382

7483
$this->assertSame(0, $cookie->getExpiresTime());
84+
85+
$cookie = Cookie::create('foo', 'bar')->withExpires(-100);
86+
87+
$this->assertSame(0, $cookie->getExpiresTime());
7588
}
7689

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

100113
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
114+
115+
$cookie = Cookie::create('foo')->withExpires($expire = time() + 3600);
116+
117+
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
101118
}
102119

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

107124
$this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');
125+
126+
$cookie = Cookie::create('foo')->withExpires(3600.6);
127+
128+
$this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');
108129
}
109130

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

115136
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
137+
138+
$cookie = Cookie::create('foo')->withExpires($expire);
139+
140+
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
116141
}
117142

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

123148
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
149+
150+
$cookie = Cookie::create('foo')->withValue('bar')->withExpires($expire);
151+
152+
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
124153
}
125154

126155
public function testGetExpiresTimeWithStringValue()
@@ -130,34 +159,54 @@ public function testGetExpiresTimeWithStringValue()
130159
$expire = strtotime($value);
131160

132161
$this->assertEqualsWithDelta($expire, $cookie->getExpiresTime(), 1, '->getExpiresTime() returns the expire date');
162+
163+
$cookie = Cookie::create('foo')->withVal 10000 ue('bar')->withExpires($value);
164+
165+
$this->assertEqualsWithDelta($expire, $cookie->getExpiresTime(), 1, '->getExpiresTime() returns the expire date');
133166
}
134167

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

139172
$this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');
173+
174+
$cookie = Cookie::create('foo')->withDomain('.mybardomain.com');
175+
176+
$this->assertEquals('.mybardomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');
140177
}
141178

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

146183
$this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');
184+
185+
$cookie = Cookie::create('foo')->withSecure(true);
186+
187+
$this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');
147188
}
148189

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

153194
$this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');
195+
196+
$cookie = Cookie::create('foo')->withHttpOnly(true);
197+
198+
$this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');
154199
}
155200

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

160205
$this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet');
206+
207+
$cookie = Cookie::create('foo')->withExpires(time() + 3600 * 24);
208+
209+
$this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet');
161210
}
162211

163212
public function testCookieIsCleared()
@@ -166,6 +215,10 @@ public function testCookieIsCleared()
166215

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

218+
$cookie = Cookie::create('foo')->withExpires(time() - 20);
219+
220+
$this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired');
221+
169222
$cookie = Cookie::create('foo', 'bar');
170223

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

179232
$this->assertFalse($cookie->isCleared());
233+
234+
$cookie = Cookie::create('foo')->withExpires(-1);
235+
236+
$this->assertFalse($cookie->isCleared());
180237
}
181238

182239
public function testToString()
183240
{
241+
$expected = 'foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly';
184242
$cookie = Cookie::create('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, false, null);
185-
$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');
243+
$this->assertEquals($expected, (string) $cookie, '->__toString() returns string representation of the cookie');
244+
245+
$cookie = Cookie::create('foo')
246+
->withValue('bar')
247+
->withExpires(strtotime('Fri, 20-May-2011 15:25:52 GMT'))
248+
->withDomain('.myfoodomain.com')
249+
->withSecure(true)
250+
->withSameSite(null);
251+
$this->assertEquals($expected, (string) $cookie, '->__toString() returns string representation of the cookie');
186252

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

257+
$cookie = Cookie::create('foo')
258+
->withValue('bar with white spaces')
259+
->withExpires(strtotime('Fri, 20-May-2011 15:25:52 GMT'))
260+
->withDomain('.myfoodomain.com')
261+
->withSecure(true)
262+
->withSameSite(null);
263+
$this->assertEquals($expected, (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
264+
265+
$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';
190266
$cookie = Cookie::create('foo', null, 1, '/admin/', '.myfoodomain.com', false, true, false, null);
191-
$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');
267+
$this->assertEquals($expected, (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
268+
269+
$cookie = Cookie::create('foo')
270+
->withExpires(1)
271+
->withPath('/admin/')
272+
->withDomain('.myfoodomain.com')
273+
->withSameSite(null);
274+
$this->assertEquals($expected, (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
192275

276+
$expected = 'foo=bar; path=/; httponly; samesite=lax';
193277
$cookie = Cookie::create('foo', 'bar');
194-
$this->assertEquals('foo=bar; path=/; httponly; samesite=lax', (string) $cookie);
278+
$this->assertEquals($expected, (string) $cookie);
279+
280+
$cookie = Cookie::create('foo')->withValue('bar');
281+
$this->assertEquals($expected, (string) $cookie);
195282
}
196283

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

290+
$cookie = Cookie::create('test')->withValue('t e s t')->withHttpOnly(false)->withSameSite(null);
291+
$this->assertFalse($cookie->isRaw());
292+
$this->assertEquals('test=t%20e%20s%20t; path=/', (string) $cookie);
293+
203294
$cookie = Cookie::create('foo', 'b+a+r', 0, '/', null, false, false, true, null);
204295
$this->assertTrue($cookie->isRaw());
205296
$this->assertEquals('foo=b+a+r; path=/', (string) $cookie);
297+
298+
$cookie = Cookie::create('foo')
299+
->withValue('t+e+s+t')
300+
->withHttpOnly(false)
301+
->withRaw(true)
302+
->withSameSite(null);
303+
$this->assertTrue($cookie->isRaw());
304+
$this->assertEquals('foo=t+e+s+t; path=/', (string) $cookie);
206305
}
207306

208307
public function testGetMaxAge()
@@ -245,6 +344,9 @@ public function testSameSiteAttribute()
245344

246345
$cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, '');
247346
$this->assertNull($cookie->getSameSite());
347+
348+
$cookie = Cookie::create('foo')->withSameSite('Lax');
349+
$this->assertEquals('lax', $cookie->getSameSite());
248350
}
249351

250352
public function testSetSecureDefault()

0 commit comments

Comments
 (0)
0