From efdf83d969317165b04758bd29ceaa6a2bb460aa Mon Sep 17 00:00:00 2001 From: Bogdan Date: Tue, 9 Jun 2026 23:37:08 +0200 Subject: [PATCH 1/2] fix: required_without() premature return inside foreach loop Bug: return statements inside the foreach loop caused only the first field in the otherFields list to be checked. All subsequent fields were silently ignored. - Refactored: foreach loop no longer contains early returns; each iteration independently determines whether to return false. - Added: null coalescing for field key access - Added: explicit is_array() check before array access to prevent warnings when dot_array_search() returns null - Added: validation for missing field key on dot-path fields --- system/Validation/Rules.php | 48 ++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/system/Validation/Rules.php b/system/Validation/Rules.php index e3e256b099b4..fe7b3bdb5f8d 100644 --- a/system/Validation/Rules.php +++ b/system/Validation/Rules.php @@ -414,30 +414,44 @@ public function required_without( // Still here? Then we fail this test if // any of the fields are not present in $data foreach (explode(',', $otherFields) as $otherField) { - if ( - (! str_contains($otherField, '.')) - && (! array_key_exists($otherField, $data) - || empty($data[$otherField])) - ) { - return false; + if (! str_contains($otherField, '.')) { + if (! array_key_exists($otherField, $data) || empty($data[$otherField])) { + return false; + } + + continue; } - if (str_contains($otherField, '.')) { - if ($field === null) { - throw new InvalidArgumentException('You must supply the parameters: field.'); - } + if ($field === null) { + throw new InvalidArgumentException('You must supply the parameters: field.'); + } - $fieldData = dot_array_search($otherField, $data); - $fieldSplitArray = explode('.', $field); - $fieldKey = $fieldSplitArray[1]; + $fieldSplitArray = explode('.', $field); + $fieldKey = $fieldSplitArray[1] ?? null; - if (is_array($fieldData)) { - return ! empty(dot_array_search($otherField, $data)[$fieldKey]); + if ($fieldKey === null) { + throw new InvalidArgumentException('Invalid field format for dot-path required_without.'); + } + + $fieldData = dot_array_search($otherField, $data); + + if (is_array($fieldData)) { + $searched = dot_array_search($otherField, $data); + + if (! is_array($searched) || ! array_key_exists($fieldKey, $searched)) { + return false; } + + if (empty($searched[$fieldKey])) { + return false; + } + } else { $nowField = str_replace('*', $fieldKey, $otherField); - $nowFieldVaule = dot_array_search($nowField, $data); + $nowFieldValue = dot_array_search($nowField, $data); - return null !== $nowFieldVaule; + if ($nowFieldValue === null) { + return false; + } } } From 2cd2f6945e3d93edaa9589363863bd058a44be1c Mon Sep 17 00:00:00 2001 From: Bogdan Date: Wed, 10 Jun 2026 00:03:04 +0200 Subject: [PATCH 2/2] test: add unit tests for required_without with multiple dot-path fields --- tests/system/Validation/ValidationTest.php | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index 7e0f4411f079..c442e238d4cd 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -1854,6 +1854,46 @@ public function testRequireWithoutWithAsterisk(): void ); } + public function testRequireWithoutWithMultipleAsterisk(): void + { + $data = [ + 'a' => [ + ['b' => 1, 'c' => 2, 'd' => 3], + ['c' => '', 'd' => 4], + ['b' => 5], + ], + ]; + + $this->validation->setRules([ + 'a.*.c' => 'required_without[a.*.b, a.*.d]', + ])->run($data); + + $this->assertSame( + 'The a.*.c field is required when a.*.b, a.*.d is not present.', + $this->validation->getError('a.1.c'), + ); + $this->assertArrayNotHasKey('a.0.c', $this->validation->getErrors(), 'Row 0: both b and d present'); + $this->assertArrayHasKey('a.2.c', $this->validation->getErrors(), 'Row 2: c missing, d missing → field required'); + } + + public function testRequireWithoutWithMultipleAsteriskLastMissing(): void + { + $data = [ + 'a' => [ + ['b' => 1, 'c' => ''], + ], + ]; + + $this->validation->setRules([ + 'a.*.c' => 'required_without[a.*.b, a.*.nonexistent]', + ])->run($data); + + $this->assertSame( + 'The a.*.c field is required when a.*.b, a.*.nonexistent is not present.', + $this->validation->getError('a.0.c'), + ); + } + /** * @see https://github.com/codeigniter4/CodeIgniter4/issues/8128 */