diff --git a/rector.php b/rector.php index 4c15387198d9..6c24c66e5539 100644 --- a/rector.php +++ b/rector.php @@ -23,9 +23,7 @@ use Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector; use Rector\CodeQuality\Rector\If_\ShortenElseIfRector; use Rector\CodeQuality\Rector\If_\SimplifyIfElseToTernaryRector; -use Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector; use Rector\CodeQuality\Rector\Ternary\TernaryEmptyArrayArrayDimFetchToCoalesceRector; -use Rector\CodeQuality\Rector\Ternary\UnnecessaryTernaryExpressionRector; use Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector; use Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector; use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; @@ -189,14 +187,12 @@ ChangeNestedForeachIfsToEarlyContinueRector::class, ChangeIfElseValueAssignToEarlyReturnRector::class, CombineIfRector::class, - SimplifyIfReturnBoolRector::class, InlineIfToExplicitIfRector::class, PreparedValueToEarlyReturnRector::class, ShortenElseIfRector::class, SimplifyIfElseToTernaryRector::class, UnusedForeachValueToArrayKeysRector::class, ChangeArrayPushToArrayAssignRector::class, - UnnecessaryTernaryExpressionRector::class, RemoveErrorSuppressInTryCatchStmtsRector::class, FuncGetArgsToVariadicParamRector::class, MakeInheritedMethodVisibilitySameAsParentRector::class, @@ -220,4 +216,4 @@ // keep '\\' prefix string on string '\Foo\Bar' StringClassNameToClassConstantRector::SHOULD_KEEP_PRE_SLASH => true, ]) - ->withCodeQualityLevel(9); + ->withCodeQualityLevel(14); diff --git a/system/Config/Services.php b/system/Config/Services.php index e37b1278e9a7..1a8843fa39b4 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -230,7 +230,7 @@ public static function email($config = null, bool $getShared = true) return static::getSharedInstance('email', $config); } - if (empty($config) || ! (is_array($config) || $config instanceof EmailConfig)) { + if (empty($config) || (! is_array($config) && ! $config instanceof EmailConfig)) { $config = config(EmailConfig::class); } diff --git a/system/Images/Handlers/ImageMagickHandler.php b/system/Images/Handlers/ImageMagickHandler.php index fa226ccf2289..e59f91869851 100644 --- a/system/Images/Handlers/ImageMagickHandler.php +++ b/system/Images/Handlers/ImageMagickHandler.php @@ -42,7 +42,7 @@ public function __construct($config = null) { parent::__construct($config); - if (! (extension_loaded('imagick') || class_exists(Imagick::class))) { + if (! extension_loaded('imagick') && ! class_exists(Imagick::class)) { throw ImageException::forMissingExtension('IMAGICK'); // @codeCoverageIgnore } diff --git a/system/Session/Handlers/DatabaseHandler.php b/system/Session/Handlers/DatabaseHandler.php index 2d1d2fbbe97e..d36e9592cec0 100644 --- a/system/Session/Handlers/DatabaseHandler.php +++ b/system/Session/Handlers/DatabaseHandler.php @@ -282,12 +282,9 @@ public function destroy($id): bool #[ReturnTypeWillChange] public function gc($max_lifetime) { - $separator = ' '; - $interval = implode($separator, ['', "{$max_lifetime} second", '']); - return $this->db->table($this->table)->where( 'timestamp <', - "now() - INTERVAL {$interval}", + "now() - INTERVAL {$max_lifetime} second", false )->delete() ? 1 : $this->fail(); } 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 63db994d6cd1..cc252702c25b 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -22,6 +22,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.