Skip to content

Commit 275e895

Browse files
committed
Add support for CHIPS
1 parent 238b9d2 commit 275e895

File tree

2 files changed

+82
-5
lines changed

2 files changed

+82
-5
lines changed

src/Cookie.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class Cookie implements \Stringable
3434
protected ?string $path = null;
3535
protected ?string $sameSite = null;
3636
protected bool $secure = false;
37+
protected bool $partitioned = false;
3738
protected string $value;
3839

3940
/**
@@ -86,6 +87,10 @@ public function toString() : string
8687
if ($part !== null) {
8788
$string .= '; SameSite=' . $part;
8889
}
90+
$part = $this->isPartitioned();
91+
if ($part) {
92+
$string .= '; Partitioned';
93+
}
8994
return $string;
9095
}
9196

@@ -275,6 +280,29 @@ public function isSecure() : bool
275280
return $this->secure;
276281
}
277282

283+
/**
284+
* @param bool $partitioned
285+
*
286+
* @see https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies
287+
* @see https://wiki.php.net/rfc/chips
288+
*
289+
* @return static
290+
*/
291+
public function setPartitioned(bool $partitioned = true) : static
292+
{
293+
$this->partitioned = $partitioned;
294+
return $this;
295+
}
296+
297+
/**
298+
* @return bool
299+
*/
300+
#[Pure]
301+
public function isPartitioned() : bool
302+
{
303+
return $this->partitioned;
304+
}
305+
278306
/**
279307
* @return bool
280308
*/
@@ -299,6 +327,7 @@ public function send() : bool
299327
if ($value !== null) {
300328
$options['samesite'] = $value;
301329
}
330+
$options['partitioned'] = $this->isPartitioned();
302331
// @phpstan-ignore-next-line
303332
return \setcookie($this->getName(), $this->getValue(), $options);
304333
}
@@ -347,6 +376,9 @@ public static function parse(string $line) : ?Cookie
347376
case 'samesite':
348377
$cookie->setSameSite($val);
349378
break;
379+
case 'partitioned':
380+
$cookie->setPartitioned();
381+
break;
350382
}
351383
}
352384
return $cookie;

tests/CookieTest.php

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ public function testExpires() : void
4242
self::assertSame((string) $time, $this->cookie->getExpires()->format('U'));
4343
$this->cookie->setExpires(new \DateTime('-5 hours'));
4444
self::assertInstanceOf(\DateTime::class, $this->cookie->getExpires());
45-
self::assertSame((string) (\time() - 5 * 60 * 60), $this->cookie->getExpires()->format('U'));
45+
self::assertSame(
46+
(string) (\time() - 5 * 60 * 60),
47+
$this->cookie->getExpires()->format('U')
48+
);
4649
self::assertTrue($this->cookie->isExpired());
4750
$this->cookie->setExpires(null);
4851
self::assertNull($this->cookie->getExpires());
@@ -97,6 +100,15 @@ public function testSecure() : void
97100
self::assertFalse($this->cookie->isSecure());
98101
}
99102

103+
public function testPartitioned() : void
104+
{
105+
self::assertFalse($this->cookie->isPartitioned());
106+
$this->cookie->setPartitioned();
107+
self::assertTrue($this->cookie->isPartitioned());
108+
$this->cookie->setPartitioned(false);
109+
self::assertFalse($this->cookie->isPartitioned());
110+
}
111+
100112
/**
101113
* @runInSeparateProcess
102114
*/
@@ -118,6 +130,7 @@ public function testSend() : void
118130
$this->cookie->setDomain('domain.tld')
119131
->setPath('/blog')
120132
->setSecure()
133+
->setPartitioned()
121134
->setHttpOnly()
122135
->setSameSite('strict')
123136
->setValue('baz')
@@ -136,15 +149,35 @@ public function testSend() : void
136149
);
137150
}
138151

152+
/**
153+
* @runInSeparateProcess
154+
*/
155+
public function testSendWithPartitionedAndWithoutSecure() : void
156+
{
157+
$this->cookie->setPartitioned();
158+
$this->expectException(\ValueError::class);
159+
$this->expectExceptionMessage(
160+
'setcookie(): "partitioned" option cannot be used without "secure" option'
161+
);
162+
$this->cookie->send();
163+
}
164+
139165
public function testString() : void
140166
{
141167
$time = \time() + 30;
142-
$expected = 'foo=baz; expires='
143-
. \date('D, d M Y H:i:s', $time)
144-
. ' GMT; Max-Age=30; path=/blog; domain=domain.tld; secure; HttpOnly; SameSite=Strict';
168+
$expected = 'foo=baz'
169+
. '; expires=' . \date('D, d M Y H:i:s', $time) . ' GMT'
170+
. '; Max-Age=30'
171+
. '; path=/blog'
172+
. '; domain=domain.tld'
173+
. '; secure'
174+
. '; HttpOnly'
175+
. '; SameSite=Strict'
176+
. '; Partitioned';
145177
$this->cookie->setDomain('domain.tld')
146178
->setPath('/blog')
147179
->setSecure()
180+
->setPartitioned()
148181
->setHttpOnly()
149182
->setSameSite('strict')
150183
->setValue('baz')
@@ -163,7 +196,17 @@ public function testValue() : void
163196

164197
public function testParse() : void
165198
{
166-
$cookie = Cookie::parse('session_id=35ab1d7a4955d926a3694ab5990c0eb1; expires=Thu, 11-Jul-2019 04:57:19 GMT; Max-Age=0; path=/admin; domain=localhost; secure; HttpOnly; SameSite=Strict');
199+
$cookie = Cookie::parse(
200+
'session_id=35ab1d7a4955d926a3694ab5990c0eb1'
201+
. '; expires=Thu, 11-Jul-2019 04:57:19 GMT'
202+
. '; Max-Age=0'
203+
. '; path=/admin'
204+
. '; domain=localhost'
205+
. '; secure'
206+
. '; HttpOnly'
207+
. '; SameSite=Strict'
208+
. '; Partitioned'
209+
);
167210
self::assertSame('session_id', $cookie->getName());
168211
self::assertSame('35ab1d7a4955d926a3694ab5990c0eb1', $cookie->getValue());
169212
self::assertSame('Thu, 11 Jul 2019 04:57:19 +0000', $cookie->getExpires()->format('r'));
@@ -172,11 +215,13 @@ public function testParse() : void
172215
self::assertTrue($cookie->isSecure());
173216
self::assertTrue($cookie->isHttpOnly());
174217
self::assertSame('Strict', $cookie->getSameSite());
218+
self::assertTrue($cookie->isPartitioned());
175219
$cookie = Cookie::parse('sid=foo;secure;;HttpOnly;');
176220
self::assertSame('sid', $cookie->getName());
177221
self::assertSame('foo', $cookie->getValue());
178222
self::assertTrue($cookie->isSecure());
179223
self::assertTrue($cookie->isHttpOnly());
224+
self::assertFalse($cookie->isPartitioned());
180225
self::assertNull(Cookie::parse('sid'));
181226
self::assertNull(Cookie::parse(';sid=foo'));
182227
}

0 commit comments

Comments
 (0)