Skip to content

Commit

Permalink
Merge pull request #10493 from cakephp/3next-response-cookies
Browse files Browse the repository at this point in the history
3.next - Integrate CookieCollection into Response
  • Loading branch information
markstory committed Apr 11, 2017
2 parents b891683 + 1c6610c commit b6ad881
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 79 deletions.
4 changes: 2 additions & 2 deletions src/Http/Client/Response.php
Expand Up @@ -442,7 +442,7 @@ public function getCookieData($name)
return null;
}

return $this->cookies->get($name)->toArrayCompat();
return $this->cookies->get($name)->toArrayClient();
}

/**
Expand All @@ -469,7 +469,7 @@ protected function _getCookies()

$cookies = [];
foreach ($this->cookies as $cookie) {
$cookies[$cookie->getName()] = $cookie->toArrayCompat();
$cookies[$cookie->getName()] = $cookie->toArrayClient();
}

return $cookies;
Expand Down
82 changes: 62 additions & 20 deletions src/Http/Cookie/Cookie.php
Expand Up @@ -15,7 +15,9 @@

use Cake\Chronos\Chronos;
use Cake\Utility\Hash;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimezone;
use InvalidArgumentException;
use RuntimeException;

Expand Down Expand Up @@ -79,9 +81,9 @@ class Cookie implements CookieInterface
/**
* Expiration time
*
* @var int
* @var DateTimeInterface
*/
protected $expiresAt = 0;
protected $expiresAt;

/**
* Path
Expand Down Expand Up @@ -152,23 +154,41 @@ public function __construct(

$this->validateBool($secure);
$this->secure = $secure;

if ($expiresAt !== null) {
$this->expiresAt = (int)$expiresAt->format('U');
if ($expiresAt) {
$expiresAt = $expiresAt->setTimezone(new DateTimezone('GMT'));
}
$this->expiresAt = $expiresAt;
}

/**
* Builds the expiration value part of the header string
*
* @return string
*/
protected function _buildExpirationValue()
protected function getFormattedExpires()
{
if (!$this->expiresAt) {
return '';
}

return $this->expiresAt->format(static::EXPIRES_FORMAT);
}

/**
* Get the timestamp from the expiration time
*
* Timestamps are strings as large timestamps can overflow MAX_INT
* in 32bit systems.
*
* @return string|null The expiry time as a string timestamp.
*/
public function getExpiresTimestamp()
{
return sprintf(
'expires=%s',
gmdate(static::EXPIRES_FORMAT, $this->expiresAt)
);
if (!$this->expiresAt) {
return null;
}

return $this->expiresAt->format('U');
}

/**
Expand All @@ -184,8 +204,8 @@ public function toHeaderValue()
}
$headerValue[] = sprintf('%s=%s', $this->name, urlencode($value));

if ($this->expiresAt !== 0) {
$headerValue[] = $this->_buildExpirationValue();
if ($this->expiresAt) {
$headerValue[] = sprintf('expires=%s', $this->getFormattedExpires());
}
if ($this->path !== '') {
$headerValue[] = sprintf('path=%s', $this->path);
Expand Down Expand Up @@ -248,10 +268,11 @@ public function getName()
* @param string $name Name of the cookie
* @return void
* @throws \InvalidArgumentException
* @link https://tools.ietf.org/html/rfc2616#section-2.2 Rules for naming cookies.
*/
protected function validateName($name)
{
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
if (preg_match("/[=,;\t\r\n\013\014]/", $name)) {
throw new InvalidArgumentException(
sprintf('The cookie name `%s` contains invalid characters.', $name)
);
Expand Down Expand Up @@ -441,15 +462,15 @@ public function isHttpOnly()
public function withExpiry(DateTimeInterface $dateTime)
{
$new = clone $this;
$new->expiresAt = (int)$dateTime->format('U');
$new->expiresAt = $dateTime->setTimezone(new DateTimezone('GMT'));

return $new;
}

/**
* Get the current expiry time
*
* @return int|null Timestamp of expiry or null
* @return DateTimeInterface|null Timestamp of expiry or null
*/
public function getExpiry()
{
Expand All @@ -464,7 +485,7 @@ public function getExpiry()
public function withNeverExpire()
{
$new = clone $this;
$new->expiresAt = Chronos::createFromDate(2038, 1, 1)->format('U');
$new->expiresAt = Chronos::createFromDate(2038, 1, 1);

return $new;
}
Expand All @@ -479,7 +500,7 @@ public function withNeverExpire()
public function withExpired()
{
$new = clone $this;
$new->expiresAt = Chronos::parse('-1 year')->format('U');
$new->expiresAt = Chronos::parse('-1 year');

return $new;
}
Expand Down Expand Up @@ -619,7 +640,7 @@ public function toArray()
'domain' => $this->getDomain(),
'secure' => $this->isSecure(),
'httponly' => $this->isHttpOnly(),
'expires' => $this->getExpiry()
'expires' => $this->getExpiresTimestamp()
];
}

Expand All @@ -631,7 +652,7 @@ public function toArray()
*
* @return array
*/
public function toArrayCompat()
public function toArrayClient()
{
return [
'name' => $this->getName(),
Expand All @@ -640,7 +661,28 @@ public function toArrayCompat()
'domain' => $this->getDomain(),
'secure' => $this->isSecure(),
'httponly' => $this->isHttpOnly(),
'expires' => gmdate(static::EXPIRES_FORMAT, $this->expiresAt)
'expires' => $this->getFormattedExpires()
];
}

/**
* Convert the cookie into an array of its properties.
*
* This method is compatible with the historical behavior of Cake\Http\Response,
* where `httponly` is `httpOnly` and `expires` is `expire`
*
* @return array
*/
public function toArrayResponse()
{
return [
'name' => $this->getName(),
'value' => $this->getValue(),
'path' => $this->getPath(),
'domain' => $this->getDomain(),
'secure' => $this->isSecure(),
'httpOnly' => $this->isHttpOnly(),
'expire' => $this->getExpiresTimestamp()
];
}

Expand Down
4 changes: 2 additions & 2 deletions src/Http/Cookie/CookieCollection.php
Expand Up @@ -253,7 +253,7 @@ protected function findMatchingCookies($scheme, $host, $path)
$domain = ltrim($domain, '.');
}

$expires = $cookie->getExpiry();
$expires = $cookie->getExpiresTimestamp();
if ($expires && time() > $expires) {
continue;
}
Expand Down Expand Up @@ -389,7 +389,7 @@ protected function removeExpiredCookies($host, $path)
$hostPattern = '/' . preg_quote($host, '/') . '$/';

foreach ($this->cookies as $i => $cookie) {
$expires = $cookie->getExpiry();
$expires = $cookie->getExpiresTimestamp();
$expired = ($expires > 0 && $expires < $time);

$pathMatches = strpos($path, $cookie->getPath()) === 0;
Expand Down

0 comments on commit b6ad881

Please sign in to comment.