From 0ee9933fd7fdc984f020cb5fe9792e43c39676f5 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sun, 26 Nov 2023 10:27:26 +0100 Subject: [PATCH 01/19] [Validator] Made tests forward-compatible with ICU 72.1 --- .../Tests/ConstraintValidatorTest.php | 18 ++--- .../Constraints/EqualToValidatorTest.php | 11 +-- .../GreaterThanOrEqualValidatorTest.php | 11 +-- .../Constraints/GreaterThanValidatorTest.php | 17 +++-- .../Constraints/IdenticalToValidatorTest.php | 9 ++- .../LessThanOrEqualValidatorTest.php | 11 +-- .../Constraints/LessThanValidatorTest.php | 17 +++-- .../Constraints/NotEqualToValidatorTest.php | 11 +-- .../NotIdenticalToValidatorTest.php | 11 +-- .../Tests/Constraints/RangeValidatorTest.php | 70 +++++++++---------- .../Validator/Tests/IcuCompatibilityTrait.php | 30 ++++++++ 11 files changed, 135 insertions(+), 81 deletions(-) create mode 100644 src/Symfony/Component/Validator/Tests/IcuCompatibilityTrait.php diff --git a/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php b/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php index 7fb4a91c6cdd4..e5ec479141eda 100644 --- a/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php @@ -18,17 +18,19 @@ class ConstraintValidatorTest extends TestCase { + use IcuCompatibilityTrait; + /** * @dataProvider formatValueProvider */ - public function testFormatValue($expected, $value, $format = 0) + public function testFormatValue(string $expected, $value, int $format = 0) { \Locale::setDefault('en'); $this->assertSame($expected, (new TestFormatValueConstraintValidator())->formatValueProxy($value, $format)); } - public static function formatValueProvider() + public static function formatValueProvider(): array { $defaultTimezone = date_default_timezone_get(); date_default_timezone_set('Europe/Moscow'); // GMT+3 @@ -43,10 +45,10 @@ public static function formatValueProvider() ['object', $toString = new TestToStringObject()], ['ccc', $toString, ConstraintValidator::OBJECT_TO_STRING], ['object', $dateTime = new \DateTimeImmutable('1971-02-02T08:00:00UTC')], - [class_exists(\IntlDateFormatter::class) ? 'Oct 4, 2019, 11:02 AM' : '2019-10-04 11:02:03', new \DateTimeImmutable('2019-10-04T11:02:03+09:00'), ConstraintValidator::PRETTY_DATE], - [class_exists(\IntlDateFormatter::class) ? 'Feb 2, 1971, 8:00 AM' : '1971-02-02 08:00:00', $dateTime, ConstraintValidator::PRETTY_DATE], - [class_exists(\IntlDateFormatter::class) ? 'Jan 1, 1970, 6:00 AM' : '1970-01-01 06:00:00', new \DateTimeImmutable('1970-01-01T06:00:00Z'), ConstraintValidator::PRETTY_DATE], - [class_exists(\IntlDateFormatter::class) ? 'Jan 1, 1970, 3:00 PM' : '1970-01-01 15:00:00', (new \DateTimeImmutable('1970-01-01T23:00:00'))->setTimezone(new \DateTimeZone('America/New_York')), ConstraintValidator::PRETTY_DATE], + [class_exists(\IntlDateFormatter::class) ? static::normalizeIcuSpaces("Oct 4, 2019, 11:02\u{202F}AM") : '2019-10-04 11:02:03', new \DateTimeImmutable('2019-10-04T11:02:03+09:00'), ConstraintValidator::PRETTY_DATE], + [class_exists(\IntlDateFormatter::class) ? static::normalizeIcuSpaces("Feb 2, 1971, 8:00\u{202F}AM") : '1971-02-02 08:00:00', $dateTime, ConstraintValidator::PRETTY_DATE], + [class_exists(\IntlDateFormatter::class) ? static::normalizeIcuSpaces("Jan 1, 1970, 6:00\u{202F}AM") : '1970-01-01 06:00:00', new \DateTimeImmutable('1970-01-01T06:00:00Z'), ConstraintValidator::PRETTY_DATE], + [class_exists(\IntlDateFormatter::class) ? static::normalizeIcuSpaces("Jan 1, 1970, 3:00\u{202F}PM") : '1970-01-01 15:00:00', (new \DateTimeImmutable('1970-01-01T23:00:00'))->setTimezone(new \DateTimeZone('America/New_York')), ConstraintValidator::PRETTY_DATE], ]; if (\PHP_VERSION_ID >= 80100) { @@ -61,11 +63,11 @@ public static function formatValueProvider() final class TestFormatValueConstraintValidator extends ConstraintValidator { - public function validate($value, Constraint $constraint) + public function validate($value, Constraint $constraint): void { } - public function formatValueProxy($value, $format) + public function formatValueProxy($value, int $format): string { return $this->formatValue($value, $format); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php index 628bd2534ff28..c55757902a750 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php @@ -14,12 +14,15 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\EqualTo; use Symfony\Component\Validator\Constraints\EqualToValidator; +use Symfony\Component\Validator\Tests\IcuCompatibilityTrait; /** * @author Daniel Holmes */ class EqualToValidatorTest extends AbstractComparisonValidatorTestCase { + use IcuCompatibilityTrait; + protected function createValidator() { return new EqualToValidator(); @@ -70,14 +73,14 @@ public static function provideInvalidComparisons(): array return [ [1, '1', 2, '2', 'int'], ['22', '"22"', '333', '"333"', 'string'], - [new \DateTime('2001-01-01'), 'Jan 1, 2001, 12:00 AM', new \DateTime('2000-01-01'), 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2001-01-01'), 'Jan 1, 2001, 12:00 AM', '2000-01-01', 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2001-01-01 UTC'), 'Jan 1, 2001, 12:00 AM', '2000-01-01 UTC', 'Jan 1, 2000, 12:00 AM', 'DateTime'], + [new \DateTime('2001-01-01'), self::normalizeIcuSpaces("Jan 1, 2001, 12:00\u{202F}AM"), new \DateTime('2000-01-01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2001-01-01'), self::normalizeIcuSpaces("Jan 1, 2001, 12:00\u{202F}AM"), '2000-01-01', self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2001-01-01 UTC'), self::normalizeIcuSpaces("Jan 1, 2001, 12:00\u{202F}AM"), '2000-01-01 UTC', self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], [new ComparisonTest_Class(4), '4', new ComparisonTest_Class(5), '5', __NAMESPACE__.'\ComparisonTest_Class'], ]; } - public static function provideComparisonsToNullValueAtPropertyPath() + public static function provideComparisonsToNullValueAtPropertyPath(): array { return [ [5, '5', false], diff --git a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php index fd3622e870125..367c670f0fe5a 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php @@ -14,12 +14,15 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; use Symfony\Component\Validator\Constraints\GreaterThanOrEqualValidator; +use Symfony\Component\Validator\Tests\IcuCompatibilityTrait; /** * @author Daniel Holmes */ class GreaterThanOrEqualValidatorTest extends AbstractComparisonValidatorTestCase { + use IcuCompatibilityTrait; + protected function createValidator() { return new GreaterThanOrEqualValidator(); @@ -73,14 +76,14 @@ public static function provideInvalidComparisons(): array { return [ [1, '1', 2, '2', 'int'], - [new \DateTime('2000/01/01'), 'Jan 1, 2000, 12:00 AM', new \DateTime('2005/01/01'), 'Jan 1, 2005, 12:00 AM', 'DateTime'], - [new \DateTime('2000/01/01'), 'Jan 1, 2000, 12:00 AM', '2005/01/01', 'Jan 1, 2005, 12:00 AM', 'DateTime'], - [new \DateTime('2000/01/01 UTC'), 'Jan 1, 2000, 12:00 AM', '2005/01/01 UTC', 'Jan 1, 2005, 12:00 AM', 'DateTime'], + [new \DateTime('2000/01/01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), new \DateTime('2005/01/01'), self::normalizeIcuSpaces("Jan 1, 2005, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2000/01/01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), '2005/01/01', self::normalizeIcuSpaces("Jan 1, 2005, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2000/01/01 UTC'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), '2005/01/01 UTC', self::normalizeIcuSpaces("Jan 1, 2005, 12:00\u{202F}AM"), 'DateTime'], ['b', '"b"', 'c', '"c"', 'string'], ]; } - public static function provideComparisonsToNullValueAtPropertyPath() + public static function provideComparisonsToNullValueAtPropertyPath(): array { return [ [5, '5', true], diff --git a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorTest.php index 5f68e6fe2723c..1816788b84e5c 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorTest.php @@ -14,12 +14,15 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\GreaterThan; use Symfony\Component\Validator\Constraints\GreaterThanValidator; +use Symfony\Component\Validator\Tests\IcuCompatibilityTrait; /** * @author Daniel Holmes */ class GreaterThanValidatorTest extends AbstractComparisonValidatorTestCase { + use IcuCompatibilityTrait; + protected function createValidator() { return new GreaterThanValidator(); @@ -69,12 +72,12 @@ public static function provideInvalidComparisons(): array return [ [1, '1', 2, '2', 'int'], [2, '2', 2, '2', 'int'], - [new \DateTime('2000/01/01'), 'Jan 1, 2000, 12:00 AM', new \DateTime('2005/01/01'), 'Jan 1, 2005, 12:00 AM', 'DateTime'], - [new \DateTime('2000/01/01'), 'Jan 1, 2000, 12:00 AM', new \DateTime('2000/01/01'), 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2000/01/01'), 'Jan 1, 2000, 12:00 AM', '2005/01/01', 'Jan 1, 2005, 12:00 AM', 'DateTime'], - [new \DateTime('2000/01/01'), 'Jan 1, 2000, 12:00 AM', '2000/01/01', 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2000/01/01 UTC'), 'Jan 1, 2000, 12:00 AM', '2005/01/01 UTC', 'Jan 1, 2005, 12:00 AM', 'DateTime'], - [new \DateTime('2000/01/01 UTC'), 'Jan 1, 2000, 12:00 AM', '2000/01/01 UTC', 'Jan 1, 2000, 12:00 AM', 'DateTime'], + [new \DateTime('2000/01/01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), new \DateTime('2005/01/01'), self::normalizeIcuSpaces("Jan 1, 2005, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2000/01/01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), new \DateTime('2000/01/01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2000/01/01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), '2005/01/01', self::normalizeIcuSpaces("Jan 1, 2005, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2000/01/01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), '2000/01/01', self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2000/01/01 UTC'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), '2005/01/01 UTC', self::normalizeIcuSpaces("Jan 1, 2005, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2000/01/01 UTC'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), '2000/01/01 UTC', self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], [new ComparisonTest_Class(4), '4', new ComparisonTest_Class(5), '5', __NAMESPACE__.'\ComparisonTest_Class'], [new ComparisonTest_Class(5), '5', new ComparisonTest_Class(5), '5', __NAMESPACE__.'\ComparisonTest_Class'], ['22', '"22"', '333', '"333"', 'string'], @@ -82,7 +85,7 @@ public static function provideInvalidComparisons(): array ]; } - public static function provideComparisonsToNullValueAtPropertyPath() + public static function provideComparisonsToNullValueAtPropertyPath(): array { return [ [5, '5', true], diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IdenticalToValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IdenticalToValidatorTest.php index f9cc83b515b40..165e31823f530 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IdenticalToValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IdenticalToValidatorTest.php @@ -14,12 +14,15 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\IdenticalTo; use Symfony\Component\Validator\Constraints\IdenticalToValidator; +use Symfony\Component\Validator\Tests\IcuCompatibilityTrait; /** * @author Daniel Holmes */ class IdenticalToValidatorTest extends AbstractComparisonValidatorTestCase { + use IcuCompatibilityTrait; + protected function createValidator() { return new IdenticalToValidator(); @@ -90,13 +93,13 @@ public static function provideInvalidComparisons(): array [1, '1', 2, '2', 'int'], [2, '2', '2', '"2"', 'string'], ['22', '"22"', '333', '"333"', 'string'], - [new \DateTime('2001-01-01'), 'Jan 1, 2001, 12:00 AM', new \DateTime('2001-01-01'), 'Jan 1, 2001, 12:00 AM', 'DateTime'], - [new \DateTime('2001-01-01'), 'Jan 1, 2001, 12:00 AM', new \DateTime('1999-01-01'), 'Jan 1, 1999, 12:00 AM', 'DateTime'], + [new \DateTime('2001-01-01'), self::normalizeIcuSpaces("Jan 1, 2001, 12:00\u{202F}AM"), new \DateTime('2001-01-01'), self::normalizeIcuSpaces("Jan 1, 2001, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2001-01-01'), self::normalizeIcuSpaces("Jan 1, 2001, 12:00\u{202F}AM"), new \DateTime('1999-01-01'), self::normalizeIcuSpaces("Jan 1, 1999, 12:00\u{202F}AM"), 'DateTime'], [new ComparisonTest_Class(4), '4', new ComparisonTest_Class(5), '5', __NAMESPACE__.'\ComparisonTest_Class'], ]; } - public static function provideComparisonsToNullValueAtPropertyPath() + public static function provideComparisonsToNullValueAtPropertyPath(): array { return [ [5, '5', false], diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorTest.php index 6072f6f2275e9..2526987ec3daa 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorTest.php @@ -14,12 +14,15 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\LessThanOrEqual; use Symfony\Component\Validator\Constraints\LessThanOrEqualValidator; +use Symfony\Component\Validator\Tests\IcuCompatibilityTrait; /** * @author Daniel Holmes */ class LessThanOrEqualValidatorTest extends AbstractComparisonValidatorTestCase { + use IcuCompatibilityTrait; + protected function createValidator() { return new LessThanOrEqualValidator(); @@ -75,15 +78,15 @@ public static function provideInvalidComparisons(): array { return [ [2, '2', 1, '1', 'int'], - [new \DateTime('2010-01-01'), 'Jan 1, 2010, 12:00 AM', new \DateTime('2000-01-01'), 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2010-01-01'), 'Jan 1, 2010, 12:00 AM', '2000-01-01', 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2010-01-01 UTC'), 'Jan 1, 2010, 12:00 AM', '2000-01-01 UTC', 'Jan 1, 2000, 12:00 AM', 'DateTime'], + [new \DateTime('2010-01-01'), self::normalizeIcuSpaces("Jan 1, 2010, 12:00\u{202F}AM"), new \DateTime('2000-01-01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2010-01-01'), self::normalizeIcuSpaces("Jan 1, 2010, 12:00\u{202F}AM"), '2000-01-01', self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2010-01-01 UTC'), self::normalizeIcuSpaces("Jan 1, 2010, 12:00\u{202F}AM"), '2000-01-01 UTC', self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], [new ComparisonTest_Class(5), '5', new ComparisonTest_Class(4), '4', __NAMESPACE__.'\ComparisonTest_Class'], ['c', '"c"', 'b', '"b"', 'string'], ]; } - public static function provideComparisonsToNullValueAtPropertyPath() + public static function provideComparisonsToNullValueAtPropertyPath(): array { return [ [5, '5', true], diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorTest.php index acc815a04530b..e8106b6cb01ef 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorTest.php @@ -14,12 +14,15 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\LessThan; use Symfony\Component\Validator\Constraints\LessThanValidator; +use Symfony\Component\Validator\Tests\IcuCompatibilityTrait; /** * @author Daniel Holmes */ class LessThanValidatorTest extends AbstractComparisonValidatorTestCase { + use IcuCompatibilityTrait; + protected function createValidator() { return new LessThanValidator(); @@ -69,19 +72,19 @@ public static function provideInvalidComparisons(): array return [ [3, '3', 2, '2', 'int'], [2, '2', 2, '2', 'int'], - [new \DateTime('2010-01-01'), 'Jan 1, 2010, 12:00 AM', new \DateTime('2000-01-01'), 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2000-01-01'), 'Jan 1, 2000, 12:00 AM', new \DateTime('2000-01-01'), 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2010-01-01'), 'Jan 1, 2010, 12:00 AM', '2000-01-01', 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2000-01-01'), 'Jan 1, 2000, 12:00 AM', '2000-01-01', 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2010-01-01 UTC'), 'Jan 1, 2010, 12:00 AM', '2000-01-01 UTC', 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2000-01-01 UTC'), 'Jan 1, 2000, 12:00 AM', '2000-01-01 UTC', 'Jan 1, 2000, 12:00 AM', 'DateTime'], + [new \DateTime('2010-01-01'), self::normalizeIcuSpaces("Jan 1, 2010, 12:00\u{202F}AM"), new \DateTime('2000-01-01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2000-01-01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), new \DateTime('2000-01-01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2010-01-01'), self::normalizeIcuSpaces("Jan 1, 2010, 12:00\u{202F}AM"), '2000-01-01', self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2000-01-01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), '2000-01-01', self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2010-01-01 UTC'), self::normalizeIcuSpaces("Jan 1, 2010, 12:00\u{202F}AM"), '2000-01-01 UTC', self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2000-01-01 UTC'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), '2000-01-01 UTC', self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], [new ComparisonTest_Class(5), '5', new ComparisonTest_Class(5), '5', __NAMESPACE__.'\ComparisonTest_Class'], [new ComparisonTest_Class(6), '6', new ComparisonTest_Class(5), '5', __NAMESPACE__.'\ComparisonTest_Class'], ['333', '"333"', '22', '"22"', 'string'], ]; } - public static function provideComparisonsToNullValueAtPropertyPath() + public static function provideComparisonsToNullValueAtPropertyPath(): array { return [ [5, '5', true], diff --git a/src/Symfony/Component/Validator/Tests/Constraints/NotEqualToValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/NotEqualToValidatorTest.php index 465458b07c0d4..65d4329efedd1 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/NotEqualToValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/NotEqualToValidatorTest.php @@ -14,12 +14,15 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\NotEqualTo; use Symfony\Component\Validator\Constraints\NotEqualToValidator; +use Symfony\Component\Validator\Tests\IcuCompatibilityTrait; /** * @author Daniel Holmes */ class NotEqualToValidatorTest extends AbstractComparisonValidatorTestCase { + use IcuCompatibilityTrait; + protected function createValidator() { return new NotEqualToValidator(); @@ -70,14 +73,14 @@ public static function provideInvalidComparisons(): array [3, '3', 3, '3', 'int'], ['2', '"2"', 2, '2', 'int'], ['a', '"a"', 'a', '"a"', 'string'], - [new \DateTime('2000-01-01'), 'Jan 1, 2000, 12:00 AM', new \DateTime('2000-01-01'), 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2000-01-01'), 'Jan 1, 2000, 12:00 AM', '2000-01-01', 'Jan 1, 2000, 12:00 AM', 'DateTime'], - [new \DateTime('2000-01-01 UTC'), 'Jan 1, 2000, 12:00 AM', '2000-01-01 UTC', 'Jan 1, 2000, 12:00 AM', 'DateTime'], + [new \DateTime('2000-01-01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), new \DateTime('2000-01-01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2000-01-01'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), '2000-01-01', self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], + [new \DateTime('2000-01-01 UTC'), self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), '2000-01-01 UTC', self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], [new ComparisonTest_Class(5), '5', new ComparisonTest_Class(5), '5', __NAMESPACE__.'\ComparisonTest_Class'], ]; } - public static function provideComparisonsToNullValueAtPropertyPath() + public static function provideComparisonsToNullValueAtPropertyPath(): array { return [ [5, '5', true], diff --git a/src/Symfony/Component/Validator/Tests/Constraints/NotIdenticalToValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/NotIdenticalToValidatorTest.php index 49cff99122d9d..a67aa8cb98cfb 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/NotIdenticalToValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/NotIdenticalToValidatorTest.php @@ -14,12 +14,15 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\NotIdenticalTo; use Symfony\Component\Validator\Constraints\NotIdenticalToValidator; +use Symfony\Component\Validator\Tests\IcuCompatibilityTrait; /** * @author Daniel Holmes */ class NotIdenticalToValidatorTest extends AbstractComparisonValidatorTestCase { + use IcuCompatibilityTrait; + protected function createValidator() { return new NotIdenticalToValidator(); @@ -86,17 +89,15 @@ public static function provideInvalidComparisons(): array $date = new \DateTime('2000-01-01'); $object = new ComparisonTest_Class(2); - $comparisons = [ + return [ [3, '3', 3, '3', 'int'], ['a', '"a"', 'a', '"a"', 'string'], - [$date, 'Jan 1, 2000, 12:00 AM', $date, 'Jan 1, 2000, 12:00 AM', 'DateTime'], + [$date, self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), $date, self::normalizeIcuSpaces("Jan 1, 2000, 12:00\u{202F}AM"), 'DateTime'], [$object, '2', $object, '2', __NAMESPACE__.'\ComparisonTest_Class'], ]; - - return $comparisons; } - public static function provideComparisonsToNullValueAtPropertyPath() + public static function provideComparisonsToNullValueAtPropertyPath(): array { return [ [5, '5', true], diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php index 2f7da24176cfb..83a2a3d596048 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php @@ -16,9 +16,12 @@ use Symfony\Component\Validator\Constraints\RangeValidator; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\IcuCompatibilityTrait; class RangeValidatorTest extends ConstraintValidatorTestCase { + use IcuCompatibilityTrait; + protected function createValidator() { return new RangeValidator(); @@ -31,7 +34,7 @@ public function testNullIsValid() $this->assertNoViolation(); } - public static function getTenToTwenty() + public static function getTenToTwenty(): array { return [ [10.00001], @@ -55,7 +58,7 @@ public static function getLessThanTen() ]; } - public static function getMoreThanTwenty() + public static function getMoreThanTwenty(): array { return [ [20.000001, '20.000001'], @@ -291,7 +294,7 @@ public function testInvalidValuesCombinedMinNamed($value, $formattedValue) ->assertRaised(); } - public static function getTenthToTwentiethMarch2014() + public static function getTenthToTwentiethMarch2014(): array { // The provider runs before setUp(), so we need to manually fix // the default timezone @@ -302,18 +305,17 @@ public static function getTenthToTwentiethMarch2014() [new \DateTime('March 10, 2014')], [new \DateTime('March 15, 2014')], [new \DateTime('March 20, 2014')], + [new \DateTimeImmutable('March 10, 2014')], + [new \DateTimeImmutable('March 15, 2014')], + [new \DateTimeImmutable('March 20, 2014')], ]; - $tests[] = [new \DateTimeImmutable('March 10, 2014')]; - $tests[] = [new \DateTimeImmutable('March 15, 2014')]; - $tests[] = [new \DateTimeImmutable('March 20, 2014')]; - date_default_timezone_set($timezone); return $tests; } - public static function getSoonerThanTenthMarch2014() + public static function getSoonerThanTenthMarch2014(): array { // The provider runs before setUp(), so we need to manually fix // the default timezone @@ -321,19 +323,18 @@ public static function getSoonerThanTenthMarch2014() date_default_timezone_set('UTC'); $tests = [ - [new \DateTime('March 20, 2013'), 'Mar 20, 2013, 12:00 AM'], - [new \DateTime('March 9, 2014'), 'Mar 9, 2014, 12:00 AM'], + [new \DateTime('March 20, 2013'), self::normalizeIcuSpaces("Mar 20, 2013, 12:00\u{202F}AM")], + [new \DateTime('March 9, 2014'), self::normalizeIcuSpaces("Mar 9, 2014, 12:00\u{202F}AM")], + [new \DateTimeImmutable('March 20, 2013'), self::normalizeIcuSpaces("Mar 20, 2013, 12:00\u{202F}AM")], + [new \DateTimeImmutable('March 9, 2014'), self::normalizeIcuSpaces("Mar 9, 2014, 12:00\u{202F}AM")], ]; - $tests[] = [new \DateTimeImmutable('March 20, 2013'), 'Mar 20, 2013, 12:00 AM']; - $tests[] = [new \DateTimeImmutable('March 9, 2014'), 'Mar 9, 2014, 12:00 AM']; - date_default_timezone_set($timezone); return $tests; } - public static function getLaterThanTwentiethMarch2014() + public static function getLaterThanTwentiethMarch2014(): array { // The provider runs before setUp(), so we need to manually fix // the default timezone @@ -341,13 +342,12 @@ public static function getLaterThanTwentiethMarch2014() date_default_timezone_set('UTC'); $tests = [ - [new \DateTime('March 21, 2014'), 'Mar 21, 2014, 12:00 AM'], - [new \DateTime('March 9, 2015'), 'Mar 9, 2015, 12:00 AM'], + [new \DateTime('March 21, 2014'), self::normalizeIcuSpaces("Mar 21, 2014, 12:00\u{202F}AM")], + [new \DateTime('March 9, 2015'), self::normalizeIcuSpaces("Mar 9, 2015, 12:00\u{202F}AM")], + [new \DateTimeImmutable('March 21, 2014'), self::normalizeIcuSpaces("Mar 21, 2014, 12:00\u{202F}AM")], + [new \DateTimeImmutable('March 9, 2015'), self::normalizeIcuSpaces("Mar 9, 2015, 12:00\u{202F}AM")], ]; - $tests[] = [new \DateTimeImmutable('March 21, 2014'), 'Mar 21, 2014, 12:00 AM']; - $tests[] = [new \DateTimeImmutable('March 9, 2015'), 'Mar 9, 2015, 12:00 AM']; - date_default_timezone_set($timezone); return $tests; @@ -389,7 +389,7 @@ public function testValidDatesMinMax($value) /** * @dataProvider getSoonerThanTenthMarch2014 */ - public function testInvalidDatesMin($value, $dateTimeAsString) + public function testInvalidDatesMin(\DateTimeInterface $value, string $dateTimeAsString) { // Conversion of dates to string differs between ICU versions // Make sure we have the correct version loaded @@ -404,7 +404,7 @@ public function testInvalidDatesMin($value, $dateTimeAsString) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $dateTimeAsString) - ->setParameter('{{ limit }}', 'Mar 10, 2014, 12:00 AM') + ->setParameter('{{ limit }}', self::normalizeIcuSpaces("Mar 10, 2014, 12:00\u{202F}AM")) ->setCode(Range::TOO_LOW_ERROR) ->assertRaised(); } @@ -412,7 +412,7 @@ public function testInvalidDatesMin($value, $dateTimeAsString) /** * @dataProvider getLaterThanTwentiethMarch2014 */ - public function testInvalidDatesMax($value, $dateTimeAsString) + public function testInvalidDatesMax(\DateTimeInterface $value, string $dateTimeAsString) { // Conversion of dates to string differs between ICU versions // Make sure we have the correct version loaded @@ -427,7 +427,7 @@ public function testInvalidDatesMax($value, $dateTimeAsString) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $dateTimeAsString) - ->setParameter('{{ limit }}', 'Mar 20, 2014, 12:00 AM') + ->setParameter('{{ limit }}', self::normalizeIcuSpaces("Mar 20, 2014, 12:00\u{202F}AM")) ->setCode(Range::TOO_HIGH_ERROR) ->assertRaised(); } @@ -435,7 +435,7 @@ public function testInvalidDatesMax($value, $dateTimeAsString) /** * @dataProvider getLaterThanTwentiethMarch2014 */ - public function testInvalidDatesCombinedMax($value, $dateTimeAsString) + public function testInvalidDatesCombinedMax(\DateTimeInterface $value, string $dateTimeAsString) { // Conversion of dates to string differs between ICU versions // Make sure we have the correct version loaded @@ -451,8 +451,8 @@ public function testInvalidDatesCombinedMax($value, $dateTimeAsString) $this->buildViolation('myNotInRangeMessage') ->setParameter('{{ value }}', $dateTimeAsString) - ->setParameter('{{ min }}', 'Mar 10, 2014, 12:00 AM') - ->setParameter('{{ max }}', 'Mar 20, 2014, 12:00 AM') + ->setParameter('{{ min }}', self::normalizeIcuSpaces("Mar 10, 2014, 12:00\u{202F}AM")) + ->setParameter('{{ max }}', self::normalizeIcuSpaces("Mar 20, 2014, 12:00\u{202F}AM")) ->setCode(Range::NOT_IN_RANGE_ERROR) ->assertRaised(); } @@ -476,13 +476,13 @@ public function testInvalidDatesCombinedMin($value, $dateTimeAsString) $this->buildViolation('myNotInRangeMessage') ->setParameter('{{ value }}', $dateTimeAsString) - ->setParameter('{{ min }}', 'Mar 10, 2014, 12:00 AM') - ->setParameter('{{ max }}', 'Mar 20, 2014, 12:00 AM') + ->setParameter('{{ min }}', self::normalizeIcuSpaces("Mar 10, 2014, 12:00\u{202F}AM")) + ->setParameter('{{ max }}', self::normalizeIcuSpaces("Mar 20, 2014, 12:00\u{202F}AM")) ->setCode(Range::NOT_IN_RANGE_ERROR) ->assertRaised(); } - public function getInvalidValues() + public function getInvalidValues(): array { return [ [9.999999], @@ -952,7 +952,7 @@ public function testInvalidDatesMinPropertyPath($value, $dateTimeAsString) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $dateTimeAsString) - ->setParameter('{{ limit }}', 'Mar 10, 2014, 12:00 AM') + ->setParameter('{{ limit }}', self::normalizeIcuSpaces("Mar 10, 2014, 12:00\u{202F}AM")) ->setParameter('{{ min_limit_path }}', 'value') ->setCode(Range::TOO_LOW_ERROR) ->assertRaised(); @@ -978,7 +978,7 @@ public function testInvalidDatesMaxPropertyPath($value, $dateTimeAsString) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $dateTimeAsString) - ->setParameter('{{ limit }}', 'Mar 20, 2014, 12:00 AM') + ->setParameter('{{ limit }}', self::normalizeIcuSpaces("Mar 20, 2014, 12:00\u{202F}AM")) ->setParameter('{{ max_limit_path }}', 'value') ->setCode(Range::TOO_HIGH_ERROR) ->assertRaised(); @@ -1005,8 +1005,8 @@ public function testInvalidDatesCombinedMaxPropertyPath($value, $dateTimeAsStrin $this->buildViolation('myNotInRangeMessage') ->setParameter('{{ value }}', $dateTimeAsString) - ->setParameter('{{ min }}', 'Mar 10, 2014, 12:00 AM') - ->setParameter('{{ max }}', 'Mar 20, 2014, 12:00 AM') + ->setParameter('{{ min }}', self::normalizeIcuSpaces("Mar 10, 2014, 12:00\u{202F}AM")) + ->setParameter('{{ max }}', self::normalizeIcuSpaces("Mar 20, 2014, 12:00\u{202F}AM")) ->setParameter('{{ max_limit_path }}', 'max') ->setParameter('{{ min_limit_path }}', 'min') ->setCode(Range::NOT_IN_RANGE_ERROR) @@ -1034,8 +1034,8 @@ public function testInvalidDatesCombinedMinPropertyPath($value, $dateTimeAsStrin $this->buildViolation('myNotInRangeMessage') ->setParameter('{{ value }}', $dateTimeAsString) - ->setParameter('{{ min }}', 'Mar 10, 2014, 12:00 AM') - ->setParameter('{{ max }}', 'Mar 20, 2014, 12:00 AM') + ->setParameter('{{ min }}', self::normalizeIcuSpaces("Mar 10, 2014, 12:00\u{202F}AM")) + ->setParameter('{{ max }}', self::normalizeIcuSpaces("Mar 20, 2014, 12:00\u{202F}AM")) ->setParameter('{{ max_limit_path }}', 'max') ->setParameter('{{ min_limit_path }}', 'min') ->setCode(Range::NOT_IN_RANGE_ERROR) diff --git a/src/Symfony/Component/Validator/Tests/IcuCompatibilityTrait.php b/src/Symfony/Component/Validator/Tests/IcuCompatibilityTrait.php new file mode 100644 index 0000000000000..bdd83feb9a309 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/IcuCompatibilityTrait.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests; + +trait IcuCompatibilityTrait +{ + /** + * Normalized spaces in date strings generated by INTL for older ICU versions. + * + * In version 72.1, ICU started to render a narrow non-breaking space (NNBSP) into localized time strings. This + * method allows us to write expectations in a forward-compatible manner. + */ + private static function normalizeIcuSpaces(string $input): string + { + if (\defined('INTL_ICU_VERSION') && version_compare(\INTL_ICU_VERSION, '72.1', '>=')) { + return $input; + } + + return str_replace("\u{202F}", ' ', $input); + } +} From 4f620a1c763bc4073a9e997584e22d91de6d5643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Podhorsk=C3=BD?= Date: Fri, 24 Nov 2023 23:39:32 +0100 Subject: [PATCH 02/19] [String] Fix Inflector for 'icon' --- src/Symfony/Component/String/Inflector/EnglishInflector.php | 3 +++ .../Component/String/Tests/Inflector/EnglishInflectorTest.php | 1 + 2 files changed, 4 insertions(+) diff --git a/src/Symfony/Component/String/Inflector/EnglishInflector.php b/src/Symfony/Component/String/Inflector/EnglishInflector.php index 16efc53a82234..e1bcd87b2ce65 100644 --- a/src/Symfony/Component/String/Inflector/EnglishInflector.php +++ b/src/Symfony/Component/String/Inflector/EnglishInflector.php @@ -253,6 +253,9 @@ final class EnglishInflector implements InflectorInterface // seasons (season), treasons (treason), poisons (poison), lessons (lesson) ['nos', 3, true, true, 'sons'], + // icons (icon) + ['noc', 3, true, true, 'cons'], + // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) ['no', 2, true, true, 'a'], diff --git a/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php b/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php index 6c7a1c7c6e895..cf66bf05b660c 100644 --- a/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php +++ b/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php @@ -295,6 +295,7 @@ public static function pluralizeProvider() ['tree', 'trees'], ['waltz', 'waltzes'], ['wife', 'wives'], + ['icon', 'icons'], // test casing: if the first letter was uppercase, it should remain so ['Man', 'Men'], From fd85bf46463160d338bc2b3f5a3d106e517029e2 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 26 Nov 2023 19:07:00 +0100 Subject: [PATCH 03/19] Bump Symfony version to 6.4.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 9ebfac110e959..7825b57cf2148 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.4.0-RC2'; + public const VERSION = '6.4.0-DEV'; public const VERSION_ID = 60400; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 4; public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = 'RC2'; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '11/2026'; public const END_OF_LIFE = '11/2027'; From 0182b9f5abc85b8789074581c1c6316dc0b3d034 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 26 Nov 2023 22:19:03 +0100 Subject: [PATCH 04/19] Document BC break with $secret parameter introduction --- UPGRADE-6.4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADE-6.4.md b/UPGRADE-6.4.md index 182d98cce2f16..be510f1228668 100644 --- a/UPGRADE-6.4.md +++ b/UPGRADE-6.4.md @@ -198,7 +198,7 @@ Security * [BC break] `UserValueResolver` no longer implements `ArgumentValueResolverInterface` * [BC break] Make `PersistentToken` immutable * Deprecate accepting only `DateTime` for `TokenProviderInterface::updateToken()`, use `DateTimeInterface` instead - * Deprecate calling the constructor of `DefaultLoginRateLimiter` with an empty secret + * [BC break] Added required `string $secret` parameter to the constructor of `DefaultLoginRateLimiter` SecurityBundle -------------- From d625fa0c5cf344198c05ada94a52db62c6a866bd Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 27 Nov 2023 12:12:09 +0100 Subject: [PATCH 05/19] fix typo --- UPGRADE-6.4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADE-6.4.md b/UPGRADE-6.4.md index be510f1228668..36b4ea046fc4c 100644 --- a/UPGRADE-6.4.md +++ b/UPGRADE-6.4.md @@ -198,7 +198,7 @@ Security * [BC break] `UserValueResolver` no longer implements `ArgumentValueResolverInterface` * [BC break] Make `PersistentToken` immutable * Deprecate accepting only `DateTime` for `TokenProviderInterface::updateToken()`, use `DateTimeInterface` instead - * [BC break] Added required `string $secret` parameter to the constructor of `DefaultLoginRateLimiter` + * [BC break] Add required `string $secret` parameter to the constructor of `DefaultLoginRateLimiter` SecurityBundle -------------- From bbe8848829f823fc98e344e8aef6b238b28e3d38 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 27 Nov 2023 16:59:28 +0100 Subject: [PATCH 06/19] [VarExporter] Fix serializing objects that implement __sleep() and that are made lazy --- src/Symfony/Component/VarExporter/LazyGhostTrait.php | 2 +- src/Symfony/Component/VarExporter/LazyProxyTrait.php | 2 +- .../Component/VarExporter/Tests/LazyGhostTraitTest.php | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/VarExporter/LazyGhostTrait.php b/src/Symfony/Component/VarExporter/LazyGhostTrait.php index 13e33f59c9bd8..7c3a60507cc15 100644 --- a/src/Symfony/Component/VarExporter/LazyGhostTrait.php +++ b/src/Symfony/Component/VarExporter/LazyGhostTrait.php @@ -355,7 +355,7 @@ public function __serialize(): array $data = []; foreach (parent::__sleep() as $name) { - $value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0$scope\0$name"] ?? $k = null; + $value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0$class\0$name"] ?? $properties[$k = "\0$scope\0$name"] ?? $k = null; if (null === $k) { trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $name), \E_USER_NOTICE); diff --git a/src/Symfony/Component/VarExporter/LazyProxyTrait.php b/src/Symfony/Component/VarExporter/LazyProxyTrait.php index 153c3820844b5..d683ec3f1259d 100644 --- a/src/Symfony/Component/VarExporter/LazyProxyTrait.php +++ b/src/Symfony/Component/VarExporter/LazyProxyTrait.php @@ -295,7 +295,7 @@ public function __serialize(): array $data = []; foreach (parent::__sleep() as $name) { - $value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0$scope\0$name"] ?? $k = null; + $value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0$class\0$name"] ?? $properties[$k = "\0$scope\0$name"] ?? $k = null; if (null === $k) { trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $name), \E_USER_NOTICE); diff --git a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php index 0cbbd835b8f64..52a6fe3f6ca8c 100644 --- a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php +++ b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php @@ -146,7 +146,13 @@ public function testMagicClass(MagicClass $instance) $instance->bar = 123; $serialized = serialize($instance); $clone = unserialize($serialized); - $this->assertSame(123, $clone->bar); + + if ($instance instanceof ChildMagicClass) { + // ChildMagicClass redefines the $data property but not the __sleep() method + $this->assertFalse(isset($clone->bar)); + } else { + $this->assertSame(123, $clone->bar); + } } public static function provideMagicClass() From 4002efb72ebdd14b54174e6caefc24fc68b8da00 Mon Sep 17 00:00:00 2001 From: Ivan Sarastov Date: Mon, 27 Nov 2023 19:14:29 +0200 Subject: [PATCH 07/19] [Validator] Add missing translations for Bulgarian #51931 --- .../Resources/translations/validators.bg.xlf | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf index 455ff81679a1b..d9efdf5751fe1 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.bg.xlf @@ -402,6 +402,30 @@ The value of the netmask should be between {{ min }} and {{ max }}. Стойността на мрежовата маска трябва да бъде между {{ min }} и {{ max }}. + + The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less. + Името на файла е твърде дълго. Трябва да съдържа не повече от {{ filename_max_length }} символ.|Името на файла е твърде дълго. Трябва да съдържа не повече от {{ filename_max_length }} символа. + + + The password strength is too low. Please use a stronger password. + Сложността на паролата е твърде малка. Моля използвайте по-сложна парола. + + + This value contains characters that are not allowed by the current restriction-level. + Стойността съдържа символи, които не са позволени от текущото ниво на ограничение. + + + Using invisible characters is not allowed. + Използването на невидими символи не е позволено. + + + Mixing numbers from different scripts is not allowed. + Смесването на числа от различни скриптове не е позволено. + + + Using hidden overlay characters is not allowed. + Използването на скрити насложени символи не е позволено. + From b051700a8446a787c455d3f61bb4a1cee2c85429 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 27 Nov 2023 16:46:08 +0100 Subject: [PATCH 08/19] [VarExporter] Work around php/php-src#12695 for lazy objects, fixing nullsafe-related behavior --- .../VarExporter/Internal/Hydrator.php | 8 +++--- .../VarExporter/Internal/LazyObjectState.php | 19 ++++++++------ .../Component/VarExporter/LazyGhostTrait.php | 25 ++++++++++++++----- .../Component/VarExporter/ProxyHelper.php | 3 +++ .../VarExporter/Tests/LazyGhostTraitTest.php | 4 ++- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/Symfony/Component/VarExporter/Internal/Hydrator.php b/src/Symfony/Component/VarExporter/Internal/Hydrator.php index f665f6ee15c6e..ebc5914326386 100644 --- a/src/Symfony/Component/VarExporter/Internal/Hydrator.php +++ b/src/Symfony/Component/VarExporter/Internal/Hydrator.php @@ -271,10 +271,10 @@ public static function getPropertyScopes($class) $name = $property->name; if (\ReflectionProperty::IS_PRIVATE & $flags) { - $propertyScopes["\0$class\0$name"] = $propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $class : null]; + $propertyScopes["\0$class\0$name"] = $propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $class : null, $property]; continue; } - $propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $property->class : null]; + $propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $property->class : null, $property]; if (\ReflectionProperty::IS_PROTECTED & $flags) { $propertyScopes["\0*\0$name"] = $propertyScopes[$name]; @@ -288,8 +288,8 @@ public static function getPropertyScopes($class) if (!$property->isStatic()) { $name = $property->name; $readonlyScope = $property->isReadOnly() ? $class : null; - $propertyScopes["\0$class\0$name"] = [$class, $name, $readonlyScope]; - $propertyScopes[$name] ??= [$class, $name, $readonlyScope]; + $propertyScopes["\0$class\0$name"] = [$class, $name, $readonlyScope, $property]; + $propertyScopes[$name] ??= [$class, $name, $readonlyScope, $property]; } } } diff --git a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php index 2f649dd1ca481..f47dea4d8e6f5 100644 --- a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php +++ b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php @@ -65,29 +65,32 @@ public function initialize($instance, $propertyName, $propertyScope) return $this->status = self::STATUS_INITIALIZED_PARTIAL; } - $status = self::STATUS_UNINITIALIZED_PARTIAL; - if ($initializer = $this->initializer["\0"] ?? null) { if (!\is_array($values = $initializer($instance, LazyObjectRegistry::$defaultProperties[$class]))) { throw new \TypeError(sprintf('The lazy-initializer defined for instance of "%s" must return an array, got "%s".', $class, get_debug_type($values))); } $properties = (array) $instance; foreach ($values as $key => $value) { - if ($k === $key) { - $status = self::STATUS_INITIALIZED_PARTIAL; - } if (!\array_key_exists($key, $properties) && [$scope, $name, $readonlyScope] = $propertyScopes[$key] ?? null) { $scope = $readonlyScope ?? ('*' !== $scope ? $scope : $class); $accessor = LazyObjectRegistry::$classAccessors[$scope] ??= LazyObjectRegistry::getClassAccessors($scope); $accessor['set']($instance, $name, $value); + + if ($k === $key) { + $this->status = self::STATUS_INITIALIZED_PARTIAL; + } } } } - return $status; + return $this->status; + } + + if (self::STATUS_INITIALIZED_PARTIAL === $this->status) { + return self::STATUS_INITIALIZED_PARTIAL; } - $this->status = self::STATUS_INITIALIZED_FULL; + $this->status = self::STATUS_INITIALIZED_PARTIAL; try { if ($defaultProperties = array_diff_key(LazyObjectRegistry::$defaultProperties[$instance::class], $this->skippedProperties)) { @@ -102,7 +105,7 @@ public function initialize($instance, $propertyName, $propertyScope) throw $e; } - return self::STATUS_INITIALIZED_FULL; + return $this->status = self::STATUS_INITIALIZED_FULL; } public function reset($instance): void diff --git a/src/Symfony/Component/VarExporter/LazyGhostTrait.php b/src/Symfony/Component/VarExporter/LazyGhostTrait.php index 13e33f59c9bd8..b0fc3b1cbd3bc 100644 --- a/src/Symfony/Component/VarExporter/LazyGhostTrait.php +++ b/src/Symfony/Component/VarExporter/LazyGhostTrait.php @@ -172,10 +172,18 @@ public function &__get($name): mixed $scope = Registry::getScope($propertyScopes, $class, $name); $state = $this->lazyObjectState ?? null; - if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"])) - && LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $readonlyScope ?? $scope) - ) { - goto get_in_scope; + if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))) { + if (LazyObjectState::STATUS_INITIALIZED_FULL === $state->status) { + // Work around php/php-src#12695 + $property = $propertyScopes[null === $scope ? $name : "\0$scope\0$name"][3] + ?? (Hydrator::$propertyScopes[$this::class] = Hydrator::getPropertyScopes($this::class))[3]; + } else { + $property = null; + } + + if ($property?->isInitialized($this) ?? LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $readonlyScope ?? $scope)) { + goto get_in_scope; + } } } @@ -237,7 +245,9 @@ public function __set($name, $value): void $scope = Registry::getScope($propertyScopes, $class, $name, $readonlyScope); $state = $this->lazyObjectState ?? null; - if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"]))) { + if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"])) + && LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status + ) { if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state->status) { $state->initialize($this, $name, $readonlyScope ?? $scope); } @@ -271,6 +281,7 @@ public function __isset($name): bool $state = $this->lazyObjectState ?? null; if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"])) + && LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status && LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $readonlyScope ?? $scope) ) { goto isset_in_scope; @@ -300,7 +311,9 @@ public function __unset($name): void $scope = Registry::getScope($propertyScopes, $class, $name, $readonlyScope); $state = $this->lazyObjectState ?? null; - if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"]))) { + if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"])) + && LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status + ) { if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state->status) { $state->initialize($this, $name, $readonlyScope ?? $scope); } diff --git a/src/Symfony/Component/VarExporter/ProxyHelper.php b/src/Symfony/Component/VarExporter/ProxyHelper.php index 155715de662c9..71530fd55d7f8 100644 --- a/src/Symfony/Component/VarExporter/ProxyHelper.php +++ b/src/Symfony/Component/VarExporter/ProxyHelper.php @@ -322,6 +322,9 @@ private static function exportPropertyScopes(string $parent): string { $propertyScopes = Hydrator::$propertyScopes[$parent] ??= Hydrator::getPropertyScopes($parent); uksort($propertyScopes, 'strnatcmp'); + foreach ($propertyScopes as $k => $v) { + unset($propertyScopes[$k][3]); + } $propertyScopes = VarExporter::export($propertyScopes); $propertyScopes = str_replace(VarExporter::export($parent), 'parent::class', $propertyScopes); $propertyScopes = preg_replace("/(?|(,)\n( ) |\n |,\n (\]))/", '$1$2', $propertyScopes); diff --git a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php index 0cbbd835b8f64..83659935d5182 100644 --- a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php +++ b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php @@ -65,8 +65,10 @@ public function testUnsetPublic() $this->assertSame(["\0".TestClass::class."\0lazyObjectState"], array_keys((array) $instance)); unset($instance->public); - $this->assertFalse(isset($instance->public)); $this->assertSame(4, $instance->publicReadonly); + $this->expectException(\BadMethodCallException::class); + $this->expectExceptionMessage('__isset(public)'); + isset($instance->public); } public function testSetPublic() From b7d0b69455b4bd40c008884f3afff41ea1e1f956 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 27 Nov 2023 22:04:45 +0100 Subject: [PATCH 09/19] [Translation] Remove `@internal` from abstract testcases --- .../Component/Translation/Test/ProviderFactoryTestCase.php | 2 -- src/Symfony/Component/Translation/Test/ProviderTestCase.php | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/Symfony/Component/Translation/Test/ProviderFactoryTestCase.php b/src/Symfony/Component/Translation/Test/ProviderFactoryTestCase.php index a9c7c0ebf363d..110ea3d7a49f6 100644 --- a/src/Symfony/Component/Translation/Test/ProviderFactoryTestCase.php +++ b/src/Symfony/Component/Translation/Test/ProviderFactoryTestCase.php @@ -28,8 +28,6 @@ * A test case to ease testing a translation provider factory. * * @author Mathieu Santostefano - * - * @internal */ abstract class ProviderFactoryTestCase extends TestCase { diff --git a/src/Symfony/Component/Translation/Test/ProviderTestCase.php b/src/Symfony/Component/Translation/Test/ProviderTestCase.php index 2d16f2bfc77b8..a8fa0b8bbcbac 100644 --- a/src/Symfony/Component/Translation/Test/ProviderTestCase.php +++ b/src/Symfony/Component/Translation/Test/ProviderTestCase.php @@ -25,8 +25,6 @@ * A test case to ease testing a translation provider. * * @author Mathieu Santostefano - * - * @internal */ abstract class ProviderTestCase extends TestCase { From 084f8d1e093bba49eb517391a02938036d67dc40 Mon Sep 17 00:00:00 2001 From: Evert Harmeling Date: Tue, 28 Nov 2023 14:20:50 +0100 Subject: [PATCH 10/19] Minor @var doc update --- .../Component/AssetMapper/ImportMap/ImportMapAuditor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapAuditor.php b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapAuditor.php index 1597884b215bf..112e68906dfd7 100644 --- a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapAuditor.php +++ b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapAuditor.php @@ -35,7 +35,7 @@ public function audit(): array { $entries = $this->configReader->getEntries(); - /** @var array> $installed */ + /** @var array $packageAudits */ $packageAudits = []; /** @var array> $installed */ From ca1675165854f0a8febc41c8551c905b7b987488 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Tue, 28 Nov 2023 09:07:59 +0100 Subject: [PATCH 11/19] [Serializer] Fix normalization relying on allowed attributes only --- .../Normalizer/AbstractObjectNormalizer.php | 2 +- .../AbstractObjectNormalizerTest.php | 34 +++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 25f7853762385..b7bf2e15d5bc2 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -318,7 +318,7 @@ protected function getAttributes(object $object, ?string $format, array $context $allowedAttributes = $this->getAllowedAttributes($object, $context, true); if (false !== $allowedAttributes) { - $attributes = array_intersect($attributes, $allowedAttributes); + $attributes = $attributes ? array_intersect($attributes, $allowedAttributes) : $allowedAttributes; } if ($context['cache_key'] && \stdClass::class !== $class) { diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index ad89dcbcd7896..cf33e8e604848 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -458,9 +458,39 @@ public function testNormalizeEmptyObject() public function testNormalizeWithIgnoreAnnotationAndPrivateProperties() { $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); - $serializer = new Serializer([new ObjectNormalizer($classMetadataFactory)]); + $normalizer = new ObjectNormalizer($classMetadataFactory); - $this->assertSame(['foo' => 'foo'], $serializer->normalize(new ObjectDummyWithIgnoreAnnotationAndPrivateProperty())); + $this->assertSame(['foo' => 'foo'], $normalizer->normalize(new ObjectDummyWithIgnoreAnnotationAndPrivateProperty())); + } + + public function testNormalizeBasedOnAllowedAttributes() + { + $normalizer = new class() extends AbstractObjectNormalizer { + protected function getAllowedAttributes($classOrObject, array $context, bool $attributesAsString = false) + { + return ['foo']; + } + + protected function extractAttributes(object $object, string $format = null, array $context = []): array + { + return []; + } + + protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = []) + { + return $object->$attribute; + } + + protected function setAttributeValue(object $object, string $attribute, $value, string $format = null, array $context = []) + { + } + }; + + $object = new Dummy(); + $object->foo = 'foo'; + $object->bar = 'bar'; + + $this->assertSame(['foo' => 'foo'], $normalizer->normalize($object)); } /** From 02dba6d4a51e65b47c852b9498a20d639f51572e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 28 Nov 2023 21:55:58 +0100 Subject: [PATCH 12/19] [HttpClient] Add Innovative Web AG (i-web) as sponsor of version 6.4/7.0 --- src/Symfony/Component/HttpClient/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Symfony/Component/HttpClient/README.md b/src/Symfony/Component/HttpClient/README.md index 214489b7e7f76..04e15967f64ca 100644 --- a/src/Symfony/Component/HttpClient/README.md +++ b/src/Symfony/Component/HttpClient/README.md @@ -3,6 +3,18 @@ HttpClient component The HttpClient component provides powerful methods to fetch HTTP resources synchronously or asynchronously. +Sponsor +------- + +The HttpClient component for Symfony 6.4 is [backed][1] by [Innovative Web AG][2]. + +Innovative Web AG (i-web) is a specialist for web, applications and the +digitalisation of the public sector based in Switzerland. With their i-CMS, +public authorities and institutions implement modern websites and eGovernment +portals and offer user-friendly eServices for residents and companies. + +Help Symfony by [sponsoring][3] its development! + Resources --------- @@ -11,3 +23,7 @@ Resources * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) + +[1]: https://symfony.com/backers +[2]: https://www.i-web.ch +[3]: https://symfony.com/sponsor From 210b371de5069fdf01ec72462ec7e85571ebaa2c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 29 Nov 2023 07:58:28 +0100 Subject: [PATCH 13/19] don't check parameter values if they are not set --- .../Compiler/CheckTypeDeclarationsPass.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php index e69d56fb16265..867a4a22412f2 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php @@ -139,11 +139,17 @@ private function checkTypeDeclarations(Definition $checkedDefinition, \Reflectio $envPlaceholderUniquePrefix = $this->container->getParameterBag() instanceof EnvPlaceholderParameterBag ? $this->container->getParameterBag()->getEnvPlaceholderUniquePrefix() : null; for ($i = 0; $i < $checksCount; ++$i) { - if (!$reflectionParameters[$i]->hasType() || $reflectionParameters[$i]->isVariadic()) { + $p = $reflectionParameters[$i]; + if (!$p->hasType() || $p->isVariadic()) { + continue; + } + if (\array_key_exists($p->name, $values)) { + $i = $p->name; + } elseif (!\array_key_exists($i, $values)) { continue; } - $this->checkType($checkedDefinition, $values[$i], $reflectionParameters[$i], $envPlaceholderUniquePrefix); + $this->checkType($checkedDefinition, $values[$i], $p, $envPlaceholderUniquePrefix); } if ($reflectionFunction->isVariadic() && ($lastParameter = end($reflectionParameters))->hasType()) { From 7dc4abcf3e7f2a8ae93e9e146e9aa99f0cf65835 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 29 Nov 2023 09:02:20 +0100 Subject: [PATCH 14/19] [Routing] Add redirection.io as sponsor of versions 6.4/7.0/7.1 --- src/Symfony/Component/Routing/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Symfony/Component/Routing/README.md b/src/Symfony/Component/Routing/README.md index ae8284f541faa..fe91c63257327 100644 --- a/src/Symfony/Component/Routing/README.md +++ b/src/Symfony/Component/Routing/README.md @@ -41,6 +41,17 @@ $url = $generator->generate('blog_show', [ // $url = '/blog/my-blog-post' ``` +Sponsor +------- + +The Routing component for Symfony 6.4 is [backed][1] by [redirection.io][2]. + +redirection.io logs all your website’s HTTP traffic, and lets you fix errors +with redirect rules in seconds. Give your marketing, SEO and IT teams the +right tool to manage your website traffic efficiently! + +Help Symfony by [sponsoring][3] its development! + Resources --------- @@ -49,3 +60,7 @@ Resources * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) + +[1]: https://symfony.com/backers +[2]: https://redirection.io +[3]: https://symfony.com/sponsor From f49dcf52d0ec559491e8dd569aab434215c4e563 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Sun, 26 Nov 2023 21:33:54 +0100 Subject: [PATCH 15/19] [Translation] Improve tests coverage --- .../Formatter/MessageFormatter.php | 6 +- .../Catalogue/AbstractOperationTestCase.php | 11 +++ .../TranslationDataCollectorTest.php | 20 ++++++ .../Tests/Exception/ProviderExceptionTest.php | 6 +- .../TranslationProviderCollectionTest.php | 71 +++++++++++++++++++ .../Tests/Writer/TranslationWriterTest.php | 31 ++++++++ 6 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 src/Symfony/Component/Translation/Tests/Provider/TranslationProviderCollectionTest.php diff --git a/src/Symfony/Component/Translation/Formatter/MessageFormatter.php b/src/Symfony/Component/Translation/Formatter/MessageFormatter.php index 29ad574ee12e3..5e101aa438c19 100644 --- a/src/Symfony/Component/Translation/Formatter/MessageFormatter.php +++ b/src/Symfony/Component/Translation/Formatter/MessageFormatter.php @@ -36,11 +36,7 @@ public function __construct(TranslatorInterface $translator = null, IntlFormatte public function format(string $message, string $locale, array $parameters = []): string { - if ($this->translator instanceof TranslatorInterface) { - return $this->translator->trans($message, $parameters, null, $locale); - } - - return strtr($message, $parameters); + return $this->translator->trans($message, $parameters, null, $locale); } public function formatIntl(string $message, string $locale, array $parameters = []): string diff --git a/src/Symfony/Component/Translation/Tests/Catalogue/AbstractOperationTestCase.php b/src/Symfony/Component/Translation/Tests/Catalogue/AbstractOperationTestCase.php index d6b67d27b53c8..6caee0f6e5b8d 100644 --- a/src/Symfony/Component/Translation/Tests/Catalogue/AbstractOperationTestCase.php +++ b/src/Symfony/Component/Translation/Tests/Catalogue/AbstractOperationTestCase.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Translation\Tests\Catalogue; use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Exception\LogicException; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\MessageCatalogueInterface; @@ -70,5 +71,15 @@ public function testGetEmptyResult() ); } + public function testSourceAndTargetWithDifferentLocales() + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Operated catalogues must belong to the same locale.'); + $this->createOperation( + new MessageCatalogue('en'), + new MessageCatalogue('fr') + ); + } + abstract protected function createOperation(MessageCatalogueInterface $source, MessageCatalogueInterface $target); } diff --git a/src/Symfony/Component/Translation/Tests/DataCollector/TranslationDataCollectorTest.php b/src/Symfony/Component/Translation/Tests/DataCollector/TranslationDataCollectorTest.php index 8992569b01d81..c6e0cafdc166c 100644 --- a/src/Symfony/Component/Translation/Tests/DataCollector/TranslationDataCollectorTest.php +++ b/src/Symfony/Component/Translation/Tests/DataCollector/TranslationDataCollectorTest.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Translation\Tests\DataCollector; use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Translation\DataCollector\TranslationDataCollector; use Symfony\Component\Translation\DataCollectorTranslator; @@ -130,6 +132,24 @@ public function testCollect() $this->assertEquals($expectedMessages, array_values($dataCollector->getMessages()->getValue(true))); } + public function testCollectAndReset() + { + $translator = $this->getTranslator(); + $translator->method('getLocale')->willReturn('fr'); + $translator->method('getFallbackLocales')->willReturn(['en']); + + $dataCollector = new TranslationDataCollector($translator); + $dataCollector->collect($this->createMock(Request::class), $this->createMock(Response::class)); + + $this->assertSame('fr', $dataCollector->getLocale()); + $this->assertSame(['en'], $dataCollector->getFallbackLocales()); + + $dataCollector->reset(); + + $this->assertNull($dataCollector->getLocale()); + $this->assertEmpty($dataCollector->getFallbackLocales()); + } + private function getTranslator() { $translator = $this diff --git a/src/Symfony/Component/Translation/Tests/Exception/ProviderExceptionTest.php b/src/Symfony/Component/Translation/Tests/Exception/ProviderExceptionTest.php index 245ba8df77e3e..fd1d19168480c 100644 --- a/src/Symfony/Component/Translation/Tests/Exception/ProviderExceptionTest.php +++ b/src/Symfony/Component/Translation/Tests/Exception/ProviderExceptionTest.php @@ -23,8 +23,7 @@ public function testExceptionWithDebugMessage() $mock->method('getInfo')->willReturn('debug'); $exception = new ProviderException('Exception message', $mock, 503); - - self::assertInstanceOf(ProviderException::class, $exception); + $this->assertSame('debug', $exception->getDebug()); } public function testExceptionWithNullAsDebugMessage() @@ -33,7 +32,6 @@ public function testExceptionWithNullAsDebugMessage() $mock->method('getInfo')->willReturn(null); $exception = new ProviderException('Exception message', $mock, 503); - - self::assertInstanceOf(ProviderException::class, $exception); + $this->assertSame('', $exception->getDebug()); } } diff --git a/src/Symfony/Component/Translation/Tests/Provider/TranslationProviderCollectionTest.php b/src/Symfony/Component/Translation/Tests/Provider/TranslationProviderCollectionTest.php new file mode 100644 index 0000000000000..98c1e4c7b80f0 --- /dev/null +++ b/src/Symfony/Component/Translation/Tests/Provider/TranslationProviderCollectionTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Provider; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Provider\ProviderInterface; +use Symfony\Component\Translation\Provider\TranslationProviderCollection; + +class TranslationProviderCollectionTest extends TestCase +{ + public function testKeys() + { + $this->assertSame(['foo', 'baz'], $this->createProviderCollection()->keys()); + } + + public function testKeysWithGenerator() + { + $this->assertSame(['foo', 'baz'], (new TranslationProviderCollection( + (function () { + yield 'foo' => $this->createMock(ProviderInterface::class); + + yield 'baz' => $this->createMock(ProviderInterface::class); + })() + ))->keys()); + } + + public function testToString() + { + $this->assertSame('[foo,baz]', (string) $this->createProviderCollection()); + } + + public function testHas() + { + $this->assertTrue($this->createProviderCollection()->has('foo')); + } + + public function testGet() + { + $provider = $this->createMock(ProviderInterface::class); + + $this->assertSame($provider, (new TranslationProviderCollection([ + 'foo' => $provider, + 'baz' => $this->createMock(ProviderInterface::class), + ]))->get('foo')); + } + + public function testGetThrowsException() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Provider "invalid" not found. Available: "[foo,baz]".'); + + $this->createProviderCollection()->get('invalid'); + } + + private function createProviderCollection(): TranslationProviderCollection + { + return new TranslationProviderCollection([ + 'foo' => $this->createMock(ProviderInterface::class), + 'baz' => $this->createMock(ProviderInterface::class), + ]); + } +} diff --git a/src/Symfony/Component/Translation/Tests/Writer/TranslationWriterTest.php b/src/Symfony/Component/Translation/Tests/Writer/TranslationWriterTest.php index 5be071351ef2f..c20c43cb19b2f 100644 --- a/src/Symfony/Component/Translation/Tests/Writer/TranslationWriterTest.php +++ b/src/Symfony/Component/Translation/Tests/Writer/TranslationWriterTest.php @@ -13,6 +13,8 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Translation\Dumper\DumperInterface; +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Exception\RuntimeException; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Writer\TranslationWriter; @@ -29,4 +31,33 @@ public function testWrite() $writer->addDumper('test', $dumper); $writer->write(new MessageCatalogue('en'), 'test'); } + + public function testGetFormats() + { + $writer = new TranslationWriter(); + $writer->addDumper('foo', $this->createMock(DumperInterface::class)); + $writer->addDumper('bar', $this->createMock(DumperInterface::class)); + + $this->assertEquals(['foo', 'bar'], $writer->getFormats()); + } + + public function testFormatIsNotSupported() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('There is no dumper associated with format "foo".'); + $writer = new TranslationWriter(); + + $writer->write(new MessageCatalogue('en'), 'foo'); + } + + public function testUnwritableDirectory() + { + $writer = new TranslationWriter(); + $writer->addDumper('foo', $this->createMock(DumperInterface::class)); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Translation Writer was not able to create directory "/foo/bar/baz".'); + + $writer->write(new MessageCatalogue('en'), 'foo', ['path' => '/foo/bar/baz']); + } } From 8c45f9fbca7691faf273b8fb1ab60a8a5e8a62e7 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 29 Nov 2023 09:26:06 +0100 Subject: [PATCH 16/19] [Serializer] Fix anonymous test class --- .../Tests/Normalizer/AbstractObjectNormalizerTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index 259c1d8a43e2e..60eaaad7fc8a5 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -855,7 +855,7 @@ public function testNormalizeWithIgnoreAnnotationAndPrivateProperties() public function testNormalizeBasedOnAllowedAttributes() { $normalizer = new class() extends AbstractObjectNormalizer { - protected function getAllowedAttributes($classOrObject, array $context, bool $attributesAsString = false) + protected function getAllowedAttributes($classOrObject, array $context, bool $attributesAsString = false): array { return ['foo']; } @@ -865,12 +865,12 @@ protected function extractAttributes(object $object, string $format = null, arra return []; } - protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = []) + protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = []): mixed { return $object->$attribute; } - protected function setAttributeValue(object $object, string $attribute, $value, string $format = null, array $context = []) + protected function setAttributeValue(object $object, string $attribute, $value, string $format = null, array $context = []): void { } }; From 1dc20a875f53346cb1279afa1028c3f3d0f5f6d5 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Wed, 29 Nov 2023 10:47:40 +0100 Subject: [PATCH 17/19] [Serializer] Revert allowed attributes fix --- .../Normalizer/AbstractObjectNormalizer.php | 16 ++++++++++------ .../AbstractObjectNormalizerTest.php | 19 ------------------- .../Serializer/Tests/SerializerTest.php | 2 +- 3 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index b7bf2e15d5bc2..732586bd85f9b 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -309,16 +309,20 @@ protected function getAttributes(object $object, ?string $format, array $context return $this->attributesCache[$key]; } - $attributes = $this->extractAttributes($object, $format, $context); + $allowedAttributes = $this->getAllowedAttributes($object, $context, true); - if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) { - array_unshift($attributes, $mapping->getTypeProperty()); + if (false !== $allowedAttributes) { + if ($context['cache_key']) { + $this->attributesCache[$key] = $allowedAttributes; + } + + return $allowedAttributes; } - $allowedAttributes = $this->getAllowedAttributes($object, $context, true); + $attributes = $this->extractAttributes($object, $format, $context); - if (false !== $allowedAttributes) { - $attributes = $attributes ? array_intersect($attributes, $allowedAttributes) : $allowedAttributes; + if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) { + array_unshift($attributes, $mapping->getTypeProperty()); } if ($context['cache_key'] && \stdClass::class !== $class) { diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index cf33e8e604848..35616b658ba78 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -17,7 +17,6 @@ use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\PropertyInfo\PropertyInfoExtractor; use Symfony\Component\PropertyInfo\Type; -use Symfony\Component\Serializer\Annotation\Ignore; use Symfony\Component\Serializer\Exception\ExtraAttributesException; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\LogicException; @@ -455,14 +454,6 @@ public function testNormalizeEmptyObject() $this->assertEquals(new \ArrayObject(), $normalizedData); } - public function testNormalizeWithIgnoreAnnotationAndPrivateProperties() - { - $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); - $normalizer = new ObjectNormalizer($classMetadataFactory); - - $this->assertSame(['foo' => 'foo'], $normalizer->normalize(new ObjectDummyWithIgnoreAnnotationAndPrivateProperty())); - } - public function testNormalizeBasedOnAllowedAttributes() { $normalizer = new class() extends AbstractObjectNormalizer { @@ -586,16 +577,6 @@ class EmptyDummy { } -class ObjectDummyWithIgnoreAnnotationAndPrivateProperty -{ - public $foo = 'foo'; - - /** @Ignore */ - public $ignored = 'ignored'; - - private $private = 'private'; -} - class AbstractObjectNormalizerWithMetadata extends AbstractObjectNormalizer { public function __construct() diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 65f7fd9d508eb..12d8eeefdd1a0 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -473,7 +473,7 @@ public function testDeserializeAndSerializeInterfacedObjectsWithTheClassMetadata 'groups' => ['two'], ]); - $this->assertEquals('{"type":"one","two":2}', $serialized); + $this->assertEquals('{"two":2,"type":"one"}', $serialized); } public function testDeserializeAndSerializeNestedInterfacedObjectsWithTheClassMetadataDiscriminator() From 4bdbb6dd17f8ff0fefa089783084609c1b9e97e3 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 29 Nov 2023 11:39:53 +0100 Subject: [PATCH 18/19] Update CHANGELOG for 6.4.0 --- CHANGELOG-6.4.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG-6.4.md b/CHANGELOG-6.4.md index 0733e749d212e..60185b4c5470a 100644 --- a/CHANGELOG-6.4.md +++ b/CHANGELOG-6.4.md @@ -7,6 +7,16 @@ in 6.4 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v6.4.0...v6.4.1 +* 6.4.0 (2023-11-29) + + * bug #52786 [Serializer] Revert allowed attributes fix (mtarld) + * bug #52765 [Translation] Remove ``@internal`` from abstract testcases (OskarStark) + * bug #52780 [DependencyInjection] don't check parameter values if they are not set (xabbuh) + * bug #52762 [VarExporter] Work around php/php-src#12695 for lazy objects, fixing nullsafe-related behavior (nicolas-grekas) + * bug #52759 [VarExporter] Fix serializing objects that implement __sleep() and that are made lazy (nicolas-grekas) + * bug #52767 [Serializer] Fix normalization relying on allowed attributes only (mtarld) + * bug #52727 [String] Fix Inflector for 'icon' (podhy) + * 6.4.0-RC2 (2023-11-26) * bug #52724 [Security] make secret required for DefaultLoginRateLimiter (RobertMe) From 68c0ba5da7f633ce8f377a0b5731f2df0fae462a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 29 Nov 2023 11:40:15 +0100 Subject: [PATCH 19/19] Update VERSION for 6.4.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 7825b57cf2148..39170e8333e20 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.4.0-DEV'; + public const VERSION = '6.4.0'; public const VERSION_ID = 60400; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 4; public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '11/2026'; public const END_OF_LIFE = '11/2027';