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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy