From c721ebbd4e02fe725820fb355093fc6f686e929d Mon Sep 17 00:00:00 2001 From: banghamid <62824084+banghamid@users.noreply.github.com> Date: Tue, 10 Nov 2026 23:44:08 +0700 Subject: [PATCH 1/2] fix(Helpers): handle zero key and non-array intermediate values in ArrayHelper::dotKeyExists() --- system/Helpers/Array/ArrayHelper.php | 10 +++++++--- .../Helpers/Array/ArrayHelperDotKeyExistsTest.php | 7 +++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/system/Helpers/Array/ArrayHelper.php b/system/Helpers/Array/ArrayHelper.php index b48efbfc4802..d3a34d4448dd 100644 --- a/system/Helpers/Array/ArrayHelper.php +++ b/system/Helpers/Array/ArrayHelper.php @@ -146,12 +146,16 @@ public static function dotKeyExists(string $index, array $array): bool $currentArray = $array; // Grab the current index - while ($currentIndex = array_shift($indexes)) { + while (($currentIndex = array_shift($indexes)) !== null) { if ($currentIndex === '*') { $currentIndex = array_shift($indexes); + if (! is_array($currentArray)) { + return false; + } + foreach ($currentArray as $item) { - if (! array_key_exists($currentIndex, $item)) { + if (! is_array($item) || ! array_key_exists($currentIndex, $item)) { return false; } } @@ -166,7 +170,7 @@ public static function dotKeyExists(string $index, array $array): bool continue; } - if (! array_key_exists($currentIndex, $currentArray)) { + if (! is_array($currentArray) || ! array_key_exists($currentIndex, $currentArray)) { return false; } diff --git a/tests/system/Helpers/Array/ArrayHelperDotKeyExistsTest.php b/tests/system/Helpers/Array/ArrayHelperDotKeyExistsTest.php index 432fafcf1a0c..4abd6f864cea 100644 --- a/tests/system/Helpers/Array/ArrayHelperDotKeyExistsTest.php +++ b/tests/system/Helpers/Array/ArrayHelperDotKeyExistsTest.php @@ -43,7 +43,11 @@ public function testDotKeyExists(): void $this->assertTrue(ArrayHelper::dotKeyExists('contacts.friends', $this->array)); $this->assertFalse(ArrayHelper::dotKeyExists('not.friends', $this->array)); $this->assertTrue(ArrayHelper::dotKeyExists('contacts.friends.0.name', $this->array)); + $this->assertFalse(ArrayHelper::dotKeyExists('contacts.friends.0.nonexistent', $this->array)); $this->assertFalse(ArrayHelper::dotKeyExists('contacts.friends.1.name', $this->array)); + $this->assertFalse(ArrayHelper::dotKeyExists('0', [])); + $this->assertFalse(ArrayHelper::dotKeyExists('contacts.friends.0.name.sub', $this->array)); + $this->assertFalse(ArrayHelper::dotKeyExists('contacts.friends.1.age.sub', $this->array)); } public function testDotKeyExistsWithEndingWildCard(): void @@ -72,6 +76,9 @@ public function testDotKeyExistsWithWildCard(): void $this->assertTrue(ArrayHelper::dotKeyExists('contacts.*.0.age', $this->array)); $this->assertTrue(ArrayHelper::dotKeyExists('contacts.*.1.age', $this->array)); $this->assertTrue(ArrayHelper::dotKeyExists('contacts.*.0.name', $this->array)); + $this->assertFalse(ArrayHelper::dotKeyExists('contacts.*.0.nonexistent', $this->array)); $this->assertFalse(ArrayHelper::dotKeyExists('contacts.*.1.name', $this->array)); + $this->assertFalse(ArrayHelper::dotKeyExists('contacts.friends.*.age.sub', $this->array)); + $this->assertFalse(ArrayHelper::dotKeyExists('contacts.friends.1.age.*.sub', $this->array)); } } From d250d4f4bdf3da6703e4f21d1d157f2f20dee76e Mon Sep 17 00:00:00 2001 From: banghamid <62824084+banghamid@users.noreply.github.com> Date: Thu, 12 Nov 2026 00:43:12 +0700 Subject: [PATCH 2/2] test(Helpers): cover non-array wildcard items, zero keys, and add changelog --- docs/changelogs/v4.7.7.md | 1 + tests/system/Helpers/Array/ArrayHelperDotKeyExistsTest.php | 3 +++ 2 files changed, 4 insertions(+) diff --git a/docs/changelogs/v4.7.7.md b/docs/changelogs/v4.7.7.md index 4472c7de75ef..dc3483c1c292 100644 --- a/docs/changelogs/v4.7.7.md +++ b/docs/changelogs/v4.7.7.md @@ -16,6 +16,7 @@ Release Date: Unreleased ## Bugs Fixed +- **Helpers:** Fixed a bug in `ArrayHelper::dotKeyExists()` where `'0'` path segments caused early loop termination and returned `true`, and non-array intermediate values threw a `TypeError`. - **HTTP:** Fixed a bug where `UploadedFile::move()` and `UploadedFile::store()` on Windows could store a file under a different name than `getName()` reported, or write its contents to an NTFS alternate data stream. Filenames ending in a period or space or containing a colon are now rejected, including explicitly supplied names. - **Security:** Fixed a bug where `Security::__construct()` threw a `TypeError` when the CSRF cookie was an array, causing an HTTP 500 response for affected requests. - **Worker Mode:** Fixed a bug where every request that ended with an uncaught exception, including a `404 Page Not Found` without a 404 override, called `exit()` in the exception handler. This ended the worker script, so FrankenPHP booted a new one, and the worker loop skipped its cleanup after the request. The exception handler now returns to the worker loop. diff --git a/tests/system/Helpers/Array/ArrayHelperDotKeyExistsTest.php b/tests/system/Helpers/Array/ArrayHelperDotKeyExistsTest.php index 4abd6f864cea..218683fe8a8c 100644 --- a/tests/system/Helpers/Array/ArrayHelperDotKeyExistsTest.php +++ b/tests/system/Helpers/Array/ArrayHelperDotKeyExistsTest.php @@ -46,6 +46,8 @@ public function testDotKeyExists(): void $this->assertFalse(ArrayHelper::dotKeyExists('contacts.friends.0.nonexistent', $this->array)); $this->assertFalse(ArrayHelper::dotKeyExists('contacts.friends.1.name', $this->array)); $this->assertFalse(ArrayHelper::dotKeyExists('0', [])); + $this->assertTrue(ArrayHelper::dotKeyExists('0', ['0' => 'value'])); + $this->assertTrue(ArrayHelper::dotKeyExists('0', ['0' => null])); $this->assertFalse(ArrayHelper::dotKeyExists('contacts.friends.0.name.sub', $this->array)); $this->assertFalse(ArrayHelper::dotKeyExists('contacts.friends.1.age.sub', $this->array)); } @@ -80,5 +82,6 @@ public function testDotKeyExistsWithWildCard(): void $this->assertFalse(ArrayHelper::dotKeyExists('contacts.*.1.name', $this->array)); $this->assertFalse(ArrayHelper::dotKeyExists('contacts.friends.*.age.sub', $this->array)); $this->assertFalse(ArrayHelper::dotKeyExists('contacts.friends.1.age.*.sub', $this->array)); + $this->assertFalse(ArrayHelper::dotKeyExists('*.name', [['name' => 'Fred'], 'not-an-array'])); } }