Skip to content

Commit 0498cbf

Browse files
committed
[Validator] Sync string to date behavior and throw a better exception
1 parent 4dde4e7 commit 0498cbf

File tree

4 files changed

+77
-10
lines changed

4 files changed

+77
-10
lines changed

Constraints/AbstractComparisonValidator.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ public function validate($value, Constraint $constraint)
6565
// This allows to compare with any date/time value supported by
6666
// the DateTime constructor:
6767
// https://php.net/datetime.formats
68-
if (\is_string($comparedValue)) {
69-
if ($value instanceof \DateTimeImmutable) {
70-
// If $value is immutable, convert the compared value to a
71-
// DateTimeImmutable too
72-
$comparedValue = new \DateTimeImmutable($comparedValue);
73-
} elseif ($value instanceof \DateTimeInterface) {
74-
// Otherwise use DateTime
75-
$comparedValue = new \DateTime($comparedValue);
68+
if (\is_string($comparedValue) && $value instanceof \DateTimeInterface) {
69+
// If $value is immutable, convert the compared value to a DateTimeImmutable too, otherwise use DateTime
70+
$dateTimeClass = $value instanceof \DateTimeImmutable ? \DateTimeImmutable::class : \DateTime::class;
71+
72+
try {
73+
$comparedValue = new $dateTimeClass($comparedValue);
74+
} catch (\Exception $e) {
75+
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)));
7676
}
7777
}
7878

Constraints/RangeValidator.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Validator\Constraint;
1515
use Symfony\Component\Validator\ConstraintValidator;
16+
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
1617
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
1718

1819
/**
@@ -50,12 +51,26 @@ public function validate($value, Constraint $constraint)
5051
// the DateTime constructor:
5152
// https://php.net/datetime.formats
5253
if ($value instanceof \DateTimeInterface) {
54+
$dateTimeClass = null;
55+
5356
if (\is_string($min)) {
54-
$min = new \DateTime($min);
57+
$dateTimeClass = $value instanceof \DateTimeImmutable ? \DateTimeImmutable::class : \DateTime::class;
58+
59+
try {
60+
$min = new $dateTimeClass($min);
61+
} catch (\Exception $e) {
62+
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)));
63+
}
5564
}
5665

5766
if (\is_string($max)) {
58-
$max = new \DateTime($max);
67+
$dateTimeClass = $dateTimeClass ?: ($value instanceof \DateTimeImmutable ? \DateTimeImmutable::class : \DateTime::class);
68+
69+
try {
70+
$max = new $dateTimeClass($max);
71+
} catch (\Exception $e) {
72+
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)));
73+
}
5974
}
6075
}
6176

Tests/Constraints/AbstractComparisonValidatorTestCase.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Intl\Util\IntlTestHelper;
1515
use Symfony\Component\Validator\Constraint;
16+
use Symfony\Component\Validator\Constraints\AbstractComparison;
1617
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
1718
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
1819

@@ -211,6 +212,31 @@ public function testInvalidComparisonToValue($dirtyValue, $dirtyValueAsString, $
211212
->assertRaised();
212213
}
213214

215+
/**
216+
* @dataProvider throwsOnInvalidStringDatesProvider
217+
*/
218+
public function testThrowsOnInvalidStringDates(AbstractComparison $constraint, $expectedMessage, $value)
219+
{
220+
$this->expectException(ConstraintDefinitionException::class);
221+
$this->expectExceptionMessage($expectedMessage);
222+
223+
$this->validator->validate($value, $constraint);
224+
}
225+
226+
public function throwsOnInvalidStringDatesProvider()
227+
{
228+
$constraint = $this->createConstraint([
229+
'value' => 'foo',
230+
]);
231+
232+
$constraintClass = \get_class($constraint);
233+
234+
return [
235+
[$constraint, sprintf('The compared value "foo" could not be converted to a "DateTimeImmutable" instance in the "%s" constraint.', $constraintClass), new \DateTimeImmutable()],
236+
[$constraint, sprintf('The compared value "foo" could not be converted to a "DateTime" instance in the "%s" constraint.', $constraintClass), new \DateTime()],
237+
];
238+
}
239+
214240
/**
215241
* @return array
216242
*/

Tests/Constraints/RangeValidatorTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Intl\Util\IntlTestHelper;
1515
use Symfony\Component\Validator\Constraints\Range;
1616
use Symfony\Component\Validator\Constraints\RangeValidator;
17+
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
1718
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
1819

1920
class RangeValidatorTest extends ConstraintValidatorTestCase
@@ -389,4 +390,29 @@ public function testNonNumeric()
389390
->setCode(Range::INVALID_CHARACTERS_ERROR)
390391
->assertRaised();
391392
}
393+
394+
/**
395+
* @dataProvider throwsOnInvalidStringDatesProvider
396+
*/
397+
public function testThrowsOnInvalidStringDates($expectedMessage, $value, $min, $max)
398+
{
399+
$this->expectException(ConstraintDefinitionException::class);
400+
$this->expectExceptionMessage($expectedMessage);
401+
402+
$this->validator->validate($value, new Range([
403+
'min' => $min,
404+
'max' => $max,
405+
]));
406+
}
407+
408+
public function throwsOnInvalidStringDatesProvider()
409+
{
410+
return [
411+
['The min value "foo" could not be converted to a "DateTimeImmutable" instance in the "Symfony\Component\Validator\Constraints\Range" constraint.', new \DateTimeImmutable(), 'foo', null],
412+
['The min value "foo" could not be converted to a "DateTime" instance in the "Symfony\Component\Validator\Constraints\Range" constraint.', new \DateTime(), 'foo', null],
413+
['The max value "foo" could not be converted to a "DateTimeImmutable" instance in the "Symfony\Component\Validator\Constraints\Range" constraint.', new \DateTimeImmutable(), null, 'foo'],
414+
['The max value "foo" could not be converted to a "DateTime" instance in the "Symfony\Component\Validator\Constraints\Range" constraint.', new \DateTime(), null, 'foo'],
415+
['The min value "bar" could not be converted to a "DateTimeImmutable" instance in the "Symfony\Component\Validator\Constraints\Range" constraint.', new \DateTimeImmutable(), 'bar', 'ccc'],
416+
];
417+
}
392418
}

0 commit comments

Comments
 (0)