diff --git a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php index 7e9934d133..cd96bc9df7 100644 --- a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php +++ b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php @@ -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))); } } diff --git a/src/Symfony/Component/Validator/Constraints/RangeValidator.php b/src/Symfony/Component/Validator/Constraints/RangeValidator.php index c7cb859a5a..ea7d277808 100644 --- a/src/Symfony/Component/Validator/Constraints/RangeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/RangeValidator.php @@ -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))); + } } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php b/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php index cb10c06164..b02e57cfa2 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php @@ -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 */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php index df33d2c422..83e32d8d97 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php @@ -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'], + ]; + } }