From b6e99fa73072c04e5dc7e5bd8083ce4f547e5d0d Mon Sep 17 00:00:00 2001 From: Denny Septian Panggabean Date: Fri, 11 Oct 2024 10:05:19 +0700 Subject: [PATCH 1/5] fix: validation multiple asterisk --- system/Validation/DotArrayFilter.php | 69 ++++++++++------------ tests/system/Validation/ValidationTest.php | 46 +++++++++++++++ 2 files changed, 77 insertions(+), 38 deletions(-) diff --git a/system/Validation/DotArrayFilter.php b/system/Validation/DotArrayFilter.php index 4d2e1cb19ed2..c7a401e1589a 100644 --- a/system/Validation/DotArrayFilter.php +++ b/system/Validation/DotArrayFilter.php @@ -21,8 +21,6 @@ final class DotArrayFilter /** * Creates a new array with only the elements specified in dot array syntax. * - * This code comes from the dot_array_search() function. - * * @param array $indexes The dot array syntax pattern to use for filtering. * @param array $array The array to filter. * @@ -33,20 +31,14 @@ public static function run(array $indexes, array $array): array $result = []; foreach ($indexes as $index) { - // See https://regex101.com/r/44Ipql/1 - $segments = preg_split( - '/(? str_replace('\.', '.', $key), - $segments - ); - - $result = array_replace_recursive($result, self::filter($segments, $array)); + $segments = preg_split('/(? str_replace('\.', '.', $key), $segments); + + $filteredArray = self::filter($segments, $array); + + if ($filteredArray !== []) { + $result = array_replace_recursive($result, $filteredArray); + } } return $result; @@ -62,53 +54,54 @@ public static function run(array $indexes, array $array): array */ private static function filter(array $indexes, array $array): array { - // If index is empty, returns empty array. + // If there are no indexes left, return an empty array if ($indexes === []) { return []; } - // Grab the current index. + // Get the current index $currentIndex = array_shift($indexes); + // If the current index doesn't exist and is not a wildcard, return an empty array if (! isset($array[$currentIndex]) && $currentIndex !== '*') { return []; } - // Handle Wildcard (*) + // Handle the wildcard '*' at the current level if ($currentIndex === '*') { - $answer = []; + $result = []; + // Iterate over all keys at this level foreach ($array as $key => $value) { - if (! is_array($value)) { - continue; - } - - $result = self::filter($indexes, $value); - - if ($result !== []) { - $answer[$key] = $result; + if ($indexes === []) { + // If no indexes are left, capture the entire value + $result[$key] = $value; + } elseif (is_array($value)) { + // If there are still indexes left, continue filtering recursively + $filtered = self::filter($indexes, $value); + if ($filtered !== []) { + $result[$key] = $filtered; + } } } - return $answer; + return $result; } - // If this is the last index, make sure to return it now, - // and not try to recurse through things. + // If this is the last index, return the value if ($indexes === []) { - return [$currentIndex => $array[$currentIndex]]; + return [$currentIndex => $array[$currentIndex] ?? []]; } - // Do we need to recursively filter this value? - if (is_array($array[$currentIndex]) && $array[$currentIndex] !== []) { - $result = self::filter($indexes, $array[$currentIndex]); + // If the current value is an array, recursively filter it + if (is_array($array[$currentIndex])) { + $filtered = self::filter($indexes, $array[$currentIndex]); - if ($result !== []) { - return [$currentIndex => $result]; + if ($filtered !== []) { + return [$currentIndex => $filtered]; } } - // Otherwise, not found. return []; } } diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index 13991dc45916..0266e1c445d2 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -1826,4 +1826,50 @@ public function testRuleWithAsteriskToMultiDimensionalArray(): void $this->validation->getErrors() ); } + + public function testRuleWithMultipleAsterisk(): void + { + $data = [ + 'id' => 1, + 'dates' => [ + 23 => [ + 45 => 'Its Mee!', + ], + ], + 'foo' => [ + 'bar' => [ + 'data' => [ + 'name' => 'John Doe', + 'age' => 29, + 'location' => 'Indonesia', + ], + ], + ], + ]; + + $this->validation->setRules([ + 'id' => 'required|numeric', + 'dates.*.*' => 'required', + 'foo.*.*.*' => 'required', + ]); + + $this->assertTrue($this->validation->run($data)); + $this->assertSame([ + 'id' => 1, + 'dates' => [ + 23 => [ + 45 => 'Its Mee!', + ], + ], + 'foo' => [ + 'bar' => [ + 'data' => [ + 'name' => 'John Doe', + 'age' => 29, + 'location' => 'Indonesia', + ], + ], + ], + ], $this->validation->getValidated()); + } } From 95c495581293bd1c1fe34b95c80bbb21c0b40aa9 Mon Sep 17 00:00:00 2001 From: Denny Septian Panggabean Date: Fri, 11 Oct 2024 10:14:54 +0700 Subject: [PATCH 2/5] docs: changelog --- user_guide_src/source/changelogs/v4.5.6.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/user_guide_src/source/changelogs/v4.5.6.rst b/user_guide_src/source/changelogs/v4.5.6.rst index e454409fc6a8..1e25bbb0a174 100644 --- a/user_guide_src/source/changelogs/v4.5.6.rst +++ b/user_guide_src/source/changelogs/v4.5.6.rst @@ -31,6 +31,8 @@ Bugs Fixed ********** - **Session Library:** The session initialization debug message now uses the correct log type "debug" instead of "info". +- **Validation:** Fixed `getValidated()` when validation multiple asterisk. + See the repo's `CHANGELOG.md `_ for a complete list of bugs fixed. From 712898dc56870139fae6cd8f7945ae90eb712119 Mon Sep 17 00:00:00 2001 From: Denny Septian Panggabean <97607754+ddevsr@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:45:57 +0700 Subject: [PATCH 3/5] Update user_guide_src/source/changelogs/v4.5.6.rst Co-authored-by: Michal Sniatala --- user_guide_src/source/changelogs/v4.5.6.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.5.6.rst b/user_guide_src/source/changelogs/v4.5.6.rst index 1e25bbb0a174..b982f54b38f7 100644 --- a/user_guide_src/source/changelogs/v4.5.6.rst +++ b/user_guide_src/source/changelogs/v4.5.6.rst @@ -31,7 +31,7 @@ Bugs Fixed ********** - **Session Library:** The session initialization debug message now uses the correct log type "debug" instead of "info". -- **Validation:** Fixed `getValidated()` when validation multiple asterisk. +- **Validation:** Fixed the `getValidated()` method that did not return valid data when validation rules used multiple asterisks. See the repo's `CHANGELOG.md `_ From 64a1beb08f182b56c7f94732c020a1f938f06fec Mon Sep 17 00:00:00 2001 From: Denny Septian Panggabean Date: Fri, 11 Oct 2024 14:10:40 +0700 Subject: [PATCH 4/5] tests: multiple asterisk for fail --- tests/system/Validation/ValidationTest.php | 92 ++++++++++++++-------- 1 file changed, 59 insertions(+), 33 deletions(-) diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index 0266e1c445d2..968864c8f4bb 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -1827,49 +1827,75 @@ public function testRuleWithAsteriskToMultiDimensionalArray(): void ); } - public function testRuleWithMultipleAsterisk(): void + /** + * @see https://github.com/codeigniter4/CodeIgniter4/issues/9219 + */ + #[DataProvider('provideMultipleAsterisk')] + public function testRuleWithMultipleAsterisk(array $data = [], array $rules = [], bool $expectedCheck = false, array $expectedData = []): void { - $data = [ - 'id' => 1, - 'dates' => [ - 23 => [ - 45 => 'Its Mee!', + $this->validation->setRules($rules); + + $this->assertSame($expectedCheck, $this->validation->run($data)); + $this->assertSame($expectedData, $this->validation->getValidated()); + } + + public static function provideMultipleAsterisk(): iterable + { + yield 'success' => [ + [ + 'dates' => [ + 23 => [ + 45 => 'Its Mee!', + ], + ], + 'foo' => [ + 'bar' => [ + 'data' => [ + 'name' => 'John Doe', + 'age' => 29, + 'location' => 'Indonesia', + ], + ], ], ], - 'foo' => [ - 'bar' => [ - 'data' => [ - 'name' => 'John Doe', - 'age' => 29, - 'location' => 'Indonesia', + [ + 'dates.*.*' => 'required', + 'foo.*.*.*' => 'required', + ], + true, + [ + 'dates' => [ + 23 => [ + 45 => 'Its Mee!', + ], + ], + 'foo' => [ + 'bar' => [ + 'data' => [ + 'name' => 'John Doe', + 'age' => 29, + 'location' => 'Indonesia', + ], ], ], ], ]; - $this->validation->setRules([ - 'id' => 'required|numeric', - 'dates.*.*' => 'required', - 'foo.*.*.*' => 'required', - ]); - - $this->assertTrue($this->validation->run($data)); - $this->assertSame([ - 'id' => 1, - 'dates' => [ - 23 => [ - 45 => 'Its Mee!', - ], - ], - 'foo' => [ - 'bar' => [ - 'data' => [ - 'name' => 'John Doe', - 'age' => 29, - 'location' => 'Indonesia', + yield 'failed' => [ + [ + 'foo' => [ + 'bar' => [ + 'data' => [ + 'name' => '', + 'age' => 29, + 'location' => 'Indonesia', + ], ], ], ], - ], $this->validation->getValidated()); + ['foo.*.*.*' => 'required'], + false, + [], + ]; } } From cbbbb75cb13c339ac362b108080fd9db78750e9a Mon Sep 17 00:00:00 2001 From: Denny Septian Panggabean Date: Fri, 11 Oct 2024 14:50:41 +0700 Subject: [PATCH 5/5] fix: PHPStan errors appear --- tests/system/Validation/ValidationTest.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index 968864c8f4bb..28003dbb6477 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -21,6 +21,7 @@ use CodeIgniter\Validation\Exceptions\ValidationException; use Config\App; use Config\Services; +use Generator; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\ExpectationFailedException; @@ -1828,18 +1829,26 @@ public function testRuleWithAsteriskToMultiDimensionalArray(): void } /** + * @param array $data + * @param array $rules + * @param array $expectedData + * * @see https://github.com/codeigniter4/CodeIgniter4/issues/9219 */ #[DataProvider('provideMultipleAsterisk')] - public function testRuleWithMultipleAsterisk(array $data = [], array $rules = [], bool $expectedCheck = false, array $expectedData = []): void - { + public function testRuleWithMultipleAsterisk( + array $data = [], + array $rules = [], + bool $expectedCheck = false, + array $expectedData = [] + ): void { $this->validation->setRules($rules); $this->assertSame($expectedCheck, $this->validation->run($data)); $this->assertSame($expectedData, $this->validation->getValidated()); } - public static function provideMultipleAsterisk(): iterable + public static function provideMultipleAsterisk(): Generator { yield 'success' => [ [