From 4a7fbd12296da0869dfe68f8622d0b89c56ed67c Mon Sep 17 00:00:00 2001 From: Denny Septian Panggabean <97607754+ddevsr@users.noreply.github.com> Date: Tue, 22 Oct 2024 13:23:28 +0700 Subject: [PATCH] fix: `getValidated()` when validation multiple asterisk (#9220) * fix: validation multiple asterisk * docs: changelog * Update user_guide_src/source/changelogs/v4.5.6.rst Co-authored-by: Michal Sniatala * tests: multiple asterisk for fail * fix: PHPStan errors appear --------- Co-authored-by: Michal Sniatala --- system/Validation/DotArrayFilter.php | 69 ++++++++---------- tests/system/Validation/ValidationTest.php | 81 +++++++++++++++++++++ user_guide_src/source/changelogs/v4.5.6.rst | 2 + 3 files changed, 114 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..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; @@ -1826,4 +1827,84 @@ public function testRuleWithAsteriskToMultiDimensionalArray(): void $this->validation->getErrors() ); } + + /** + * @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 { + $this->validation->setRules($rules); + + $this->assertSame($expectedCheck, $this->validation->run($data)); + $this->assertSame($expectedData, $this->validation->getValidated()); + } + + public static function provideMultipleAsterisk(): Generator + { + yield 'success' => [ + [ + 'dates' => [ + 23 => [ + 45 => 'Its Mee!', + ], + ], + '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', + ], + ], + ], + ], + ]; + + yield 'failed' => [ + [ + 'foo' => [ + 'bar' => [ + 'data' => [ + 'name' => '', + 'age' => 29, + 'location' => 'Indonesia', + ], + ], + ], + ], + ['foo.*.*.*' => 'required'], + false, + [], + ]; + } } diff --git a/user_guide_src/source/changelogs/v4.5.6.rst b/user_guide_src/source/changelogs/v4.5.6.rst index e454409fc6a8..b982f54b38f7 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 the `getValidated()` method that did not return valid data when validation rules used multiple asterisks. + See the repo's `CHANGELOG.md `_ for a complete list of bugs fixed.