[Validator] Only traverse arrays that are cascaded into

This commit is contained in:
Philipp Cordes 2019-01-06 15:04:00 +01:00 committed by Fabien Potencier
parent 0c8e8a50b0
commit 7db9200279
3 changed files with 31 additions and 12 deletions

View File

@ -68,7 +68,7 @@ class Collection extends Composite
}
if (!$field instanceof Optional && !$field instanceof Required) {
$this->fields[$fieldName] = $field = new Required($field);
$this->fields[$fieldName] = new Required($field);
}
}
}

View File

@ -589,6 +589,30 @@ abstract class AbstractValidatorTest extends TestCase
$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();

View File

@ -352,24 +352,18 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
* 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 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
* 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 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
$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;
}