diff --git a/CHANGELOG.md b/CHANGELOG.md index 4255982..9c1ddc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ ## 1.1.1 under development -- no changes in this release. +- Add #27: Add the parameter `$encodeValue` to the `Cookie` constructor and the `Cookie::withRawValue()` method + that creates a cookie copy with a new value that will not be encoded (vjik) ## 1.1.0 May 05, 2021 diff --git a/README.md b/README.md index e1a901a..e84938a 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,13 @@ $middleware = new \Yiisoft\Cookies\CookieMiddleware( $response = $middleware->process($request, $handler); ``` +Create cookie with raw value that will not be encoded: + +```php +$cookie = (new \Yiisoft\Cookies\Cookie('cookieName')) + ->withRawValue('ebaKUq90PhiHck_MR7st-E1SxhbYWiTsLo82mCTbNuAh7rgflx5LVsYfJJseyQCrODuVcJkTSYhm1WKte-l5lQ==') +``` + See [Yii guide to cookies](https://github.com/yiisoft/docs/blob/master/guide/en/runtime/cookies.md) for more info. ## Testing diff --git a/src/Cookie.php b/src/Cookie.php index b4627c4..0709068 100644 --- a/src/Cookie.php +++ b/src/Cookie.php @@ -70,6 +70,11 @@ final class Cookie */ private string $value; + /** + * @var bool Whether cookie value should be encoded. + */ + private bool $encodeValue; + /** * @var DateTimeInterface|null The maximum lifetime of the cookie. * If unspecified, the cookie becomes a session cookie, which will be removed @@ -127,6 +132,7 @@ final class Cookie * @param bool|null $secure Whether the client should send back the cookie only over HTTPS connection. * @param bool|null $httpOnly Whether the cookie should be accessible only through the HTTP protocol. * @param string|null $sameSite Whether the cookie should be available for cross-site requests. + * @param bool $encodeValue Whether cookie value should be encoded. * * @throws InvalidArgumentException When one or more arguments are not valid. */ @@ -138,14 +144,16 @@ public function __construct( ?string $path = '/', ?bool $secure = true, ?bool $httpOnly = true, - ?string $sameSite = self::SAME_SITE_LAX + ?string $sameSite = self::SAME_SITE_LAX, + bool $encodeValue = true ) { if (!preg_match(self::PATTERN_TOKEN, $name)) { throw new InvalidArgumentException("The cookie name \"$name\" contains invalid characters or is empty."); } $this->name = $name; - $this->setValue($value); + $this->value = $value; + $this->encodeValue = $encodeValue; $this->expires = $expires !== null ? clone $expires : null; $this->domain = $domain; $this->setPath($path); @@ -176,7 +184,21 @@ public function getName(): string public function withValue(string $value): self { $new = clone $this; - $new->setValue($value); + $new->value = $value; + $new->encodeValue = true; + return $new; + } + + /** + * Creates a cookie copy with a new value that will not be encoded. + * + * @param $value string Value of the cookie. + */ + public function withRawValue(string $value): self + { + $new = clone $this; + $new->value = $value; + $new->encodeValue = false; return $new; } @@ -190,11 +212,6 @@ public function getValue(): string return $this->value; } - private function setValue(string $value): void - { - $this->value = $value; - } - /** * Creates a cookie copy with a new time the cookie expires. * @@ -450,7 +467,7 @@ public function addToResponse(ResponseInterface $response): ResponseInterface public function __toString(): string { $cookieParts = [ - $this->name . '=' . urlencode($this->value), + $this->name . '=' . ($this->encodeValue ? urlencode($this->value) : $this->value), ]; if ($this->expires !== null) { diff --git a/tests/CookieTest.php b/tests/CookieTest.php index f6762aa..092553b 100644 --- a/tests/CookieTest.php +++ b/tests/CookieTest.php @@ -292,6 +292,14 @@ public function testGetters(): void $this->assertEquals(Cookie::SAME_SITE_LAX, $cookie->getSameSite()); } + public function testRawValue(): void + { + $cookie = (new Cookie('test'))->withRawValue('Q=='); + + $this->assertSame('Q==', $cookie->getValue()); + $this->assertSame('test=Q==; Path=/; Secure; HttpOnly; SameSite=Lax', (string)$cookie); + } + public function testImmutability(): void { $expires = new DateTime(); @@ -310,6 +318,7 @@ public function testImmutability(): void $this->assertNotSame($original, $original->withSameSite(Cookie::SAME_SITE_LAX)); $this->assertNotSame($original, $original->withSecure(true)); $this->assertNotSame($original, $original->withValue('value')); + $this->assertNotSame($original, $original->withRawValue('value')); $this->assertNotSame($original, $original->expire()); $this->assertNotSame($original, $original->expireWhenBrowserIsClosed()); }