Skip to content

Commit f9c6387

Browse files
authored
Merge pull request #62 from battis/feat/chips
feat: add support for CHIPS partitioned cookies
2 parents b90cdd3 + 7d39ae5 commit f9c6387

File tree

4 files changed

+124
-51
lines changed

4 files changed

+124
-51
lines changed

README.md

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
FIG Cookies
2-
===========
1+
# FIG Cookies
32

43
Managing Cookies for PSR-7 Requests and Responses.
54

@@ -15,20 +14,16 @@ Managing Cookies for PSR-7 Requests and Responses.
1514
<br>
1615
[![Join the chat at https://gitter.im/dflydev/dflydev-fig-cookies](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dflydev/dflydev-fig-cookies?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
1716

18-
19-
Installation
20-
------------
17+
## Installation
2118

2219
```bash
2320
$> composer require dflydev/fig-cookies
2421
```
2522

23+
## Concepts
2624

27-
Concepts
28-
--------
29-
30-
FIG Cookies tackles two problems, managing **Cookie** *Request* headers and
31-
managing **Set-Cookie** *Response* headers. It does this by way of introducing
25+
FIG Cookies tackles two problems, managing **Cookie** _Request_ headers and
26+
managing **Set-Cookie** _Response_ headers. It does this by way of introducing
3227
a `Cookies` class to manage collections of `Cookie` instances and a
3328
`SetCookies` class to manage collections of `SetCookie` instances.
3429

@@ -66,9 +61,7 @@ verbose very quickly. In order to get around that, FIG Cookies provides
6661
two facades in an attempt to help simplify things and make the whole process
6762
less verbose.
6863

69-
70-
Basic Usage
71-
-----------
64+
## Basic Usage
7265

7366
The easiest way to start working with FIG Cookies is by using the
7467
`FigRequestCookies` and `FigResponseCookies` classes. They are facades to the
@@ -81,7 +74,6 @@ process so be wary of using too many of these calls in the same section of
8174
code. In some cases it may be better to work with the primitive FIG Cookies
8275
classes directly rather than using the facades.
8376

84-
8577
### Request Cookies
8678

8779
Requests include cookie information in the **Cookie** request header. The
@@ -180,6 +172,7 @@ $setCookie = SetCookie::create('lu')
180172
->withSecure(true)
181173
->withHttpOnly(true)
182174
->withSameSite(SameSite::lax())
175+
->withPartitioned()
183176
;
184177
```
185178

@@ -279,9 +272,7 @@ $setCookie = SetCookie::create('ba')
279272
FigResponseCookies::set($response, $setCookie->expire());
280273
```
281274

282-
283-
FAQ
284-
---
275+
## FAQ
285276

286277
### Do you call `setcookies`?
287278

@@ -290,15 +281,13 @@ No.
290281
Delivery of the rendered `SetCookie` instances is the responsibility of the
291282
PSR-7 client implementation.
292283

293-
294284
### Do you do anything with sessions?
295285

296286
No.
297287

298288
It would be possible to build session handling using cookies on top of FIG
299289
Cookies but it is out of scope for this package.
300290

301-
302291
### Do you read from `$_COOKIES`?
303292

304293
No.
@@ -310,18 +299,14 @@ implementations should be including `$_COOKIES` values in the headers
310299
so in that case FIG Cookies may be interacting with `$_COOKIES`
311300
indirectly.
312301

313-
314-
License
315-
-------
302+
## License
316303

317304
MIT, see LICENSE.
318305

319-
320-
Community
321-
---------
306+
## Community
322307

323308
Want to get involved? Here are a few ways:
324309

325-
* Find us in the #dflydev IRC channel on irc.freenode.org.
326-
* Mention [@dflydev](https://twitter.com/dflydev) on Twitter.
327-
* [![Join the chat at https://gitter.im/dflydev/dflydev-fig-cookies](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dflydev/dflydev-fig-cookies?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
310+
- Find us in the #dflydev IRC channel on irc.freenode.org.
311+
- Mention [@dflydev](https://twitter.com/dflydev) on Twitter.
312+
- [![Join the chat at https://gitter.im/dflydev/dflydev-fig-cookies](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dflydev/dflydev-fig-cookies?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

src/Dflydev/FigCookies/SetCookie.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class SetCookie
4242
private $httpOnly = false;
4343
/** @var SameSite|null */
4444
private $sameSite;
45+
/** @var bool */
46+
private $partitioned = false;
4547

4648
private function __construct(string $name, ?string $value = null)
4749
{
@@ -94,6 +96,11 @@ public function getSameSite(): ?SameSite
9496
return $this->sameSite;
9597
}
9698

99+
public function getPartitioned(): bool
100+
{
101+
return $this->partitioned;
102+
}
103+
97104
public function withValue(?string $value = null): self
98105
{
99106
$clone = clone $this;
@@ -212,6 +219,18 @@ public function withoutSameSite(): self
212219
return $clone;
213220
}
214221

222+
public function withPartitioned(bool $partitioned = true): self
223+
{
224+
$clone = clone $this;
225+
226+
$clone->partitioned = $partitioned;
227+
if ($partitioned) {
228+
$clone->secure = true;
229+
}
230+
231+
return $clone;
232+
}
233+
215234
public function __toString(): string
216235
{
217236
$cookieStringParts = [
@@ -225,6 +244,7 @@ public function __toString(): string
225244
$cookieStringParts = $this->appendFormattedSecurePartIfSet($cookieStringParts);
226245
$cookieStringParts = $this->appendFormattedHttpOnlyPartIfSet($cookieStringParts);
227246
$cookieStringParts = $this->appendFormattedSameSitePartIfSet($cookieStringParts);
247+
$cookieStringParts = $this->appendFormattedPartitionedPartIfSet($cookieStringParts);
228248

229249
return implode('; ', $cookieStringParts);
230250
}
@@ -301,6 +321,9 @@ public static function fromSetCookieString(string $string): self
301321
case 'samesite':
302322
$setCookie = $setCookie->withSameSite(SameSite::fromString((string) $attributeValue));
303323
break;
324+
case 'partitioned':
325+
$setCookie = $setCookie->withPartitioned();
326+
break;
304327
}
305328
}
306329

@@ -406,4 +429,18 @@ private function appendFormattedSameSitePartIfSet(array $cookieStringParts): arr
406429

407430
return $cookieStringParts;
408431
}
432+
433+
/**
434+
* @param string[] $cookieStringParts
435+
*
436+
* @return string[]
437+
*/
438+
private function appendFormattedPartitionedPartIfSet(array $cookieStringParts): array
439+
{
440+
if ($this->partitioned) {
441+
$cookieStringParts[] = 'Partitioned';
442+
}
443+
444+
return $cookieStringParts;
445+
}
409446
}

tests/Dflydev/FigCookies/SetCookieTest.php

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -110,37 +110,50 @@ public function provideParsesFromSetCookieStringData(): array
110110
[
111111
'lu=Rg3vHJZnehYLjVg7qi3bZjzg; Domain=.example.com; Path=/; Expires=Tue, 15 Jan 2013 21:47:38 GMT; Max-Age=500; Secure; HttpOnly',
112112
SetCookie::create('lu')
113-
->withValue('Rg3vHJZnehYLjVg7qi3bZjzg')
114-
->withExpires(new DateTime('Tue, 15-Jan-2013 21:47:38 GMT'))
115-
->withMaxAge(500)
116-
->withPath('/')
117-
->withDomain('.example.com')
118-
->withSecure(true)
119-
->withHttpOnly(true),
113+
->withValue('Rg3vHJZnehYLjVg7qi3bZjzg')
114+
->withExpires(new DateTime('Tue, 15-Jan-2013 21:47:38 GMT'))
115+
->withMaxAge(500)
116+
->withPath('/')
117+
->withDomain('.example.com')
118+
->withSecure(true)
119+
->withHttpOnly(true),
120120
],
121121
[
122122
'lu=Rg3vHJZnehYLjVg7qi3bZjzg; Domain=.example.com; Path=/; Expires=Tue, 15 Jan 2013 21:47:38 GMT; Max-Age=500; Secure; HttpOnly; SameSite=Strict',
123123
SetCookie::create('lu')
124-
->withValue('Rg3vHJZnehYLjVg7qi3bZjzg')
125-
->withExpires(new DateTime('Tue, 15-Jan-2013 21:47:38 GMT'))
126-
->withMaxAge(500)
127-
->withPath('/')
128-
->withDomain('.example.com')
129-
->withSecure(true)
130-
->withHttpOnly(true)
131-
->withSameSite(SameSite::strict()),
124+
->withValue('Rg3vHJZnehYLjVg7qi3bZjzg')
125+
->withExpires(new DateTime('Tue, 15-Jan-2013 21:47:38 GMT'))
126+
->withMaxAge(500)
127+
->withPath('/')
128+
->withDomain('.example.com')
129+
->withSecure(true)
130+
->withHttpOnly(true)
131+
->withSameSite(SameSite::strict()),
132132
],
133133
[
134134
'lu=Rg3vHJZnehYLjVg7qi3bZjzg; Domain=.example.com; Path=/; Expires=Tue, 15 Jan 2013 21:47:38 GMT; Max-Age=500; Secure; HttpOnly; SameSite=Lax',
135135
SetCookie::create('lu')
136-
->withValue('Rg3vHJZnehYLjVg7qi3bZjzg')
137-
->withExpires(new DateTime('Tue, 15-Jan-2013 21:47:38 GMT'))
138-
->withMaxAge(500)
139-
->withPath('/')
140-
->withDomain('.example.com')
141-
->withSecure(true)
142-
->withHttpOnly(true)
143-
->withSameSite(SameSite::lax()),
136+
->withValue('Rg3vHJZnehYLjVg7qi3bZjzg')
137+
->withExpires(new DateTime('Tue, 15-Jan-2013 21:47:38 GMT'))
138+
->withMaxAge(500)
139+
->withPath('/')
140+
->withDomain('.example.com')
141+
->withSecure(true)
142+
->withHttpOnly(true)
143+
->withSameSite(SameSite::lax()),
144+
],
145+
[
146+
'lu=d2ioU4.4KDUjjnCGNut4; Domain=.example.com; Path=/; Expires=Tue, 15 Jan 2013 21:47:38 GMT; Max-Age=500; Secure; HttpOnly; SameSite=Lax; Partitioned',
147+
SetCookie::create('lu')
148+
->withValue('d2ioU4.4KDUjjnCGNut4')
149+
->withExpires(new DateTime('Tue, 15-Jan-2013 21:47:38 GMT'))
150+
->withMaxAge(500)
151+
->withPath('/')
152+
->withDomain('.example.com')
153+
->withSecure(true)
154+
->withHttpOnly(true)
155+
->withSameSite(SameSite::lax())
156+
->withPartitioned(),
144157
],
145158
];
146159
}
@@ -171,6 +184,30 @@ public function it_creates_long_living_cookies(): void
171184
self::assertGreaterThan($fourYearsFromNow, $setCookie->getExpires());
172185
}
173186

187+
/**
188+
* @test
189+
*/
190+
public function it_creates_partitioned_cookies(): void
191+
{
192+
$setCookie = SetCookie::create('chips_cookie')->withPartitioned();
193+
self::assertEquals(true, $setCookie->getPartitioned());
194+
}
195+
196+
/**
197+
* @test
198+
*/
199+
public function it_does_not_set_partitioned_cookies_by_default(): void
200+
{
201+
$setCookie = SetCookie::create('non_chips_cookie');
202+
self::assertEquals(false, $setCookie->getPartitioned());
203+
}
204+
205+
public function partitioned_cookies_are_secure(): void
206+
{
207+
$setCookie = SetCookie::create('maybe_secure_partitioned_cookie')->withPartitioned();
208+
self::assertEquals(true, $setCookie->getSecure());
209+
}
210+
174211
/** @test */
175212
public function SameSite_modifier_can_be_added_and_removed(): void
176213
{

tests/Dflydev/FigCookies/SetCookiesTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,20 @@ public function provideSetCookieStringsAndExpectedSetCookiesData(): array
181181
SetCookie::create('c', 'CCC'),
182182
],
183183
],
184+
[
185+
[
186+
'd=DDD',
187+
'e=EEE; Partitioned',
188+
'f=FFF',
189+
'g=GGG; Partitioned',
190+
],
191+
[
192+
SetCookie::create('d', 'DDD'),
193+
SetCookie::create('e', 'EEE')->withPartitioned(),
194+
SetCookie::create('f', 'FFF'),
195+
SetCookie::create('g', 'GGG')->withPartitioned(),
196+
],
197+
],
184198
];
185199
}
186200

0 commit comments

Comments
 (0)