bug #33439 [Validator] Sync string to date behavior and throw a better exception (fancyweb)
This PR was merged into the 3.4 branch.
Discussion
----------
[Validator] Sync string to date behavior and throw a better exception
| Q | A
| ------------- | ---
| Branch? | 3.4
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
`\DateTimeImmutable` are not compared with `\DateTimeImmutable` in the `RangeValidator` class (contrary to the behavior in the `AbstractComparisonValidator` class).
Also, let's throw a dedicated exception when the provided string value cannot be parsed to a `\DateTime` or `\DateTimeImmutable`. It's better than the default exception IMO.
Commits
-------
28d7d9444f
[Validator] Sync string to date behavior and throw a better exception
This commit is contained in:
commit
38514cb0f4
@ -65,14 +65,14 @@ abstract class AbstractComparisonValidator extends ConstraintValidator
|
||||
// This allows to compare with any date/time value supported by
|
||||
// the DateTime constructor:
|
||||
// https://php.net/datetime.formats
|
||||
if (\is_string($comparedValue)) {
|
||||
if ($value instanceof \DateTimeImmutable) {
|
||||
// If $value is immutable, convert the compared value to a
|
||||
// DateTimeImmutable too
|
||||
$comparedValue = new \DateTimeImmutable($comparedValue);
|
||||
} elseif ($value instanceof \DateTimeInterface) {
|
||||
// Otherwise use DateTime
|
||||
$comparedValue = new \DateTime($comparedValue);
|
||||
if (\is_string($comparedValue) && $value instanceof \DateTimeInterface) {
|
||||
// If $value is immutable, convert the compared value to a DateTimeImmutable too, otherwise use DateTime
|
||||
$dateTimeClass = $value instanceof \DateTimeImmutable ? \DateTimeImmutable::class : \DateTime::class;
|
||||
|
||||
try {
|
||||
$comparedValue = new $dateTimeClass($comparedValue);
|
||||
} catch (\Exception $e) {
|
||||
throw new ConstraintDefinitionException(sprintf('The compared value "%s" could not be converted to a "%s" instance in the "%s" constraint.', $comparedValue, $dateTimeClass, \get_class($constraint)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
@ -50,12 +51,26 @@ class RangeValidator extends ConstraintValidator
|
||||
// the DateTime constructor:
|
||||
// https://php.net/datetime.formats
|
||||
if ($value instanceof \DateTimeInterface) {
|
||||
$dateTimeClass = null;
|
||||
|
||||
if (\is_string($min)) {
|
||||
$min = new \DateTime($min);
|
||||
$dateTimeClass = $value instanceof \DateTimeImmutable ? \DateTimeImmutable::class : \DateTime::class;
|
||||
|
||||
try {
|
||||
$min = new $dateTimeClass($min);
|
||||
} catch (\Exception $e) {
|
||||
throw new ConstraintDefinitionException(sprintf('The min value "%s" could not be converted to a "%s" instance in the "%s" constraint.', $min, $dateTimeClass, \get_class($constraint)));
|
||||
}
|
||||
}
|
||||
|
||||
if (\is_string($max)) {
|
||||
$max = new \DateTime($max);
|
||||
$dateTimeClass = $dateTimeClass ?: ($value instanceof \DateTimeImmutable ? \DateTimeImmutable::class : \DateTime::class);
|
||||
|
||||
try {
|
||||
$max = new $dateTimeClass($max);
|
||||
} catch (\Exception $e) {
|
||||
throw new ConstraintDefinitionException(sprintf('The max value "%s" could not be converted to a "%s" instance in the "%s" constraint.', $max, $dateTimeClass, \get_class($constraint)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Validator\Tests\Constraints;
|
||||
|
||||
use Symfony\Component\Intl\Util\IntlTestHelper;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\Constraints\AbstractComparison;
|
||||
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
|
||||
|
||||
@ -211,6 +212,31 @@ abstract class AbstractComparisonValidatorTestCase extends ConstraintValidatorTe
|
||||
->assertRaised();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider throwsOnInvalidStringDatesProvider
|
||||
*/
|
||||
public function testThrowsOnInvalidStringDates(AbstractComparison $constraint, $expectedMessage, $value)
|
||||
{
|
||||
$this->expectException(ConstraintDefinitionException::class);
|
||||
$this->expectExceptionMessage($expectedMessage);
|
||||
|
||||
$this->validator->validate($value, $constraint);
|
||||
}
|
||||
|
||||
public function throwsOnInvalidStringDatesProvider()
|
||||
{
|
||||
$constraint = $this->createConstraint([
|
||||
'value' => 'foo',
|
||||
]);
|
||||
|
||||
$constraintClass = \get_class($constraint);
|
||||
|
||||
return [
|
||||
[$constraint, sprintf('The compared value "foo" could not be converted to a "DateTimeImmutable" instance in the "%s" constraint.', $constraintClass), new \DateTimeImmutable()],
|
||||
[$constraint, sprintf('The compared value "foo" could not be converted to a "DateTime" instance in the "%s" constraint.', $constraintClass), new \DateTime()],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Validator\Tests\Constraints;
|
||||
use Symfony\Component\Intl\Util\IntlTestHelper;
|
||||
use Symfony\Component\Validator\Constraints\Range;
|
||||
use Symfony\Component\Validator\Constraints\RangeValidator;
|
||||
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
|
||||
|
||||
class RangeValidatorTest extends ConstraintValidatorTestCase
|
||||
@ -389,4 +390,29 @@ class RangeValidatorTest extends ConstraintValidatorTestCase
|
||||
->setCode(Range::INVALID_CHARACTERS_ERROR)
|
||||
->assertRaised();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider throwsOnInvalidStringDatesProvider
|
||||
*/
|
||||
public function testThrowsOnInvalidStringDates($expectedMessage, $value, $min, $max)
|
||||
{
|
||||
$this->expectException(ConstraintDefinitionException::class);
|
||||
$this->expectExceptionMessage($expectedMessage);
|
||||
|
||||
$this->validator->validate($value, new Range([
|
||||
'min' => $min,
|
||||
'max' => $max,
|
||||
]));
|
||||
}
|
||||
|
||||
public function throwsOnInvalidStringDatesProvider()
|
||||
{
|
||||
return [
|
||||
['The min value "foo" could not be converted to a "DateTimeImmutable" instance in the "Symfony\Component\Validator\Constraints\Range" constraint.', new \DateTimeImmutable(), 'foo', null],
|
||||
['The min value "foo" could not be converted to a "DateTime" instance in the "Symfony\Component\Validator\Constraints\Range" constraint.', new \DateTime(), 'foo', null],
|
||||
['The max value "foo" could not be converted to a "DateTimeImmutable" instance in the "Symfony\Component\Validator\Constraints\Range" constraint.', new \DateTimeImmutable(), null, 'foo'],
|
||||
['The max value "foo" could not be converted to a "DateTime" instance in the "Symfony\Component\Validator\Constraints\Range" constraint.', new \DateTime(), null, 'foo'],
|
||||
['The min value "bar" could not be converted to a "DateTimeImmutable" instance in the "Symfony\Component\Validator\Constraints\Range" constraint.', new \DateTimeImmutable(), 'bar', 'ccc'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user