Skip to content

Commit 5a0f1e3

Browse files
authored
Merge pull request #6544 from kenjis/fix-set-cookie
fix: set_cookie() does not use Config\Cookie values
2 parents 3edba10 + 2955253 commit 5a0f1e3

File tree

8 files changed

+122
-16
lines changed

8 files changed

+122
-16
lines changed

system/HTTP/ResponseTrait.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use CodeIgniter\HTTP\Exceptions\HTTPException;
1818
use CodeIgniter\Pager\PagerInterface;
1919
use CodeIgniter\Security\Exceptions\SecurityException;
20+
use Config\Cookie as CookieConfig;
2021
use Config\Services;
2122
use DateTime;
2223
use DateTimeZone;
@@ -544,8 +545,8 @@ public function redirect(string $uri, string $method = 'auto', ?int $code = null
544545
* @param string $domain Cookie domain (e.g.: '.yourdomain.com')
545546
* @param string $path Cookie path (default: '/')
546547
* @param string $prefix Cookie name prefix ('': the default prefix)
547-
* @param bool $secure Whether to only transfer cookies via SSL
548-
* @param bool $httponly Whether only make the cookie accessible via HTTP (no javascript)
548+
* @param bool|null $secure Whether to only transfer cookies via SSL
549+
* @param bool|null $httponly Whether only make the cookie accessible via HTTP (no javascript)
549550
* @param string|null $samesite
550551
*
551552
* @return $this
@@ -557,8 +558,8 @@ public function setCookie(
557558
$domain = '',
558559
$path = '/',
559560
$prefix = '',
560-
$secure = false,
561-
$httponly = false,
561+
$secure = null,
562+
$httponly = null,
562563
$samesite = null
563564
) {
564565
if ($name instanceof Cookie) {
@@ -567,8 +568,17 @@ public function setCookie(
567568
return $this;
568569
}
569570

571+
/** @var CookieConfig|null $cookieConfig */
572+
$cookieConfig = config('Cookie');
573+
574+
if ($cookieConfig instanceof CookieConfig) {
575+
$secure ??= $cookieConfig->secure;
576+
$httponly ??= $cookieConfig->httponly;
577+
$samesite ??= $cookieConfig->samesite;
578+
}
579+
570580
if (is_array($name)) {
571-
// always leave 'name' in last place, as the loop will break otherwise, due to $$item
581+
// always leave 'name' in last place, as the loop will break otherwise, due to ${$item}
572582
foreach (['samesite', 'value', 'expire', 'domain', 'path', 'prefix', 'secure', 'httponly', 'name'] as $item) {
573583
if (isset($name[$item])) {
574584
${$item} = $name[$item];

system/Helpers/cookie_helper.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
* @param string $domain For site-wide cookie. Usually: .yourdomain.com
3131
* @param string $path The cookie path
3232
* @param string $prefix The cookie prefix ('': the default prefix)
33-
* @param bool $secure True makes the cookie secure
34-
* @param bool $httpOnly True makes the cookie accessible via http(s) only (no javascript)
33+
* @param bool|null $secure True makes the cookie secure
34+
* @param bool|null $httpOnly True makes the cookie accessible via http(s) only (no javascript)
3535
* @param string|null $sameSite The cookie SameSite value
3636
*
3737
* @see \CodeIgniter\HTTP\Response::setCookie()
@@ -43,8 +43,8 @@ function set_cookie(
4343
string $domain = '',
4444
string $path = '/',
4545
string $prefix = '',
46-
bool $secure = false,
47-
bool $httpOnly = false,
46+
?bool $secure = null,
47+
?bool $httpOnly = null,
4848
?string $sameSite = null
4949
) {
5050
$response = Services::response();

tests/system/HTTP/ResponseCookieTest.php

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace CodeIgniter\HTTP;
1313

14+
use CodeIgniter\Config\Factories;
1415
use CodeIgniter\Cookie\Cookie;
1516
use CodeIgniter\Cookie\CookieStore;
1617
use CodeIgniter\Cookie\Exceptions\CookieException;
@@ -135,11 +136,11 @@ public function testCookieHTTPOnly()
135136

136137
$response->setCookie('foo', 'bar');
137138
$cookie = $response->getCookie('foo');
138-
$this->assertFalse($cookie->isHTTPOnly());
139+
$this->assertTrue($cookie->isHTTPOnly());
139140

140-
$response->setCookie(['name' => 'bee', 'value' => 'bop', 'httponly' => true]);
141+
$response->setCookie(['name' => 'bee', 'value' => 'bop', 'httponly' => false]);
141142
$cookie = $response->getCookie('bee');
142-
$this->assertTrue($cookie->isHTTPOnly());
143+
$this->assertFalse($cookie->isHTTPOnly());
143144
}
144145

145146
public function testCookieExpiry()
@@ -255,6 +256,7 @@ public function testCookieStrictSetSameSite()
255256

256257
public function testCookieBlankSetSameSite()
257258
{
259+
/** @var CookieConfig $config */
258260
$config = config('Cookie');
259261
$config->samesite = '';
260262
$response = new Response(new App());
@@ -314,6 +316,30 @@ public function testCookieInvalidSameSite()
314316
]);
315317
}
316318

319+
public function testSetCookieConfigCookieIsUsed()
320+
{
321+
/** @var CookieConfig $config */
322+
$config = config('Cookie');
323+
$config->secure = true;
324+
$config->httponly = true;
325+
$config->samesite = 'None';
326+
Factories::injectMock('config', 'Cookie', $config);
327+
328+
$cookieAttr = [
329+
'name' => 'bar',
330+
'value' => 'foo',
331+
'expire' => 9999,
332+
];
333+
$response = new Response(new App());
334+
$response->setCookie($cookieAttr);
335+
336+
$cookie = $response->getCookie('bar');
337+
$options = $cookie->getOptions();
338+
$this->assertTrue($options['secure']);
339+
$this->assertTrue($options['httponly']);
340+
$this->assertSame('None', $options['samesite']);
341+
}
342+
317343
public function testGetCookieStore()
318344
{
319345
$response = new Response(new App());

tests/system/Helpers/CookieHelperTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use CodeIgniter\Test\CIUnitTestCase;
2121
use CodeIgniter\Test\Mock\MockResponse;
2222
use Config\App;
23+
use Config\Cookie;
2324
use Config\Cookie as CookieConfig;
2425
use Config\Services;
2526

@@ -89,6 +90,31 @@ public function testSetCookieByArrayParameters()
8990
delete_cookie($this->name);
9091
}
9192

93+
public function testSetCookieConfigCookieIsUsed()
94+
{
95+
/** @var Cookie $config */
96+
$config = config('Cookie');
97+
$config->secure = true;
98+
$config->httponly = true;
99+
$config->samesite = 'None';
100+
Factories::injectMock('config', 'Cookie', $config);
101+
102+
$cookieAttr = [
103+
'name' => $this->name,
104+
'value' => $this->value,
105+
'expire' => $this->expire,
106+
];
107+
set_cookie($cookieAttr);
108+
109+
$cookie = $this->response->getCookie($this->name);
110+
$options = $cookie->getOptions();
111+
$this->assertTrue($options['secure']);
112+
$this->assertTrue($options['httponly']);
113+
$this->assertSame('None', $options['samesite']);
114+
115+
delete_cookie($this->name);
116+
}
117+
92118
public function testSetCookieSecured()
93119
{
94120
$pre = 'Hello, I try to';

user_guide_src/source/changelogs/v4.2.7.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Release Date: Unreleased
1212
BREAKING
1313
********
1414

15+
- The default values of the parameters in :php:func:`set_cookie()` and :php:meth:`CodeIgniter\\HTTP\\Response::setCookie()` has been fixed. Now the default values of ``$secure`` and ``$httponly`` are ``null``, and these values will be replaced with the ``Config\Cookie`` values.
1516
- ``Time::__toString()`` is now locale-independent. It returns database-compatible strings like '2022-09-07 12:00:00' in any locale.
1617

1718
Enhancements

user_guide_src/source/helpers/cookie_helper.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@ The following functions are available:
2929
:param string $domain: Cookie domain (usually: .yourdomain.com)
3030
:param string $path: Cookie path
3131
:param string $prefix: Cookie name prefix. If ``''``, the default from **app/Config/Cookie.php** is used
32-
:param bool $secure: Whether to only send the cookie through HTTPS
33-
:param bool $httpOnly: Whether to hide the cookie from JavaScript
32+
:param bool $secure: Whether to only send the cookie through HTTPS. If ``null``, the default from **app/Config/Cookie.php** is used
33+
:param bool $httpOnly: Whether to hide the cookie from JavaScript. If ``null``, the default from **app/Config/Cookie.php** is used
3434
:param string $sameSite: The value for the SameSite cookie parameter. If ``null``, the default from **app/Config/Cookie.php** is used
3535
:rtype: void
3636

37+
.. note:: Prior to v4.2.7, the default values of ``$secure`` and ``$httpOnly`` were ``false``
38+
due to a bug, and these values from **app/Config/Cookie.php** were never used.
39+
3740
This helper function gives you friendlier syntax to set browser
3841
cookies. Refer to the :doc:`Response Library </outgoing/response>` for
3942
a description of its use, as this function is an alias for

user_guide_src/source/installation/upgrade_427.rst

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,43 @@ Please refer to the upgrade instructions corresponding to your installation meth
1515
Breaking Changes
1616
****************
1717

18+
set_cookie()
19+
============
20+
21+
Due to a bug, :php:func:`set_cookie()` and :php:meth:`CodeIgniter\\HTTP\\Response::setCookie()`
22+
in the previous versions did not use the ``$secure`` and ``$httponly`` values in ``Config\Cookie``.
23+
The following code did not issue a cookie with the secure flag even if you set ``$secure = true``
24+
in ``Config\Cookie``::
25+
26+
helper('cookie');
27+
28+
$cookie = [
29+
'name' => $name,
30+
'value' => $value,
31+
];
32+
set_cookie($cookie);
33+
// or
34+
$this->response->setCookie($cookie);
35+
36+
But now the values in ``Config\Cookie`` are used for the options that are not specified.
37+
The above code issues a cookie with the secure flag if you set ``$secure = true``
38+
in ``Config\Cookie``.
39+
40+
If your code depends on this bug, please change it to explicitly specify the necessary options::
41+
42+
$cookie = [
43+
'name' => $name,
44+
'value' => $value,
45+
'secure' => false, // Set explicitly
46+
'httponly' => false, // Set explicitly
47+
];
48+
set_cookie($cookie);
49+
// or
50+
$this->response->setCookie($cookie);
51+
52+
Others
53+
======
54+
1855
- ``Time::__toString()`` is now locale-independent. It returns database-compatible strings like '2022-09-07 12:00:00' in any locale. Most locales are not affected by this change. But in a few locales like `ar`, `fa`, ``Time::__toString()`` (or ``(string) $time`` or implicit casting to a string) no longer returns a localized datetime string. if you want to get a localized datetime string, use :ref:`Time::toDateTimeString() <time-todatetimestring>` instead.
1956

2057
Project Files

user_guide_src/source/outgoing/response.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,11 +370,14 @@ The methods provided by the parent class that are available are:
370370
:param string $domain: Cookie domain
371371
:param string $path: Cookie path
372372
:param string $prefix: Cookie name prefix. If set to ``''``, the default value from **app/Config/Cookie.php** will be used
373-
:param bool $secure: Whether to only transfer the cookie through HTTPS
374-
:param bool $httponly: Whether to only make the cookie accessible for HTTP requests (no JavaScript)
373+
:param bool $secure: Whether to only transfer the cookie through HTTPS. If set to ``null``, the default value from **app/Config/Cookie.php** will be used
374+
:param bool $httponly: Whether to only make the cookie accessible for HTTP requests (no JavaScript). If set to ``null``, the default value from **app/Config/Cookie.php** will be used
375375
:param string $samesite: The value for the SameSite cookie parameter. If set to ``''``, no SameSite attribute will be set on the cookie. If set to ``null``, the default value from **app/Config/Cookie.php** will be used
376376
:rtype: void
377377

378+
.. note:: Prior to v4.2.7, the default values of ``$secure`` and ``$httponly`` were ``false``
379+
due to a bug, and these values from **app/Config/Cookie.php** were never used.
380+
378381
Sets a cookie containing the values you specify. There are two ways to
379382
pass information to this method so that a cookie can be set: Array
380383
Method, and Discrete Parameters:

0 commit comments

Comments
 (0)