Skip to content

Commit

Permalink
treat uninitialized properties referenced by property paths as null
Browse files Browse the repository at this point in the history
  • Loading branch information
xabbuh committed Jul 26, 2024
1 parent 249792a commit 5ad62a0
Show file tree
Hide file tree
Showing 12 changed files with 152 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Constraints/AbstractComparisonValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Validator\Constraints;

use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Validator\Constraint;
Expand Down Expand Up @@ -56,6 +57,8 @@ public function validate($value, Constraint $constraint)
$comparedValue = $this->getPropertyAccessor()->getValue($object, $path);
} catch (NoSuchPropertyException $e) {
throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: ', $path, get_debug_type($constraint)).$e->getMessage(), 0, $e);
} catch (UninitializedPropertyException $e) {
$comparedValue = null;
}
} else {
$comparedValue = $constraint->value;
Expand Down
3 changes: 3 additions & 0 deletions Constraints/BicValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\Intl\Countries;
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\Validator\Constraint;
Expand Down Expand Up @@ -130,6 +131,8 @@ public function validate($value, Constraint $constraint)
$iban = $this->getPropertyAccessor()->getValue($object, $path);
} catch (NoSuchPropertyException $e) {
throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: ', $path, get_debug_type($constraint)).$e->getMessage(), 0, $e);
} catch (UninitializedPropertyException $e) {
$iban = null;
}
}
if (!$iban) {
Expand Down
3 changes: 3 additions & 0 deletions Constraints/RangeValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Validator\Constraints;

use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Validator\Constraint;
Expand Down Expand Up @@ -178,6 +179,8 @@ private function getLimit(?string $propertyPath, $default, Constraint $constrain
return $this->getPropertyAccessor()->getValue($object, $propertyPath);
} catch (NoSuchPropertyException $e) {
throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: ', $propertyPath, get_debug_type($constraint)).$e->getMessage(), 0, $e);
} catch (UninitializedPropertyException $e) {
return null;
}
}

Expand Down
28 changes: 28 additions & 0 deletions Tests/Constraints/AbstractComparisonValidatorTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Symfony\Component\Validator\Constraints\AbstractComparison;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
use Symfony\Component\Validator\Tests\Constraints\Fixtures\TypedDummy;

class ComparisonTest_Class
{
Expand Down Expand Up @@ -274,6 +275,33 @@ public function testCompareWithNullValueAtPropertyAt($dirtyValue, $dirtyValueAsS
}
}

/**
* @requires PHP 7.4
*
* @dataProvider provideComparisonsToNullValueAtPropertyPath
*/
public function testCompareWithUninitializedPropertyAtPropertyPath($dirtyValue, $dirtyValueAsString, $isValid)
{
$this->setObject(new TypedDummy());

$this->validator->validate($dirtyValue, $this->createConstraint([
'message' => 'Constraint Message',
'propertyPath' => 'value',
]));

if ($isValid) {
$this->assertNoViolation();
} else {
$this->buildViolation('Constraint Message')
->setParameter('{{ value }}', $dirtyValueAsString)
->setParameter('{{ compared_value }}', 'null')
->setParameter('{{ compared_value_type }}', 'null')
->setParameter('{{ compared_value_path }}', 'value')
->setCode($this->getErrorCode())
->assertRaised();
}
}

public static function provideAllInvalidComparisons(): array
{
// The provider runs before setUp(), so we need to manually fix
Expand Down
13 changes: 13 additions & 0 deletions Tests/Constraints/BicValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
use Symfony\Component\Validator\Tests\Constraints\Fixtures\BicTypedDummy;

class BicValidatorTest extends ConstraintValidatorTestCase
{
Expand Down Expand Up @@ -92,6 +93,18 @@ public function testInvalidComparisonToPropertyPathFromAttribute()
->assertRaised();
}

/**
* @requires PHP 7.4
*/
public function testPropertyPathReferencingUninitializedProperty()
{
$this->setObject(new BicTypedDummy());

$this->validator->validate('UNCRIT2B912', new Bic(['ibanPropertyPath' => 'iban']));

$this->assertNoViolation();
}

public function testValidComparisonToValue()
{
$constraint = new Bic(['iban' => 'FR14 2004 1010 0505 0001 3M02 606']);
Expand Down
17 changes: 17 additions & 0 deletions Tests/Constraints/Fixtures/BicTypedDummy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Validator\Tests\Constraints\Fixtures;

class BicTypedDummy
{
public string $iban;
}
18 changes: 18 additions & 0 deletions Tests/Constraints/Fixtures/MinMaxTyped.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Validator\Tests\Constraints\Fixtures;

class MinMaxTyped
{
public int $min;
public int $max;
}
17 changes: 17 additions & 0 deletions Tests/Constraints/Fixtures/TypedDummy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Validator\Tests\Constraints\Fixtures;

class TypedDummy
{
public mixed $value;
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@ public function testCompareWithNullValueAtPropertyAt($dirtyValue, $dirtyValueAsS
$this->markTestSkipped('PropertyPath option is not used in NegativeOrZero constraint');
}

/**
* @requires PHP 7.4
*
* @dataProvider provideComparisonsToNullValueAtPropertyPath
*/
public function testCompareWithUninitializedPropertyAtPropertyPath($dirtyValue, $dirtyValueAsString, $isValid)
{
$this->markTestSkipped('PropertyPath option is not used in NegativeOrZero constraint');
}

public static function throwsOnInvalidStringDatesProvider(): array
{
self::markTestSkipped('The "value" option cannot be used in the NegativeOrZero constraint');
Expand Down
10 changes: 10 additions & 0 deletions Tests/Constraints/LessThanValidatorWithNegativeConstraintTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@ public function testCompareWithNullValueAtPropertyAt($dirtyValue, $dirtyValueAsS
$this->markTestSkipped('PropertyPath option is not used in Negative constraint');
}

/**
* @requires PHP 7.4
*
* @dataProvider provideComparisonsToNullValueAtPropertyPath
*/
public function testCompareWithUninitializedPropertyAtPropertyPath($dirtyValue, $dirtyValueAsString, $isValid)
{
$this->markTestSkipped('PropertyPath option is not used in Negative constraint');
}

public function testInvalidComparisonToPropertyPathAddsPathAsParameter()
{
$this->markTestSkipped('PropertyPath option is not used in Negative constraint');
Expand Down
29 changes: 29 additions & 0 deletions Tests/Constraints/RangeValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Symfony\Component\Validator\Constraints\RangeValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
use Symfony\Component\Validator\Tests\Constraints\Fixtures\MinMaxTyped;
use Symfony\Component\Validator\Tests\IcuCompatibilityTrait;

class RangeValidatorTest extends ConstraintValidatorTestCase
Expand Down Expand Up @@ -1042,6 +1043,34 @@ public function testInvalidDatesCombinedMinPropertyPath($value, $dateTimeAsStrin
->assertRaised();
}

/**
* @requires PHP 7.4
*/
public function testMinPropertyPathReferencingUninitializedProperty()
{
$object = new MinMaxTyped();
$object->max = 5;
$this->setObject($object);

$this->validator->validate(5, new Range(['minPropertyPath' => 'min', 'maxPropertyPath' => 'max']));

$this->assertNoViolation();
}

/**
* @requires PHP 7.4
*/
public function testMaxPropertyPathReferencingUninitializedProperty()
{
$object = new MinMaxTyped();
$object->min = 5;
$this->setObject($object);

$this->validator->validate(5, new Range(['minPropertyPath' => 'min', 'maxPropertyPath' => 'max']));

$this->assertNoViolation();
}

public static function provideMessageIfMinAndMaxSet(): array
{
$notInRangeMessage = (new Range(['min' => '']))->notInRangeMessage;
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"symfony/expression-language": "^5.1|^6.0",
"symfony/cache": "^4.4|^5.0|^6.0",
"symfony/mime": "^4.4|^5.0|^6.0",
"symfony/property-access": "^4.4|^5.0|^6.0",
"symfony/property-access": "^5.4|^6.0",
"symfony/property-info": "^5.3|^6.0",
"symfony/translation": "^5.4.35|~6.3.12|^6.4.3",
"doctrine/annotations": "^1.13|^2",
Expand Down

0 comments on commit 5ad62a0

Please sign in to comment.