8000 feature #38369 [HttpFoundation] Expired cookies string representation… · symfony/symfony@dfcde5b · GitHub
[go: up one dir, main page]

Skip to content

Commit dfcde5b

Browse files
committed
feature #38369 [HttpFoundation] Expired cookies string representation consistency & tests (iquito)
This PR was squashed before being merged into the 5.2-dev branch. Discussion ---------- [HttpFoundation] Expired cookies string representation consistency & tests | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | License | MIT These changes add consistent behavior when converting expired cookies back and forth from string representation into `Symfony\Component\HttpFoundation\Cookie` instances in `Cookie::fromString`: - When `Max-Age` is zero and `expires` is in the past, the `expires` date is kept as is (previous behavior: `expires` is overwritten with current timestamp because it is reset to current timestamp + `Max-Age`) - When `Max-Age` is zero and `expires` is in the future, expires is reset to current timestamp, as `Max-Age` is the preferred "source of truth" (same as previous behavior) - Add tests for how the Cookie class handles `Max-Age` in a cookie string and how `expires` and `Max-Age` interact - Extract helper function `expiresTimestamp` so converting to a unix timestamp can be reused in `Cookie::fromString` This is more a new feature than a bug fix in my mind, therefore I would include it in 5.1+. Commits ------- 4f5d5ec [HttpFoundation] Expired cookies string representation consistency & tests
2 parents 383d73e + 4f5d5ec commit dfcde5b

File tree

2 files changed

+48
-6
lines changed

2 files changed

+48
-6
lines changed

src/Symfony/Component/HttpFoundation/Cookie.php

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ public static function fromString(string $cookie, bool $decode = false)
6262
$value = isset($part[1]) ? ($decode ? urldecode($part[1]) : $part[1]) : null;
6363

6464
$data = HeaderUtils::combine($parts) + $data;
65+
$data['expires'] = self::expiresTimestamp($data['expires']);
6566

66-
if (isset($data['max-age'])) {
67+
if (isset($data['max-age']) && ($data['max-age'] > 0 || $data['expires'] > time())) {
6768
$data['expires'] = time() + (int) $data['max-age'];
6869
}
6970

@@ -102,7 +103,7 @@ public function __construct(string $name, string $value = null, $expire = 0, ?st
102103
$this->name = $name;
103104
$this->value = $value;
104105
$this->domain = $domain;
105-
$this->expire = $this->withExpires($expire)->expire;
106+
$this->expire = self::expiresTimestamp($expire);
106107
$this->path = empty($path) ? '/' : $path;
107108
$this->secure = $secure;
108109
$this->httpOnly = $httpOnly;
@@ -144,6 +145,21 @@ public function withDomain(?string $domain): self
144145
* @return static
145146
*/
146147
public function withExpires($expire = 0): self
148+
{
149+
$cookie = clone $this;
150+
$cookie->expire = self::expiresTimestamp($expire);
151+
152+
return $cookie;
153+
}
154+
155+
/**
156+
* Converts expires formats to a unix timestamp.
157+
*
158+
* @param int|string|\DateTimeInterface $expire
159+
*
160+
* @return int
161+
*/
162+
private static function expiresTimestamp($expire = 0)
147163
{
148164
// convert expiration time to a Unix timestamp
149165
if ($expire instanceof \DateTimeInterface) {
@@ -156,10 +172,7 @@ public function withExpires($expire = 0): self
156172
}
157173
}
158174

159-
$cookie = clone $this;
160-
$cookie->expire = 0 < $expire ? (int) $expire : 0;
161-
162-
return $cookie;
175+
return 0 < $expire ? (int) $expire : 0;
163176
}
164177

165178
/**

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,4 +363,33 @@ public function testSetSecureDefault()
363363

364364
$this->assertFalse($cookie->isSecure());
365365
}
366+
367+
public function testMaxAge()
368+
{
369+
$futureDateOneHour = gmdate('D, d-M-Y H:i:s T', time() + 3600);
370+
371+
$cookie = Cookie::fromString('foo=bar; Max-Age=3600; path=/');
372+
$this->assertEquals('foo=bar; expires='.$futureDateOneHour.'; Max-Age=3600; path=/', $cookie->__toString());
373+
374+
$cookie = Cookie::fromString('foo=bar; expires='.$futureDateOneHour.'; Max-Age=3600; path=/');
375+
$this->assertEquals('foo=bar; expires='.$futureDateOneHour.'; Max-Age=3600; path=/', $cookie->__toString());
376+
377+
$futureDateHalfHour = gmdate('D, d-M-Y H:i:s T', time() + 1800);
378+
379+
// Max-Age value takes precedence before expires
380+
$cookie = Cookie::fromString('foo=bar; expires='.$futureDateHalfHour.'; Max-Age=3600; path=/');
381+
$this->assertEquals('foo=bar; expires='.$futureDateOneHour.'; Max-Age=3600; path=/', $cookie->__toString());
382+
}
383+
384+
public function testExpiredWithMaxAge()
385+
{
386+
$cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/');
387+
$this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/', $cookie->__toString());
388+
389+
$futureDate = gmdate('D, d-M-Y H:i:s T', time() + 864000);
390+
391+
$cookie = Cookie::fromString('foo=bar; expires='.$futureDate.'; Max-Age=0; path=/');
392+
$this->assertEquals(time(), $cookie->getExpiresTime());
393+
$this->assertEquals('foo=bar; expires='.gmdate('D, d-M-Y H:i:s T', $cookie->getExpiresTime()).'; Max-Age=0; path=/', $cookie->__toString());
394+
}
366395
}

0 commit comments

Comments
 (0)
0