bug #36627 [Validator] fix lazy property usage. (bendavies)

This PR was squashed before being merged into the 3.4 branch (closes #36627).

Discussion
----------

[Validator] fix lazy property usage.

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | Fix #36343
| License       | MIT
| Doc PR        |

This attempts to fix a large regression introduced in #36343, which broke recursing values returned from `getter` Constraints, because they are now wrapped  in in a `LazyProperty`. The `LazyProperty` needs to be evaluated because some checks are done on the type of `$value`, i.e `is_array` etc... in `validateGenericNode`.

I'm concerned that the original PR didn't really add sufficient test coverage for the introduction of `LazyProperty`, and I'm not 100% sure that I've caught all the cases where the `instanceof` check are needed in this PR.

For the tests, I added the `@dataProvider getConstraintMethods` to every test that hit the problem area of code.

~~The only issue is that my fixed has broken the test introduced in #36343, `testGroupedMethodConstraintValidateInSequence`.~~

~~I think I need @HeahDude to help me work through this. Maybe there is a more simple solution, one that doesn't require doing `instanceof LazyPropery` checks in multiple places, because this feels very brittle.~~
EDIT: fixed that test.

Commits
-------

281861e788 [Validator] fix lazy property usage.
This commit is contained in:
Christian Flothmann 2020-05-02 08:43:10 +02:00
commit aee10cd44a
3 changed files with 56 additions and 13 deletions

View File

@ -53,6 +53,11 @@ class Entity extends EntityParent implements EntityInterfaceB
$this->internal = $internal;
}
public function getFirstName()
{
return $this->firstName;
}
public function getInternal()
{
return $this->internal.' from getter';
@ -141,4 +146,9 @@ class Entity extends EntityParent implements EntityInterfaceB
{
$this->childB = $childB;
}
public function getReference()
{
return $this->reference;
}
}

View File

@ -32,6 +32,8 @@ abstract class AbstractValidatorTest extends TestCase
const REFERENCE_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\Reference';
const LAZY_PROPERTY = 'Symfony\Component\Validator\Validator\LazyProperty';
/**
* @var FakeMetadataFactory
*/
@ -54,6 +56,7 @@ abstract class AbstractValidatorTest extends TestCase
$this->referenceMetadata = new ClassMetadata(self::REFERENCE_CLASS);
$this->metadataFactory->addMetadata($this->metadata);
$this->metadataFactory->addMetadata($this->referenceMetadata);
$this->metadataFactory->addMetadata(new ClassMetadata(self::LAZY_PROPERTY));
}
protected function tearDown()
@ -510,7 +513,10 @@ abstract class AbstractValidatorTest extends TestCase
$this->validate($entity);
}
public function testArrayReference()
/**
* @dataProvider getConstraintMethods
*/
public function testArrayReference($constraintMethod)
{
$entity = new Entity();
$entity->reference = ['key' => new Reference()];
@ -528,7 +534,7 @@ abstract class AbstractValidatorTest extends TestCase
$context->addViolation('Message %param%', ['%param%' => 'value']);
};
$this->metadata->addPropertyConstraint('reference', new Valid());
$this->metadata->$constraintMethod('reference', new Valid());
$this->referenceMetadata->addConstraint(new Callback([
'callback' => $callback,
'groups' => 'Group',
@ -548,8 +554,10 @@ abstract class AbstractValidatorTest extends TestCase
$this->assertNull($violations[0]->getCode());
}
// https://github.com/symfony/symfony/issues/6246
public function testRecursiveArrayReference()
/**
* @dataProvider getConstraintMethods
*/
public function testRecursiveArrayReference($constraintMethod)
{
$entity = new Entity();
$entity->reference = [2 => ['key' => new Reference()]];
@ -567,7 +575,7 @@ abstract class AbstractValidatorTest extends TestCase
$context->addViolation('Message %param%', ['%param%' => 'value']);
};
$this->metadata->addPropertyConstraint('reference', new Valid());
$this->metadata->$constraintMethod('reference', new Valid());
$this->referenceMetadata->addConstraint(new Callback([
'callback' => $callback,
'groups' => 'Group',
@ -611,7 +619,10 @@ abstract class AbstractValidatorTest extends TestCase
$this->assertCount(0, $violations);
}
public function testArrayTraversalCannotBeDisabled()
/**
* @dataProvider getConstraintMethods
*/
public function testArrayTraversalCannotBeDisabled($constraintMethod)
{
$entity = new Entity();
$entity->reference = ['key' => new Reference()];
@ -620,7 +631,7 @@ abstract class AbstractValidatorTest extends TestCase
$context->addViolation('Message %param%', ['%param%' => 'value']);
};
$this->metadata->addPropertyConstraint('reference', new Valid([
$this->metadata->$constraintMethod('reference', new Valid([
'traverse' => false,
]));
$this->referenceMetadata->addConstraint(new Callback($callback));
@ -631,7 +642,10 @@ abstract class AbstractValidatorTest extends TestCase
$this->assertCount(1, $violations);
}
public function testRecursiveArrayTraversalCannotBeDisabled()
/**
* @dataProvider getConstraintMethods
*/
public function testRecursiveArrayTraversalCannotBeDisabled($constraintMethod)
{
$entity = new Entity();
$entity->reference = [2 => ['key' => new Reference()]];
@ -640,9 +654,10 @@ abstract class AbstractValidatorTest extends TestCase
$context->addViolation('Message %param%', ['%param%' => 'value']);
};
$this->metadata->addPropertyConstraint('reference', new Valid([
$this->metadata->$constraintMethod('reference', new Valid([
'traverse' => false,
]));
$this->referenceMetadata->addConstraint(new Callback($callback));
$violations = $this->validate($entity);
@ -651,12 +666,15 @@ abstract class AbstractValidatorTest extends TestCase
$this->assertCount(1, $violations);
}
public function testIgnoreScalarsDuringArrayTraversal()
/**
* @dataProvider getConstraintMethods
*/
public function testIgnoreScalarsDuringArrayTraversal($constraintMethod)
{
$entity = new Entity();
$entity->reference = ['string', 1234];
$this->metadata->addPropertyConstraint('reference', new Valid());
$this->metadata->$constraintMethod('reference', new Valid());
$violations = $this->validate($entity);
@ -664,12 +682,15 @@ abstract class AbstractValidatorTest extends TestCase
$this->assertCount(0, $violations);
}
public function testIgnoreNullDuringArrayTraversal()
/**
* @dataProvider getConstraintMethods
*/
public function testIgnoreNullDuringArrayTraversal($constraintMethod)
{
$entity = new Entity();
$entity->reference = [null];
$this->metadata->addPropertyConstraint('reference', new Valid());
$this->metadata->$constraintMethod('reference', new Valid());
$violations = $this->validate($entity);
@ -1218,6 +1239,14 @@ abstract class AbstractValidatorTest extends TestCase
}
}
public function getConstraintMethods()
{
return [
['addPropertyConstraint'],
['addGetterConstraint'],
];
}
public function getTestReplaceDefaultGroup()
{
return [

View File

@ -677,6 +677,10 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
// See validateClassNode()
$cascadedGroups = null !== $cascadedGroups && \count($cascadedGroups) > 0 ? $cascadedGroups : $groups;
if ($value instanceof LazyProperty) {
$value = $value->getPropertyValue();
}
if (\is_array($value)) {
// Arrays are always traversed, independent of the specified
// traversal strategy