Skip to content

Commit bbe221b

Browse files
Merge branch '4.3' into 4.4
* 4.3: [HttpClient] Fix a bug preventing Server Pushes to be handled properly [HttpClient] fix support for 103 Early Hints and other informational status codes [DI] fix failure [Validator] Add ConstraintValidator::formatValue() tests [HttpClient] improve handling of HTTP/2 PUSH Fix #33427 [Validator] Only handle numeric values in DivisibleBy [Validator] Sync string to date behavior and throw a better exception Check phpunit configuration for listeners [DI] fix support for "!tagged_locator foo"
2 parents fd79ac2 + 4fb63ec commit bbe221b

12 files changed

+191
-26
lines changed

ConstraintValidator.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,10 @@ protected function formatTypeOf($value)
8585
*/
8686
protected function formatValue($value, $format = 0)
8787
{
88-
$isDateTime = $value instanceof \DateTimeInterface;
89-
90-
if (($format & self::PRETTY_DATE) && $isDateTime) {
88+
if (($format & self::PRETTY_DATE) && $value instanceof \DateTimeInterface) {
9189
if (class_exists('IntlDateFormatter')) {
9290
$locale = \Locale::getDefault();
93-
$formatter = new \IntlDateFormatter($locale, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT);
91+
$formatter = new \IntlDateFormatter($locale, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT, $value->getTimezone());
9492

9593
// neither the native nor the stub IntlDateFormatter support
9694
// DateTimeImmutable as of yet

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/DivisibleByValidator.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Validator\Constraints;
1313

14+
use Symfony\Component\Validator\Exception\UnexpectedValueException;
15+
1416
/**
1517
* Validates that values are a multiple of the given number.
1618
*
@@ -23,6 +25,14 @@ class DivisibleByValidator extends AbstractComparisonValidator
2325
*/
2426
protected function compareValues($value1, $value2)
2527
{
28+
if (!is_numeric($value1)) {
29+
throw new UnexpectedValueException($value1, 'numeric');
30+
}
31+
32+
if (!is_numeric($value2)) {
33+
throw new UnexpectedValueException($value2, 'numeric');
34+
}
35+
2636
if (!$value2 = abs($value2)) {
2737
return false;
2838
}

Constraints/RangeValidator.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,26 @@ public function validate($value, Constraint $constraint)
6161
// the DateTime constructor:
6262
// https://php.net/datetime.formats
6363
if ($value instanceof \DateTimeInterface) {
64+
$dateTimeClass = null;
65+
6466
if (\is_string($min)) {
65-
$min = new \DateTime($min);
67+
$dateTimeClass = $value instanceof \DateTimeImmutable ? \DateTimeImmutable::class : \DateTime::class;
68+
69+
try {
70+
$min = new $dateTimeClass($min);
71+
} catch (\Exception $e) {
72+
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)));
73+
}
6674
}
6775

6876
if (\is_string($max)) {
69-
$max = new \DateTime($max);
77+
$dateTimeClass = $dateTimeClass ?: ($value instanceof \DateTimeImmutable ? \DateTimeImmutable::class : \DateTime::class);
78+
79+
try {
80+
$max = new $dateTimeClass($max);
81+
} catch (\Exception $e) {
82+
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)));
83+
}
7084
}
7185
}
7286

Tests/ConstraintValidatorTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Validator\Constraint;
16+
use Symfony\Component\Validator\ConstraintValidator;
17+
18+
final class ConstraintValidatorTest extends TestCase
19+
{
20+
/**
21+
* @dataProvider formatValueProvider
22+
*/
23+
public function testFormatValue($expected, $value, $format = 0)
24+
{
25+
$this->assertSame($expected, (new TestFormatValueConstraintValidator())->formatValueProxy($value, $format));
26+
}
27+
28+
public function formatValueProvider()
29+
{
30+
$data = [
31+
['true', true],
32+
['false', false],
33+
['null', null],
34+
['resource', fopen('php://memory', 'r')],
35+
['"foo"', 'foo'],
36+
['array', []],
37+
['object', $toString = new TestToStringObject()],
38+
['ccc', $toString, ConstraintValidator::OBJECT_TO_STRING],
39+
['object', $dateTime = (new \DateTimeImmutable('@0'))->setTimezone(new \DateTimeZone('UTC'))],
40+
[class_exists(\IntlDateFormatter::class) ? 'Jan 1, 1970, 12:00 AM' : '1970-01-01 00:00:00', $dateTime, ConstraintValidator::PRETTY_DATE],
41+
];
42+
43+
return $data;
44+
}
45+
}
46+
47+
final class TestFormatValueConstraintValidator extends ConstraintValidator
48+
{
49+
public function validate($value, Constraint $constraint)
50+
{
51+
}
52+
53+
public function formatValueProxy($value, $format)
54+
{
55+
return $this->formatValue($value, $format);
56+
}
57+
}
58+
59+
final class TestToStringObject
60+
{
61+
public function __toString()
62+
{
63+
return 'ccc';
64+
}
65+
}

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

@@ -224,6 +225,31 @@ public function testInvalidComparisonToPropertyPathAddsPathAsParameter()
224225
->assertRaised();
225226
}
226227

228+
/**
229+
* @dataProvider throwsOnInvalidStringDatesProvider
230+
*/
231+
public function testThrowsOnInvalidStringDates(AbstractComparison $constraint, $expectedMessage, $value)
232+
{
233+
$this->expectException(ConstraintDefinitionException::class);
234+
$this->expectExceptionMessage($expectedMessage);
235+
236+
$this->validator->validate($value, $constraint);
237+
}
238+
239+
public function throwsOnInvalidStringDatesProvider(): array
240+
{
241+
$constraint = $this->createConstraint([
242+
'value' => 'foo',
243+
]);
244+
245+
$constraintClass = \get_class($constraint);
246+
247+
return [
248+
[$constraint, sprintf('The compared value "foo" could not be converted to a "DateTimeImmutable" instance in the "%s" constraint.', $constraintClass), new \DateTimeImmutable()],
249+
[$constraint, sprintf('The compared value "foo" could not be converted to a "DateTime" instance in the "%s" constraint.', $constraintClass), new \DateTime()],
250+
];
251+
}
252+
227253
public function provideAllInvalidComparisons(): array
228254
{
229255
// The provider runs before setUp(), so we need to manually fix

Tests/Constraints/DivisibleByValidatorTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Validator\Constraint;
1515
use Symfony\Component\Validator\Constraints\DivisibleBy;
1616
use Symfony\Component\Validator\Constraints\DivisibleByValidator;
17+
use Symfony\Component\Validator\Exception\UnexpectedValueException;
1718

1819
/**
1920
* @author Colin O'Dell <[email protected]>
@@ -76,4 +77,25 @@ public function provideInvalidComparisons(): array
7677
['22', '"22"', '10', '"10"', 'string'],
7778
];
7879
}
80+
81+
/**
82+
* @dataProvider throwsOnNonNumericValuesProvider
83+
*/
84+
public function testThrowsOnNonNumericValues(string $expectedGivenType, $value, $comparedValue)
85+
{
86+
$this->expectException(UnexpectedValueException::class);
87+
$this->expectExceptionMessage(sprintf('Expected argument of type "numeric", "%s" given', $expectedGivenType));
88+
89+
$this->validator->validate($value, $this->createConstraint([
90+
'value' => $comparedValue,
91+
]));
92+
}
93+
94+
public function throwsOnNonNumericValuesProvider()
95+
{
96+
return [
97+
[\stdClass::class, 2, new \stdClass()],
98+
[\ArrayIterator::class, new \ArrayIterator(), 12],
99+
];
100+
}
79101
}

Tests/Constraints/GreaterThanOrEqualValidatorWithPositiveOrZeroConstraintTest.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Validator\Tests\Constraints;
1313

1414
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\Constraints\AbstractComparison;
1516
use Symfony\Component\Validator\Constraints\PositiveOrZero;
1617

1718
/**
@@ -99,11 +100,11 @@ public function testValidComparisonToPropertyPath($comparedValue)
99100
}
100101

101102
/**
102-
* @dataProvider provideValidComparisonsToPropertyPath
103+
* @dataProvider throwsOnInvalidStringDatesProvider
103104
*/
104-
public function testValidComparisonToPropertyPathOnArray($comparedValue)
105+
public function testThrowsOnInvalidStringDates(AbstractComparison $constraint, $expectedMessage, $value)
105106
{
106-
$this->markTestSkipped('PropertyPath option is not used in Positive constraint');
107+
$this->markTestSkipped('The compared value cannot be an invalid string date because it is hardcoded to 0.');
107108
}
108109

109110
public function testInvalidComparisonToPropertyPathAddsPathAsParameter()

Tests/Constraints/GreaterThanValidatorWithPositiveConstraintTest.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Validator\Tests\Constraints;
1313

1414
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\Constraints\AbstractComparison;
1516
use Symfony\Component\Validator\Constraints\Positive;
1617

1718
/**
@@ -102,11 +103,11 @@ public function testValidComparisonToPropertyPath($comparedValue)
102103
}
103104

104105
/**
105-
* @dataProvider provideValidComparisonsToPropertyPath
106+
* @dataProvider throwsOnInvalidStringDatesProvider
106107
*/
107-
public function testValidComparisonToPropertyPathOnArray($comparedValue)
108+
public function testThrowsOnInvalidStringDates(AbstractComparison $constraint, $expectedMessage, $value)
108109
{
109-
$this->markTestSkipped('PropertyPath option is not used in Positive constraint');
110+
$this->markTestSkipped('The compared value cannot be an invalid string date because it is hardcoded to 0.');
110111
}
111112

112113
public function testInvalidComparisonToPropertyPathAddsPathAsParameter()

Tests/Constraints/LessThanOrEqualValidatorWithNegativeOrZeroConstraintTest.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Validator\Tests\Constraints;
1313

1414
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\Constraints\AbstractComparison;
1516
use Symfony\Component\Validator\Constraints\NegativeOrZero;
1617

1718
/**
@@ -102,11 +103,11 @@ public function testValidComparisonToPropertyPath($comparedValue)
102103
}
103104

104105
/**
105-
* @dataProvider provideValidComparisonsToPropertyPath
106+
* @dataProvider throwsOnInvalidStringDatesProvider
106107
*/
107-
public function testValidComparisonToPropertyPathOnArray($comparedValue)
108+
public function testThrowsOnInvalidStringDates(AbstractComparison $constraint, $expectedMessage, $value)
108109
{
109-
$this->markTestSkipped('PropertyPath option is not used in NegativeOrZero constraint');
110+
$this->markTestSkipped('The compared value cannot be an invalid string date because it is hardcoded to 0.');
110111
}
111112

112113
public function testInvalidComparisonToPropertyPathAddsPathAsParameter()

0 commit comments

Comments
 (0)