From b2328d4cedfae67bfacd272e3d8f7deab05b4f2f Mon Sep 17 00:00:00 2001 From: CorvusSharp <90767809+CorvusSharp@users.noreply.github.com> Date: Fri, 11 Sep 2026 08:30:25 +0300 Subject: [PATCH 1/2] Fix UniqueIterable comparing the whole iterable instead of its items --- src/Rule/UniqueIterableHandler.php | 24 +++++----- tests/Rule/UniqueIterableTest.php | 71 ++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/src/Rule/UniqueIterableHandler.php b/src/Rule/UniqueIterableHandler.php index a06cb7a32..7f20739c9 100644 --- a/src/Rule/UniqueIterableHandler.php +++ b/src/Rule/UniqueIterableHandler.php @@ -57,22 +57,22 @@ public function validate(mixed $value, RuleInterface $rule, ValidationContext $c $previousItem = $item; - if (!empty($stack) && count($stack) !== count(array_unique($stack, flags: SORT_REGULAR))) { - return (new Result())->addError($rule->getMessage(), [ - 'property' => $context->getTranslatedProperty(), - 'Property' => $context->getCapitalizedTranslatedProperty(), - ]); - } - - if ($value instanceof Stringable) { - $stack[] = (string) $value; - } elseif ($value instanceof DateTimeInterface) { - $stack[] = $value->getTimestamp(); + if ($item instanceof Stringable) { + $stack[] = (string) $item; + } elseif ($item instanceof DateTimeInterface) { + $stack[] = $item->getTimestamp(); } else { - $stack[] = $value; + $stack[] = $item; } } + if (count($stack) !== count(array_unique($stack, flags: SORT_REGULAR))) { + return (new Result())->addError($rule->getMessage(), [ + 'property' => $context->getTranslatedProperty(), + 'Property' => $context->getCapitalizedTranslatedProperty(), + ]); + } + return new Result(); } diff --git a/tests/Rule/UniqueIterableTest.php b/tests/Rule/UniqueIterableTest.php index d2f835c0e..ac6031d76 100644 --- a/tests/Rule/UniqueIterableTest.php +++ b/tests/Rule/UniqueIterableTest.php @@ -120,6 +120,40 @@ public function __toString() [new DateTime('2024-04-10 14:05:01'), new DateTime('2024-04-10 14:05:02')], new UniqueIterable(), ], + 'more than two unique strings' => [['a', 'b', 'c'], new UniqueIterable()], + 'more than two unique integers' => [[1, 2, 3, 4], new UniqueIterable()], + 'more than two unique floats' => [[1.5, 2.5, 3.5], new UniqueIterable()], + 'more than two unique stringable values' => [ + [ + new class implements Stringable { + public function __toString() + { + return 'a'; + } + }, + new class implements Stringable { + public function __toString() + { + return 'b'; + } + }, + new class implements Stringable { + public function __toString() + { + return 'c'; + } + }, + ], + new UniqueIterable(), + ], + 'more than two unique datetime values' => [ + [ + new DateTime('2024-04-10 14:05:01'), + new DateTime('2024-04-10 14:05:02'), + new DateTime('2024-04-10 14:05:03'), + ], + new UniqueIterable(), + ], 'using as attribute' => [ new class { #[UniqueIterable] @@ -213,6 +247,43 @@ public function getRules(): array null, ['data' => ['"Данные" - в списке есть недопустимое значение.']], ], + 'two equal strings' => [['a', 'a'], new UniqueIterable(), ['' => [$message]]], + 'duplicate loses to a later disallowed item' => [ + [1, 1, []], + new UniqueIterable(), + ['' => [$incorrectItemValueMessage]], + ], + 'duplicate loses to a later type mismatch' => [ + ['data' => [1, 1, 'a']], + ['data' => new UniqueIterable()], + ['data' => ['All iterable items of data must have the same type.']], + ], + 'two equal integers' => [[1, 1], new UniqueIterable(), ['' => [$message]]], + 'two equal floats' => [[1.5, 1.5], new UniqueIterable(), ['' => [$message]]], + 'two equal boolean values' => [[true, true], new UniqueIterable(), ['' => [$message]]], + 'two equal stringable values' => [ + [ + new class implements Stringable { + public function __toString() + { + return 'a'; + } + }, + new class implements Stringable { + public function __toString() + { + return 'a'; + } + }, + ], + new UniqueIterable(), + ['' => [$message]], + ], + 'two equal datetime values' => [ + [new DateTime('2024-04-10 14:05:01'), new DateTime('2024-04-10 14:05:01')], + new UniqueIterable(), + ['' => [$message]], + ], 'strings' => [['a', 'b', 'a', 'c'], new UniqueIterable(), ['' => [$message]]], 'integers' => [[1, 2, 1, 3], new UniqueIterable(), ['' => [$message]]], 'floats' => [[1.5, 2.5, 1.5, 3.5], new UniqueIterable(), ['' => [$message]]], From 4e0c53aaeccc1d6bc220f02f3e2fd8afe27c3764 Mon Sep 17 00:00:00 2001 From: CorvusSharp <90767809+CorvusSharp@users.noreply.github.com> Date: Fri, 11 Sep 2026 08:30:25 +0300 Subject: [PATCH 2/2] Add changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d91eca83..f59a4807e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 2.6.1 under development -- no changes in this release. +- Bug #817: Fix `UniqueIterable` comparing the whole iterable instead of its items (@CorvusSharp) ## 2.6.0 June 02, 2026