From 7db9200279319a5730d7bd44cd2db137d35dde43 Mon Sep 17 00:00:00 2001 From: Philipp Cordes Date: Sun, 6 Jan 2019 15:04:00 +0100 Subject: [PATCH] [Validator] Only traverse arrays that are cascaded into --- .../Validator/Constraints/Collection.php | 2 +- .../Tests/Validator/AbstractValidatorTest.php | 24 +++++++++++++++++++ .../RecursiveContextualValidator.php | 17 +++++-------- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/Collection.php b/src/Symfony/Component/Validator/Constraints/Collection.php index 21427722d949..86418690b8d2 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 ecfc10feb643..884ccc7da0f9 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 50253c50e1bb..dc139c36d425 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; }