diff --git a/src/Symfony/Component/Validator/Constraints/Collection.php b/src/Symfony/Component/Validator/Constraints/Collection.php index 21427722d9491..86418690b8d2b 100644 --- a/src/Symfony/Component/Validator/Constraints/Collection.php +++ b/src/Symfony/Component/Validator/Constraints/Collection.php @@ -68,7 +68,7 @@ protected function initializeNestedConstraints() } if (!$field instanceof Optional && !$field instanceof Required) { - $this->fields[$fieldName] = $field = new Required($field); + $this->fields[$fieldName] = new Required($field); } } } diff --git a/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php index ecfc10feb643a..884ccc7da0f95 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php @@ -589,6 +589,30 @@ public function testRecursiveArrayReference() $this->assertNull($violations[0]->getCode()); } + public function testOnlyCascadedArraysAreTraversed() + { + $entity = new Entity(); + $entity->reference = ['key' => new Reference()]; + + $callback = function ($value, ExecutionContextInterface $context) { + $context->addViolation('Message %param%', ['%param%' => 'value']); + }; + + $this->metadata->addPropertyConstraint('reference', new Callback([ + 'callback' => function () {}, + 'groups' => 'Group', + ])); + $this->referenceMetadata->addConstraint(new Callback([ + 'callback' => $callback, + 'groups' => 'Group', + ])); + + $violations = $this->validate($entity, null, 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $this->assertCount(0, $violations); + } + public function testArrayTraversalCannotBeDisabled() { $entity = new Entity(); diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index 50253c50e1bb4..dc139c36d4254 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -352,24 +352,18 @@ private function validateObject($object, $propertyPath, array $groups, $traversa * Validates each object in a collection against the constraints defined * for their classes. * - * If the parameter $recursive is set to true, nested {@link \Traversable} - * objects are iterated as well. Nested arrays are always iterated, - * regardless of the value of $recursive. + * Nested arrays are also iterated. * * @param iterable $collection The collection * @param string $propertyPath The current property path * @param (string|GroupSequence)[] $groups The validated groups * @param ExecutionContextInterface $context The current execution context - * - * @see ClassNode - * @see CollectionNode */ private function validateEachObjectIn($collection, $propertyPath, array $groups, ExecutionContextInterface $context) { foreach ($collection as $key => $value) { if (\is_array($value)) { - // Arrays are always cascaded, independent of the specified - // traversal strategy + // Also traverse nested arrays $this->validateEachObjectIn( $value, $propertyPath.'['.$key.']', @@ -599,7 +593,8 @@ private function validateClassNode($object, $cacheKey, ClassMetadataInterface $m * in the passed metadata object. Then, if the value is an instance of * {@link \Traversable} and the selected traversal strategy permits it, * the value is traversed and each nested object validated against its own - * constraints. Arrays are always traversed. + * constraints. If the value is an array, it is traversed regardless of + * the given strategy. * * @param mixed $value The validated value * @param object|null $object The current object @@ -658,8 +653,8 @@ private function validateGenericNode($value, $object, $cacheKey, MetadataInterfa $cascadingStrategy = $metadata->getCascadingStrategy(); - // Quit unless we have an array or a cascaded object - if (!\is_array($value) && !($cascadingStrategy & CascadingStrategy::CASCADE)) { + // Quit unless we cascade + if (!($cascadingStrategy & CascadingStrategy::CASCADE)) { return; }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: