diff --git a/CHANGELOG-6.4.md b/CHANGELOG-6.4.md index 517fa7fc82dc5..0733e749d212e 100644 --- a/CHANGELOG-6.4.md +++ b/CHANGELOG-6.4.md @@ -7,6 +7,38 @@ 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-RC2 (2023-11-26) + + * bug #52724 [Security] make secret required for DefaultLoginRateLimiter (RobertMe) + * bug #52617 [AssetMapper] Fix resolving jsdeliver default + other exports from modules (ogizanagi) + * feature #52712 [AssetMapper] Exclude dot files (weaverryan) + * bug #52725 [AssetMapper] Fix: also download files referenced by url() in CSS (weaverryan) + * bug #52702 [AssetMapper] Fix eager imports are not deduplicated (smnandre) + * bug #52719 [Mime] Add `TemplatedEmail::$locale` to the serialized props (mkrauser) + * bug #52677 [Translation] [Lokalise] Fix language format on Lokalise Provider (welcoMattic) + * bug #52715 [Cache] fix detecting the database server version (xabbuh) + * bug #52688 [Cache] Add url decoding of password in `RedisTrait` DSN (alexandre-daubois) + * bug #52172 [Serializer] Fix denormalizing empty string into `object|null` parameter (Jeroeny) + * bug #52693 [Messenger] Fix message handlers with multiple `from_transports` (valtzu) + * bug #52684 [PropertyInfo] Fixed promoted property type detection for `PhpStanExtractor` (LastDragon-ru) + * bug #52681 [Serializer] Fix support for DiscriminatorMap in PropertyNormalizer (mtarld) + * bug #52680 [Serializer] Fix access to private properties/getters when using the ``@Ignore`` annotation (mtarld) + * bug #52713 [Serializer] Fix deserialization_path missing using contructor (mtarld) + * bug #52683 [Serializer] Fix constructor deserialization path (mtarld) + * bug #52707 [HttpKernel] Fix logging deprecations to the "php" channel when channel "deprecation" is not defined (nicolas-grekas) + * bug #52589 [Serializer] Fix XML attributes not added on empty node (mtarld) + * bug #52686 [Cache] fix detecting the server version with Doctrine DBAL 4 (xabbuh) + * bug #52629 [Messenger] Fix support for Redis Sentinel using php-redis 6.0.0 (pepeh) + * bug #52656 [FrameworkBundle] Add TemplateController to the list of allowed controllers for fragments (nicolas-grekas) + * bug #52459 [Cache][HttpFoundation][Lock] Fix PDO store not creating table + add tests (HypeMC) + * bug #52626 [Serializer] Fix denormalizing date intervals having both weeks and days (oneNevan) + * bug #52578 [Serializer] Fix denormalize constructor arguments (mtarld) + * bug #52526 Add some more non-countable English nouns (paullallier) + * bug #52604 [FrameworkBundle] register the virtual request stack together with common profiling services (xabbuh) + * bug #52039 [Scheduler] Continue with stored `Checkpoint::$time` on lock (Jeroeny) + * bug #52631 [DomCrawler] Revert "bug #52579 UriResolver support path with colons" (lyrixx) + * bug #52618 [VarExporter] Fix handling mangled property names returned by __sleep() (nicolas-grekas) + * 6.4.0-RC1 (2023-11-15) * bug #52588 [Messenger] Use extension_loaded call to check if pcntl extension is loaded, as SIGTERM might be set be swoole (Sergii Dolgushev) diff --git a/CHANGELOG-7.0.md b/CHANGELOG-7.0.md index f4bc3f7460f11..5b3f89afe49aa 100644 --- a/CHANGELOG-7.0.md +++ b/CHANGELOG-7.0.md @@ -7,6 +7,16 @@ in 7.0 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/v7.0.0...v7.0.1 +* 7.0.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) + * 7.0.0-RC2 (2023-11-26) * bug #52724 [Security] make secret required for DefaultLoginRateLimiter (RobertMe) 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 */ diff --git a/src/Symfony/Component/HttpClient/README.md b/src/Symfony/Component/HttpClient/README.md index 214489b7e7f76..4e4823de44fc5 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 7.0 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 diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 5848b24ef7cb8..c5c06a9755872 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 = '7.0.0-RC2'; + public const VERSION = '7.0.0'; public const VERSION_ID = 70000; public const MAJOR_VERSION = 7; public const MINOR_VERSION = 0; public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = 'RC2'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '07/2024'; public const END_OF_LIFE = '07/2024'; diff --git a/src/Symfony/Component/Routing/README.md b/src/Symfony/Component/Routing/README.md index ae8284f541faa..37c5f5c9c9ce1 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 7.0 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 diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 6816e5366853a..4edbe9c3e4cc4 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -246,16 +246,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 ($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 = array_intersect($attributes, $allowedAttributes); + if ($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 49f19666c2e22..16e869efd19e1 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -18,7 +18,6 @@ use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\DiscriminatorMap; -use Symfony\Component\Serializer\Attribute\Ignore; use Symfony\Component\Serializer\Attribute\SerializedName; use Symfony\Component\Serializer\Attribute\SerializedPath; use Symfony\Component\Serializer\Exception\ExtraAttributesException; @@ -837,12 +836,39 @@ public function testDenormalizeWithCorrectOrderOfAttributeAndProperty() $this->assertSame('nested-id', $test->id); } - public function testNormalizeWithIgnoreAttributeAndPrivateProperties() + public function testNormalizeBasedOnAllowedAttributes() { - $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); - $serializer = new Serializer([new ObjectNormalizer($classMetadataFactory)]); + $normalizer = new class() extends AbstractObjectNormalizer { + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } + + protected function getAllowedAttributes($classOrObject, array $context, bool $attributesAsString = false): array + { + 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 = []): mixed + { + return $object->$attribute; + } + + protected function setAttributeValue(object $object, string $attribute, $value, string $format = null, array $context = []): void + { + } + }; + + $object = new Dummy(); + $object->foo = 'foo'; + $object->bar = 'bar'; - $this->assertSame(['foo' => 'foo'], $serializer->normalize(new ObjectDummyWithIgnoreAttributeAndPrivateProperty())); + $this->assertSame(['foo' => 'foo'], $normalizer->normalize($object)); } public function testDenormalizeUntypedFormat() @@ -1054,16 +1080,6 @@ class ObjectDummyWithContextAttributeSkipNullValues public ?string $propertyWithNullSkipNullValues = null; } -class ObjectDummyWithIgnoreAttributeAndPrivateProperty -{ - 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 88a3a16009efe..67953a4d16f08 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -469,7 +469,7 @@ public function testDeserializeAndSerializeInterfacedObjectsWithTheClassMetadata 'groups' => ['two'], ]); - $this->assertEquals('{"type":"one","two":2}', $serialized); + $this->assertEquals('{"two":2,"type":"one"}', $serialized); } public function testDeserializeAndSerializeNestedInterfacedObjectsWithTheClassMetadataDiscriminator() diff --git a/src/Symfony/Component/String/Inflector/EnglishInflector.php b/src/Symfony/Component/String/Inflector/EnglishInflector.php index a0f6dc938e4cd..feea7f668df04 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'], 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/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 { 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']); + } } 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. + Използването на скрити насложени символи не е позволено. + diff --git a/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php b/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php index 493778183d103..d378ba2925dad 100644 --- a/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/ConstraintValidatorTest.php @@ -18,6 +18,8 @@ class ConstraintValidatorTest extends TestCase { + use IcuCompatibilityTrait; + /** * @dataProvider formatValueProvider */ @@ -43,10 +45,10 @@ public static function formatValueProvider(): array ['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], ['FirstCase', TestEnum::FirstCase], ]; @@ -62,7 +64,7 @@ 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 ea9efe90e238c..06c0edfbf1a50 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(): EqualToValidator { return new EqualToValidator(); @@ -61,14 +64,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 e1e29576bc7b3..aaeb98d8bfd3f 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(): GreaterThanOrEqualValidator { return new GreaterThanOrEqualValidator(); @@ -64,14 +67,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 bc4b5df04fb06..ab8a0ac10f51d 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(): GreaterThanValidator { return new GreaterThanValidator(); @@ -60,12 +63,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'], @@ -73,7 +76,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 334743c4b0e5e..424c028c0a808 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(): IdenticalToValidator { return new IdenticalToValidator(); @@ -81,13 +84,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 aec7edcb603e4..d4bcc346fdabf 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(): LessThanOrEqualValidator { return new LessThanOrEqualValidator(); @@ -66,15 +69,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 7721d812fe9a5..14064962a26d7 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(): LessThanValidator { return new LessThanValidator(); @@ -60,19 +63,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 de8249d8b37dd..a297ae37fba3f 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(): NotEqualToValidator { return new NotEqualToValidator(); @@ -61,14 +64,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 a594599c4ae09..eab5395bcd808 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(): NotIdenticalToValidator { return new NotIdenticalToValidator(); @@ -77,17 +80,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 1f8cb736aca57..876595c37fc94 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(): RangeValidator { 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'], @@ -277,7 +280,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 @@ -298,7 +301,7 @@ public static function getTenthToTwentiethMarch2014() 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 @@ -306,10 +309,10 @@ 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 \DateTimeImmutable('March 20, 2013'), 'Mar 20, 2013, 12:00 AM'], - [new \DateTimeImmutable('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")], ]; date_default_timezone_set($timezone); @@ -317,7 +320,7 @@ public static function getSoonerThanTenthMarch2014() 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 @@ -325,10 +328,10 @@ 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 \DateTimeImmutable('March 21, 2014'), 'Mar 21, 2014, 12:00 AM'], - [new \DateTimeImmutable('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")], ]; date_default_timezone_set($timezone); @@ -372,7 +375,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 @@ -387,7 +390,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(); } @@ -395,7 +398,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 @@ -410,7 +413,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(); } @@ -418,7 +421,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 @@ -434,8 +437,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(); } @@ -459,13 +462,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], @@ -927,7 +930,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(); @@ -953,7 +956,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(); @@ -980,8 +983,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) @@ -1009,8 +1012,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); + } +} diff --git a/src/Symfony/Component/VarDumper/CHANGELOG.md b/src/Symfony/Component/VarDumper/CHANGELOG.md index e44b5c08ce61c..f7021f59a4598 100644 --- a/src/Symfony/Component/VarDumper/CHANGELOG.md +++ b/src/Symfony/Component/VarDumper/CHANGELOG.md @@ -6,7 +6,7 @@ CHANGELOG * Add argument `$label` to `VarDumper::dump()` * Require explicit argument when calling `VarDumper::setHandler()` - * Remove display of backtrace in `Twig_Template`, only `Twig\Template` are supported + * Remove display of backtrace in `Twig_Template`, only `Twig\Template` is supported 6.4 --- diff --git a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php index 5f5d50ecf70c1..ec2afb4312484 100644 --- a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php @@ -214,7 +214,7 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, boo $ellipsis = $ellipsis->attr['ellipsis'] ?? 0; if (is_file($f['file']) && 0 <= self::$srcContext) { - if (!empty($f['class']) && is_subclass_of($f['class'], 'Twig\Template') && method_exists($f['class'], 'getDebugInfo')) { + if (!empty($f['class']) && is_subclass_of($f['class'], 'Twig\Template')) { $template = null; if (isset($f['object'])) { $template = $f['object']; diff --git a/src/Symfony/Component/VarExporter/Internal/Hydrator.php b/src/Symfony/Component/VarExporter/Internal/Hydrator.php index 86c03095b5a4f..f4ec8b7c44032 100644 --- a/src/Symfony/Component/VarExporter/Internal/Hydrator.php +++ b/src/Symfony/Component/VarExporter/Internal/Hydrator.php @@ -262,10 +262,10 @@ public static function getPropertyScopes($class): array $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]; @@ -279,8 +279,8 @@ public static function getPropertyScopes($class): array 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 2293031e4c107..2ac8459ec7418 100644 --- a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php +++ b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php @@ -47,11 +47,11 @@ public function __construct(public readonly \Closure $initializer, $skippedPrope public function initialize($instance, $propertyName, $propertyScope) { - if (self::STATUS_INITIALIZED_FULL === $this->status) { - return self::STATUS_INITIALIZED_FULL; + if (self::STATUS_UNINITIALIZED_FULL !== $this->status) { + return $this->status; } - $this->status = self::STATUS_INITIALIZED_FULL; + $this->status = self::STATUS_INITIALIZED_PARTIAL; try { if ($defaultProperties = array_diff_key(LazyObjectRegistry::$defaultProperties[$instance::class], $this->skippedProperties)) { @@ -66,7 +66,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 b416d171b9e39..1b28bfd2e105e 100644 --- a/src/Symfony/Component/VarExporter/LazyGhostTrait.php +++ b/src/Symfony/Component/VarExporter/LazyGhostTrait.php @@ -106,8 +106,17 @@ public function &__get($name): mixed $state = $this->lazyObjectState ?? null; if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))) { - $state->initialize($this, $name, $readonlyScope ?? $scope); - goto get_in_scope; + 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; + } } } @@ -169,7 +178,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); } @@ -202,8 +213,10 @@ public function __isset($name): bool $scope = Registry::getScope($propertyScopes, $class, $name); $state = $this->lazyObjectState ?? null; - if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))) { - $state->initialize($this, $name, $readonlyScope ?? $scope); + 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; } } @@ -231,7 +244,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); } @@ -286,7 +301,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/ProxyHelper.php b/src/Symfony/Component/VarExporter/ProxyHelper.php index 15dcd7041f24f..9cfd7b92b8f65 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 c87adb008c8c6..dfc98ffcb6b7a 100644 --- a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php +++ b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php @@ -64,8 +64,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() @@ -145,7 +147,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()