From 41048ff22de4c5523e4624b6c4dd3b432a23f77e Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Mon, 19 Oct 2026 13:38:51 +0800 Subject: [PATCH 1/2] fix: widen accepted types in `set_value()`'s `$default` --- system/Helpers/form_helper.php | 19 +++++------ tests/system/Helpers/FormHelperTest.php | 34 +++++++++++++++++++ user_guide_src/source/changelogs/v4.7.6.rst | 4 +++ user_guide_src/source/helpers/form_helper.rst | 4 +-- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index dee1f001365b..b030b2f06009 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -11,6 +11,7 @@ * the LICENSE file that was distributed with this source code. */ +use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\Validation\Exceptions\ValidationException; use Config\App; use Config\Validation; @@ -547,27 +548,25 @@ function form_close(string $extra = ''): string if (! function_exists('set_value')) { /** - * Form Value - * * Grabs a value from the POST array for the specified field so you can * re-populate an input field or textarea * - * @param string $field Field name - * @param list|string $default Default value - * @param bool $htmlEscape Whether to escape HTML special characters or not + * @param string $field Field name + * @param list|scalar|Stringable|null $default Default value + * @param bool $htmlEscape Whether to escape HTML special characters or not * * @return list|string */ function set_value(string $field, $default = '', bool $htmlEscape = true): array|string { + /** @var IncomingRequest $request */ $request = service('request'); - // Try any old input data we may have first - $value = $request->getOldInput($field); - - $value ??= $request->getPost($field) ?? $default; + $value = $request->getOldInput($field) + ?? $request->getPost($field) + ?? (is_array($default) ? $default : (string) $default); - return ($htmlEscape) ? esc($value) : $value; + return $htmlEscape ? esc($value) : $value; } } diff --git a/tests/system/Helpers/FormHelperTest.php b/tests/system/Helpers/FormHelperTest.php index c24d34b04a3e..7ed8c8668123 100644 --- a/tests/system/Helpers/FormHelperTest.php +++ b/tests/system/Helpers/FormHelperTest.php @@ -22,10 +22,12 @@ use Config\Filters; use Config\Services; use PHPUnit\Framework\Attributes\BackupGlobals; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\PreserveGlobalState; use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\Attributes\WithoutErrorHandler; +use Stringable; /** * @internal @@ -867,6 +869,38 @@ public function testSetValue(): void $this->assertSame('baz', set_value('foo', 'baz')); } + #[DataProvider('provideSetValueCastsNonArrayDefaultToString')] + public function testSetValueCastsNonArrayDefaultToString(mixed $default, string $expected): void + { + $this->assertSame($expected, set_value('foo', $default)); + } + + /** + * @return iterable + */ + public static function provideSetValueCastsNonArrayDefaultToString(): iterable + { + $stringable = self::createStub(Stringable::class); + $stringable->method('__toString')->willReturn(''); + + yield 'int' => [1, '1']; + + yield 'float' => [1.5, '1.5']; + + yield 'true' => [true, '1']; + + yield 'false' => [false, '']; + + yield 'null' => [null, '']; + + yield 'stringable' => [$stringable, '<bar>']; + } + + public function testSetValueKeepsArrayDefault(): void + { + $this->assertSame(['a', 'b'], set_value('foo', ['a', 'b'])); + } + public function testSetSelect(): void { $_SESSION['_ci_old_input']['post']['foo'] = 'bar'; diff --git a/user_guide_src/source/changelogs/v4.7.6.rst b/user_guide_src/source/changelogs/v4.7.6.rst index dc63b43dc354..72610d1f7c3d 100644 --- a/user_guide_src/source/changelogs/v4.7.6.rst +++ b/user_guide_src/source/changelogs/v4.7.6.rst @@ -38,6 +38,10 @@ Bugs Fixed is used instead. Values that cannot be converted to a string, such as objects not implementing ``Stringable``, now throw a ``FormatException`` instead of a fatal ``Error``. +- **Helpers:** Fixed a regression in v4.7.5 where ``set_value()`` threw a + ``TypeError`` when the default value was not a string or an array, such as + an integer, a float, ``null``, or a ``Stringable`` object. Such values are + now converted to a string. - **HTTP:** Fixed stale ``$_REQUEST`` values after ``SiteURIFactory`` normalizes GET parameters, so ``Validation::withRequest()`` reads the normalized values while unrelated REQUEST entries are preserved. - **HTTP:** Fixed a bug where ``IncomingRequest::isSecure()`` did not recognize ``https`` in the ``X-Forwarded-Proto`` header from a trusted diff --git a/user_guide_src/source/helpers/form_helper.rst b/user_guide_src/source/helpers/form_helper.rst index 9a2d9f026abb..36d19374a774 100644 --- a/user_guide_src/source/helpers/form_helper.rst +++ b/user_guide_src/source/helpers/form_helper.rst @@ -452,10 +452,10 @@ The following functions are available: .. php:function:: set_value($field[, $default = ''[, $html_escape = true]]) :param string $field: Field name - :param string $default: Default value + :param mixed $default: Default value. An array is returned as is, any other value is converted to a string. :param bool $html_escape: Whether to turn off HTML escaping of the value :returns: Field value - :rtype: string + :rtype: array|string Permits you to set the value of an input or textarea element. You must supply the field name via the first parameter of the function. The From 96573527c1d56e70a399d9a029a41163b48a05d2 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Mon, 19 Oct 2026 13:40:07 +0800 Subject: [PATCH 2/2] docs: reflow changelog --- user_guide_src/source/changelogs/v4.7.6.rst | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.7.6.rst b/user_guide_src/source/changelogs/v4.7.6.rst index 72610d1f7c3d..75e6f890047c 100644 --- a/user_guide_src/source/changelogs/v4.7.6.rst +++ b/user_guide_src/source/changelogs/v4.7.6.rst @@ -33,23 +33,11 @@ Bugs Fixed ********** - **Commands:** Fixed a bug where generator commands did not sort the imports of a template with CRLF line endings. -- **Format:** Fixed ``XMLFormatter`` so that an array key that becomes empty after - sanitization no longer triggers a ``ValueError``; the fallback tag name ``item`` - is used instead. Values that cannot be converted to a string, such as objects - not implementing ``Stringable``, now throw a ``FormatException`` instead of a - fatal ``Error``. -- **Helpers:** Fixed a regression in v4.7.5 where ``set_value()`` threw a - ``TypeError`` when the default value was not a string or an array, such as - an integer, a float, ``null``, or a ``Stringable`` object. Such values are - now converted to a string. +- **Format:** Fixed ``XMLFormatter`` so that an array key that becomes empty after sanitization no longer triggers a ``ValueError``; the fallback tag name ``item`` is used instead. Values that cannot be converted to a string, such as objects not implementing ``Stringable``, now throw a ``FormatException`` instead of a fatal ``Error``. +- **Helpers:** Fixed a regression in v4.7.5 where ``set_value()`` threw a ``TypeError`` when the default value was not a string or an array, such as an integer, a float, ``null``, or a ``Stringable`` object. Such values are now converted to a string. - **HTTP:** Fixed stale ``$_REQUEST`` values after ``SiteURIFactory`` normalizes GET parameters, so ``Validation::withRequest()`` reads the normalized values while unrelated REQUEST entries are preserved. -- **HTTP:** Fixed a bug where ``IncomingRequest::isSecure()`` did not - recognize ``https`` in the ``X-Forwarded-Proto`` header from a trusted - proxy when the value contained uppercase letters or surrounding - whitespace, or was represented as a single-element array. -- **Security:** Fixed a bug where a valid JSON request body that is not an object - (for example a JSON array) was corrupted after CSRF verification when the - token was sent via the ``X-CSRF-TOKEN`` header. +- **HTTP:** Fixed a bug where ``IncomingRequest::isSecure()`` did not recognize ``https`` in the ``X-Forwarded-Proto`` header from a trusted proxy when the value contained uppercase letters or surrounding whitespace, or was represented as a single-element array. +- **Security:** Fixed a bug where a valid JSON request body that is not an object (for example a JSON array) was corrupted after CSRF verification when the token was sent via the ``X-CSRF-TOKEN`` header. See the repo's `CHANGELOG.md `_