From 136c4a314c007092bd675eead8f78060b90948ff Mon Sep 17 00:00:00 2001 From: Michael Babker Date: Sun, 26 Sep 2021 13:23:50 -0500 Subject: [PATCH 01/42] [DoctrineBridge] Allow AbstractDoctrineExtension implementations to support the newer bundle structure --- UPGRADE-5.4.md | 6 +++++ UPGRADE-6.0.md | 2 ++ src/Symfony/Bridge/Doctrine/CHANGELOG.md | 3 +++ .../AbstractDoctrineExtension.php | 25 ++++++++++++++----- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/UPGRADE-5.4.md b/UPGRADE-5.4.md index f98966dbf1b2d..57d27502d6876 100644 --- a/UPGRADE-5.4.md +++ b/UPGRADE-5.4.md @@ -12,6 +12,12 @@ Console * Deprecate `HelperSet::setCommand()` and `getCommand()` without replacement +DoctrineBridge +-------------- + + * Add argument `$bundleDir` to `AbstractDoctrineExtension::getMappingDriverBundleConfigDefaults()` + * Add argument `$bundleDir` to `AbstractDoctrineExtension::getMappingResourceConfigDirectory()` + Finder ------ diff --git a/UPGRADE-6.0.md b/UPGRADE-6.0.md index afd57a60fafca..7bcba1f6d0ddf 100644 --- a/UPGRADE-6.0.md +++ b/UPGRADE-6.0.md @@ -10,6 +10,8 @@ DoctrineBridge -------------- * Remove `UserLoaderInterface::loadUserByUsername()` in favor of `UserLoaderInterface::loadUserByIdentifier()` + * Add argument `$bundleDir` to `AbstractDoctrineExtension::getMappingDriverBundleConfigDefaults()` + * Add argument `$bundleDir` to `AbstractDoctrineExtension::getMappingResourceConfigDirectory()` Cache ----- diff --git a/src/Symfony/Bridge/Doctrine/CHANGELOG.md b/src/Symfony/Bridge/Doctrine/CHANGELOG.md index d2e0a7761eeef..4e6abc3b78b1f 100644 --- a/src/Symfony/Bridge/Doctrine/CHANGELOG.md +++ b/src/Symfony/Bridge/Doctrine/CHANGELOG.md @@ -7,6 +7,9 @@ CHANGELOG * Add `DoctrineOpenTransactionLoggerMiddleware` to log when a transaction has been left open * Deprecate `PdoCacheAdapterDoctrineSchemaSubscriber` and add `DoctrineDbalCacheAdapterSchemaSubscriber` instead * `UniqueEntity` constraint retrieves a maximum of two entities if the default repository method is used. + * Add support for the newer bundle structure to `AbstractDoctrineExtension::loadMappingInformation()` + * Add argument `$bundleDir` to `AbstractDoctrineExtension::getMappingDriverBundleConfigDefaults()` + * Add argument `$bundleDir` to `AbstractDoctrineExtension::getMappingResourceConfigDirectory()` 5.3 --- diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php index 2714e27d754a3..236789a721a66 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -73,9 +73,11 @@ protected function loadMappingInformation(array $objectManager, ContainerBuilder if ($mappingConfig['is_bundle']) { $bundle = null; + $bundleMetadata = null; foreach ($container->getParameter('kernel.bundles') as $name => $class) { if ($mappingName === $name) { $bundle = new \ReflectionClass($class); + $bundleMetadata = $container->getParameter('kernel.bundles_metadata')[$name]; break; } @@ -85,7 +87,7 @@ protected function loadMappingInformation(array $objectManager, ContainerBuilder throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled.', $mappingName)); } - $mappingConfig = $this->getMappingDriverBundleConfigDefaults($mappingConfig, $bundle, $container); + $mappingConfig = $this->getMappingDriverBundleConfigDefaults($mappingConfig, $bundle, $container, $bundleMetadata['path']); if (!$mappingConfig) { continue; } @@ -133,11 +135,20 @@ protected function setMappingDriverConfig(array $mappingConfig, string $mappingN * * Returns false when autodetection failed, an array of the completed information otherwise. * + * @param string|null $bundleDir The bundle directory path + * * @return array|false */ - protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \ReflectionClass $bundle, ContainerBuilder $container) + protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \ReflectionClass $bundle, ContainerBuilder $container/*, string $bundleDir = null*/) { - $bundleDir = \dirname($bundle->getFileName()); + if (\func_num_args() < 4 && __CLASS__ !== static::class && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface && !$this instanceof \Mockery\MockInterface) { + trigger_deprecation('symfony/doctrine-bridge', '5.4', 'The "%s()" method will have a new "string $bundleDir = null" argument in version 6.0, not defining it is deprecated.', __METHOD__); + $bundleDir = null; + } else { + $bundleDir = func_get_arg(3); + } + + $bundleDir ?? $bundleDir = \dirname($bundle->getFileName()); if (!$bundleConfig['type']) { $bundleConfig['type'] = $this->detectMetadataDriver($bundleDir, $container); @@ -152,7 +163,7 @@ protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \Re if (\in_array($bundleConfig['type'], ['annotation', 'staticphp', 'attribute'])) { $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingObjectDefaultName(); } else { - $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingResourceConfigDirectory(); + $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingResourceConfigDirectory($bundleDir); } } else { $bundleConfig['dir'] = $bundleDir.'/'.$bundleConfig['dir']; @@ -246,7 +257,7 @@ protected function assertValidMappingConfiguration(array $mappingConfig, string */ protected function detectMetadataDriver(string $dir, ContainerBuilder $container) { - $configPath = $this->getMappingResourceConfigDirectory(); + $configPath = $this->getMappingResourceConfigDirectory($dir); $extension = $this->getMappingResourceExtension(); if (glob($dir.'/'.$configPath.'/*.'.$extension.'.xml', \GLOB_NOSORT)) { @@ -440,9 +451,11 @@ abstract protected function getMappingObjectDefaultName(); /** * Relative path from the bundle root to the directory where mapping files reside. * + * @param string|null $bundleDir The bundle directory path + * * @return string */ - abstract protected function getMappingResourceConfigDirectory(); + abstract protected function getMappingResourceConfigDirectory(/*string $bundleDir = null*/); /** * Extension used by the mapping files. From 798a5d9378c1a813ce45c0dcccd3ad53c430401a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 14 Nov 2021 18:22:50 +0100 Subject: [PATCH 02/42] Bump Symfony version to 6.0.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 8e6af04c0520e..28c499510d984 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -78,12 +78,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.0.0-BETA2'; + public const VERSION = '6.0.0-DEV'; public const VERSION_ID = 60000; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 0; public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = 'BETA2'; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '07/2022'; public const END_OF_LIFE = '07/2022'; From b0bb93c7d371609bc57872d9899039c34da15fde Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 14 Nov 2021 18:22:58 +0100 Subject: [PATCH 03/42] [FrameworkBundle] deprecate notifier service aliases that dont follow the convention --- .../FrameworkExtension.php | 50 ++++++----------- .../Resources/config/notifier_transports.php | 53 +++++++++++++------ .../FrameworkExtensionTest.php | 2 +- 3 files changed, 56 insertions(+), 49 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 18a94f23edbff..c73b3a7c3463c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -2486,42 +2486,42 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ $classToServices = [ AllMySmsTransportFactory::class => 'notifier.transport_factory.all-my-sms', - AmazonSnsTransportFactory::class => 'notifier.transport_factory.amazonsns', + AmazonSnsTransportFactory::class => 'notifier.transport_factory.amazon-sns', ClickatellTransportFactory::class => 'notifier.transport_factory.clickatell', DiscordTransportFactory::class => 'notifier.transport_factory.discord', EsendexTransportFactory::class => 'notifier.transport_factory.esendex', ExpoTransportFactory::class => 'notifier.transport_factory.expo', - FakeChatTransportFactory::class => 'notifier.transport_factory.fakechat', - FakeSmsTransportFactory::class => 'notifier.transport_factory.fakesms', + FakeChatTransportFactory::class => 'notifier.transport_factory.fake-chat', + FakeSmsTransportFactory::class => 'notifier.transport_factory.fake-sms', FirebaseTransportFactory::class => 'notifier.transport_factory.firebase', - FreeMobileTransportFactory::class => 'notifier.transport_factory.freemobile', + FreeMobileTransportFactory::class => 'notifier.transport_factory.free-mobile', GatewayApiTransportFactory::class => 'notifier.transport_factory.gateway-api', GitterTransportFactory::class => 'notifier.transport_factory.gitter', - GoogleChatTransportFactory::class => 'notifier.transport_factory.googlechat', + GoogleChatTransportFactory::class => 'notifier.transport_factory.google-chat', InfobipTransportFactory::class => 'notifier.transport_factory.infobip', IqsmsTransportFactory::class => 'notifier.transport_factory.iqsms', - LightSmsTransportFactory::class => 'notifier.transport_factory.lightsms', - LinkedInTransportFactory::class => 'notifier.transport_factory.linkedin', + LightSmsTransportFactory::class => 'notifier.transport_factory.light-sms', + LinkedInTransportFactory::class => 'notifier.transport_factory.linked-in', MailjetNotifierTransportFactory::class => 'notifier.transport_factory.mailjet', MattermostTransportFactory::class => 'notifier.transport_factory.mattermost', MercureTransportFactory::class => 'notifier.transport_factory.mercure', - MessageBirdTransport::class => 'notifier.transport_factory.messagebird', - MessageMediaTransportFactory::class => 'notifier.transport_factory.messagemedia', - MicrosoftTeamsTransportFactory::class => 'notifier.transport_factory.microsoftteams', + MessageBirdTransport::class => 'notifier.transport_factory.message-bird', + MessageMediaTransportFactory::class => 'notifier.transport_factory.message-media', + MicrosoftTeamsTransportFactory::class => 'notifier.transport_factory.microsoft-teams', MobytTransportFactory::class => 'notifier.transport_factory.mobyt', NexmoTransportFactory::class => 'notifier.transport_factory.nexmo', OctopushTransportFactory::class => 'notifier.transport_factory.octopush', - OneSignalTransportFactory::class => 'notifier.transport_factory.onesignal', - OvhCloudTransportFactory::class => 'notifier.transport_factory.ovhcloud', - RocketChatTransportFactory::class => 'notifier.transport_factory.rocketchat', + OneSignalTransportFactory::class => 'notifier.transport_factory.one-signal', + OvhCloudTransportFactory::class => 'notifier.transport_factory.ovh-cloud', + RocketChatTransportFactory::class => 'notifier.transport_factory.rocket-chat', SendinblueNotifierTransportFactory::class => 'notifier.transport_factory.sendinblue', SinchTransportFactory::class => 'notifier.transport_factory.sinch', SlackTransportFactory::class => 'notifier.transport_factory.slack', Sms77TransportFactory::class => 'notifier.transport_factory.sms77', SmsapiTransportFactory::class => 'notifier.transport_factory.smsapi', - SmsBiurasTransportFactory::class => 'notifier.transport_factory.smsbiuras', + SmsBiurasTransportFactory::class => 'notifier.transport_factory.sms-biuras', SmscTransportFactory::class => 'notifier.transport_factory.smsc', - SpotHitTransportFactory::class => 'notifier.transport_factory.spothit', + SpotHitTransportFactory::class => 'notifier.transport_factory.spot-hit', TelegramTransportFactory::class => 'notifier.transport_factory.telegram', TelnyxTransportFactory::class => 'notifier.transport_factory.telnyx', TurboSmsTransport::class => 'notifier.transport_factory.turbo-sms', @@ -2533,27 +2533,11 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ $parentPackages = ['symfony/framework-bundle', 'symfony/notifier']; foreach ($classToServices as $class => $service) { - switch ($package = substr($service, \strlen('notifier.transport_factory.'))) { - case 'amazonsns': $package = 'amazon-sns'; break; - case 'fakechat': $package = 'fake-chat'; break; - case 'fakesms': $package = 'fake-sms'; break; - case 'freemobile': $package = 'free-mobile'; break; - case 'googlechat': $package = 'google-chat'; break; - case 'lightsms': $package = 'light-sms'; break; - case 'linkedin': $package = 'linked-in'; break; - case 'messagebird': $package = 'message-bird'; break; - case 'messagemedia': $package = 'message-media'; break; - case 'microsoftteams': $package = 'microsoft-teams'; break; - case 'onesignal': $package = 'one-signal'; break; - case 'ovhcloud': $package = 'ovh-cloud'; break; - case 'rocketchat': $package = 'rocket-chat'; break; - case 'smsbiuras': $package = 'sms-biuras'; break; - case 'spothit': $package = 'spot-hit'; break; - case 'turbosms': $package = 'turbo-sms'; break; - } + $package = substr($service, \strlen('notifier.transport_factory.')); if (!ContainerBuilder::willBeAvailable(sprintf('symfony/%s-notifier', $package), $class, $parentPackages, true)) { $container->removeDefinition($service); + $container->removeAlias(str_replace('-', '', $service)); // @deprecated to be removed in 6.0 } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index 6bfee17747e58..c70fe08180155 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -59,6 +59,29 @@ return static function (ContainerConfigurator $container) { $container->services() + ->alias('notifier.transport_factory.fakechat', 'notifier.transport_factory.fake-chat') + ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.fake-chat" instead.') + ->alias('notifier.transport_factory.fakesms', 'notifier.transport_factory.fake-sms') + ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.fake-sms" instead.') + ->alias('notifier.transport_factory.freemobile', 'notifier.transport_factory.free-mobile') + ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.free-mobile" instead.') + ->alias('notifier.transport_factory.googlechat', 'notifier.transport_factory.google-chat') + ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.google-chat" instead.') + ->alias('notifier.transport_factory.lightsms', 'notifier.transport_factory.light-sms') + ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.light-sms" instead.') + ->alias('notifier.transport_factory.linkedin', 'notifier.transport_factory.linked-in') + ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.linked-in" instead.') + ->alias('notifier.transport_factory.microsoftteams', 'notifier.transport_factory.microsoft-teams') + ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.microsoft-teams" instead.') + ->alias('notifier.transport_factory.onesignal', 'notifier.transport_factory.one-signal') + ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.one-signal" instead.') + ->alias('notifier.transport_factory.ovhcloud', 'notifier.transport_factory.ovh-cloud') + ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.ovh-cloud" instead.') + ->alias('notifier.transport_factory.rocketchat', 'notifier.transport_factory.rocket-chat') + ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.rocket-chat" instead.') + ->alias('notifier.transport_factory.spothit', 'notifier.transport_factory.spot-hit') + ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.spot-hit" instead.') + ->set('notifier.transport_factory.abstract', AbstractTransportFactory::class) ->abstract() ->args([service('event_dispatcher'), service('http_client')->ignoreOnInvalid()]) @@ -67,7 +90,7 @@ ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') - ->set('notifier.transport_factory.linkedin', LinkedInTransportFactory::class) + ->set('notifier.transport_factory.linked-in', LinkedInTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') @@ -83,11 +106,11 @@ ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') - ->set('notifier.transport_factory.rocketchat', RocketChatTransportFactory::class) + ->set('notifier.transport_factory.rocket-chat', RocketChatTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') - ->set('notifier.transport_factory.googlechat', GoogleChatTransportFactory::class) + ->set('notifier.transport_factory.google-chat', GoogleChatTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') @@ -103,23 +126,23 @@ ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') - ->set('notifier.transport_factory.freemobile', FreeMobileTransportFactory::class) + ->set('notifier.transport_factory.free-mobile', FreeMobileTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') - ->set('notifier.transport_factory.spothit', SpotHitTransportFactory::class) + ->set('notifier.transport_factory.spot-hit', SpotHitTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') - ->set('notifier.transport_factory.fakechat', FakeChatTransportFactory::class) + ->set('notifier.transport_factory.fake-chat', FakeChatTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') - ->set('notifier.transport_factory.fakesms', FakeSmsTransportFactory::class) + ->set('notifier.transport_factory.fake-sms', FakeSmsTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') - ->set('notifier.transport_factory.ovhcloud', OvhCloudTransportFactory::class) + ->set('notifier.transport_factory.ovh-cloud', OvhCloudTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') @@ -163,7 +186,7 @@ ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') - ->set('notifier.transport_factory.microsoftteams', MicrosoftTeamsTransportFactory::class) + ->set('notifier.transport_factory.microsoft-teams', MicrosoftTeamsTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') @@ -183,7 +206,7 @@ ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') - ->set('notifier.transport_factory.amazonsns', AmazonSnsTransportFactory::class) + ->set('notifier.transport_factory.amazon-sns', AmazonSnsTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') ->tag('chatter.transport_factory') @@ -193,11 +216,11 @@ ->tag('chatter.transport_factory') ->tag('texter.transport_factory') - ->set('notifier.transport_factory.lightsms', LightSmsTransportFactory::class) + ->set('notifier.transport_factory.light-sms', LightSmsTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') - ->set('notifier.transport_factory.smsbiuras', SmsBiurasTransportFactory::class) + ->set('notifier.transport_factory.sms-biuras', SmsBiurasTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') @@ -205,11 +228,11 @@ ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') - ->set('notifier.transport_factory.messagebird', MessageBirdTransportFactory::class) + ->set('notifier.transport_factory.message-bird', MessageBirdTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') - ->set('notifier.transport_factory.messagemedia', MessageMediaTransportFactory::class) + ->set('notifier.transport_factory.message-media', MessageMediaTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') @@ -233,7 +256,7 @@ ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') - ->set('notifier.transport_factory.onesignal', OneSignalTransportFactory::class) + ->set('notifier.transport_factory.one-signal', OneSignalTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index adcf065338035..3493b8f551d42 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -1984,7 +1984,7 @@ public function testIfNotifierTransportsAreKnownByFrameworkExtension() $container = $this->createContainerFromFile('notifier'); foreach ((new Finder())->in(\dirname(__DIR__, 4).'/Component/Notifier/Bridge')->directories()->depth(0)->exclude('Mercure') as $bridgeDirectory) { - $transportFactoryName = strtolower($bridgeDirectory->getFilename()); + $transportFactoryName = strtolower(preg_replace('/(.)([A-Z])/', '$1-$2', $bridgeDirectory->getFilename())); $this->assertTrue($container->hasDefinition('notifier.transport_factory.'.$transportFactoryName), sprintf('Did you forget to add the TransportFactory: "%s" to the $classToServices array in the FrameworkBundleExtension?', $bridgeDirectory->getFilename())); } } From 1ce16dc373b3060219295c6fbd75c90305a0b42e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Egyed?= Date: Sun, 14 Nov 2021 19:53:46 +0100 Subject: [PATCH 04/42] [DependencyInjection] Fix YamlFileLoader return type --- .../Component/DependencyInjection/Loader/YamlFileLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 4c09c7c6d373a..df06ffc105913 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -735,7 +735,7 @@ private function parseCallable(mixed $callable, string $parameter, string $id, s * * @throws InvalidArgumentException when the given file is not a local file or when it does not exist */ - protected function loadFile(string $file): array + protected function loadFile(string $file): ?array { if (!class_exists(\Symfony\Component\Yaml\Parser::class)) { throw new RuntimeException('Unable to load YAML config files as the Symfony Yaml Component is not installed.'); From 4cfc7273dbfa3efbe3054e5ae8f4af4ba27db81b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 11 Nov 2021 18:29:11 +0100 Subject: [PATCH 05/42] [Cache] Decrease the probability of invalidation loss on tag eviction --- .../Cache/Adapter/TagAwareAdapter.php | 113 +++++++++--------- .../Tests/Adapter/TagAwareAdapterTest.php | 20 +--- 2 files changed, 55 insertions(+), 78 deletions(-) diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index a9048995c2abb..ff22e5a8ac56e 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -41,7 +41,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterfac private static $createCacheItem; private static $setCacheItemTags; private static $getTagsByKey; - private static $invalidateTags; + private static $saveTags; public function __construct(AdapterInterface $itemsPool, AdapterInterface $tagsPool = null, float $knownTagVersionsTtl = 0.15) { @@ -95,8 +95,10 @@ static function ($deferred) { null, CacheItem::class ); - self::$invalidateTags ?? self::$invalidateTags = \Closure::bind( + self::$saveTags ?? self::$saveTags = \Closure::bind( static function (AdapterInterface $tagsAdapter, array $tags) { + ksort($tags); + foreach ($tags as $v) { $v->expiry = 0; $tagsAdapter->saveDeferred($v); @@ -114,40 +116,14 @@ static function (AdapterInterface $tagsAdapter, array $tags) { */ public function invalidateTags(array $tags) { - $ok = true; - $tagsByKey = []; - $invalidatedTags = []; + $ids = []; foreach ($tags as $tag) { \assert('' !== CacheItem::validateKey($tag)); - $invalidatedTags[$tag] = 0; - } - - if ($this->deferred) { - $items = $this->deferred; - foreach ($items as $key => $item) { - if (!$this->pool->saveDeferred($item)) { - unset($this->deferred[$key]); - $ok = false; - } - } - - $tagsByKey = (self::$getTagsByKey)($items); - $this->deferred = []; - } - - $tagVersions = $this->getTagVersions($tagsByKey, $invalidatedTags); - $f = self::$createCacheItem; - - foreach ($tagsByKey as $key => $tags) { - $this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key])); - } - $ok = $this->pool->commit() && $ok; - - if ($invalidatedTags) { - $ok = (self::$invalidateTags)($this->tags, $invalidatedTags) && $ok; + unset($this->knownTagVersions[$tag]); + $ids[] = $tag.static::TAGS_PREFIX; } - return $ok; + return !$tags || $this->tags->deleteItems($ids); } /** @@ -176,7 +152,7 @@ public function hasItem($key) } foreach ($this->getTagVersions([$itemTags]) as $tag => $version) { - if ($itemTags[$tag] !== $version && 1 !== $itemTags[$tag] - $version) { + if ($itemTags[$tag] !== $version) { return false; } } @@ -314,7 +290,30 @@ public function saveDeferred(CacheItemInterface $item) */ public function commit() { - return $this->invalidateTags([]); + if (!$this->deferred) { + return true; + } + + $ok = true; + foreach ($this->deferred as $key => $item) { + if (!$this->pool->saveDeferred($item)) { + unset($this->deferred[$key]); + $ok = false; + } + } + + $items = $this->deferred; + $tagsByKey = (self::$getTagsByKey)($items); + $this->deferred = []; + + $tagVersions = $this->getTagVersions($tagsByKey); + $f = self::$createCacheItem; + + foreach ($tagsByKey as $key => $tags) { + $this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key])); + } + + return $this->pool->commit() && $ok; } /** @@ -361,7 +360,7 @@ private function generateItems(iterable $items, array $tagKeys): \Generator foreach ($itemTags as $key => $tags) { foreach ($tags as $tag => $version) { - if ($tagVersions[$tag] !== $version && 1 !== $version - $tagVersions[$tag]) { + if ($tagVersions[$tag] !== $version) { unset($itemTags[$key]); continue 2; } @@ -377,42 +376,32 @@ private function generateItems(iterable $items, array $tagKeys): \Generator } } - private function getTagVersions(array $tagsByKey, array &$invalidatedTags = []) + private function getTagVersions(array $tagsByKey) { - $tagVersions = $invalidatedTags; + $tagVersions = []; + $fetchTagVersions = false; foreach ($tagsByKey as $tags) { $tagVersions += $tags; + + foreach ($tags as $tag => $version) { + if ($tagVersions[$tag] !== $version) { + unset($this->knownTagVersions[$tag]); + } + } } if (!$tagVersions) { return []; } - if (!$fetchTagVersions = 1 !== \func_num_args()) { - foreach ($tagsByKey as $tags) { - foreach ($tags as $tag => $version) { - if ($tagVersions[$tag] > $version) { - $tagVersions[$tag] = $version; - } - } - } - } - $now = microtime(true); $tags = []; foreach ($tagVersions as $tag => $version) { $tags[$tag.static::TAGS_PREFIX] = $tag; - if ($fetchTagVersions || !isset($this->knownTagVersions[$tag])) { + if ($fetchTagVersions || ($this->knownTagVersions[$tag][1] ?? null) !== $version || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) { + // reuse previously fetched tag versions up to the ttl $fetchTagVersions = true; - continue; - } - $version -= $this->knownTagVersions[$tag][1]; - if ((0 !== $version && 1 !== $version) || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) { - // reuse previously fetched tag versions up to the ttl, unless we are storing items or a potential miss arises - $fetchTagVersions = true; - } else { - $this->knownTagVersions[$tag][1] += $version; } } @@ -420,14 +409,20 @@ private function getTagVersions(array $tagsByKey, array &$invalidatedTags = []) return $tagVersions; } + $newTags = []; + $newVersion = null; foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) { - $tagVersions[$tag = $tags[$tag]] = $version->get() ?: 0; - if (isset($invalidatedTags[$tag])) { - $invalidatedTags[$tag] = $version->set(++$tagVersions[$tag]); + if (!$version->isHit()) { + $newTags[$tag] = $version->set($newVersion ?? $newVersion = random_int(\PHP_INT_MIN, \PHP_INT_MAX)); } + $tagVersions[$tag = $tags[$tag]] = $version->get(); $this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]]; } + if ($newTags) { + (self::$saveTags)($this->tags, $newTags); + } + return $tagVersions; } } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php index 7e69001648fcc..b688ad46ed440 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php @@ -40,25 +40,6 @@ public static function tearDownAfterClass(): void (new Filesystem())->remove(sys_get_temp_dir().'/symfony-cache'); } - /** - * Test feature specific to TagAwareAdapter as it implicit needs to save deferred when also saving expiry info. - */ - public function testInvalidateCommitsSeperatePools() - { - $pool1 = $this->createCachePool(); - - $foo = $pool1->getItem('foo'); - $foo->tag('tag'); - - $pool1->saveDeferred($foo->set('foo')); - $pool1->invalidateTags(['tag']); - - $pool2 = $this->createCachePool(); - $foo = $pool2->getItem('foo'); - - $this->assertTrue($foo->isHit()); - } - public function testPrune() { $cache = new TagAwareAdapter($this->getPruneableMock()); @@ -84,6 +65,7 @@ public function testKnownTagVersionsTtl() $tag = $this->createMock(CacheItemInterface::class); $tag->expects(self::exactly(2))->method('get')->willReturn(10); + $tag->expects(self::exactly(2))->method('set')->willReturn($tag); $tagsPool->expects(self::exactly(2))->method('getItems')->willReturn([ 'baz'.TagAwareAdapter::TAGS_PREFIX => $tag, From 720e489332ccd343a13c6b13c16567a037cd533f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 15 Nov 2021 09:21:06 +0100 Subject: [PATCH 06/42] [DependencyInjection] fix return type annotation --- .../Component/DependencyInjection/Loader/YamlFileLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index fd716d4882cc9..a06eebf7a63b0 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -743,7 +743,7 @@ private function parseCallable($callable, string $parameter, string $id, string /** * Loads a YAML file. * - * @return array + * @return array|null * * @throws InvalidArgumentException when the given file is not a local file or when it does not exist */ From aa4fb0c2c4c3a5ef135ea671c7c6cd03ece56d9e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 13 Nov 2021 15:39:10 +0100 Subject: [PATCH 07/42] Never rely on dynamic properties --- .../Tests/LazyProxy/PhpDumper/ProxyDumperTest.php | 2 ++ .../Tests/ContainerBuilderTest.php | 1 - .../Tests/Fixtures/StdClassDecorator.php | 2 ++ .../Tests/Fixtures/includes/classes.php | 6 ++++++ .../EventDispatcher/Tests/EventDispatcherTest.php | 1 + .../HttpKernel/Tests/Fixtures/KernelForTest.php | 2 +- src/Symfony/Component/Mime/Part/DataPart.php | 5 +++++ src/Symfony/Component/Mime/Part/SMimePart.php | 5 +++++ src/Symfony/Component/Mime/Part/TextPart.php | 5 +++++ .../Core/Exception/AuthenticationException.php | 9 +++++++++ ...ustomUserMessageAuthenticationExceptionTest.php | 2 ++ src/Symfony/Component/Validator/CHANGELOG.md | 2 +- src/Symfony/Component/Validator/Constraint.php | 14 +++++++++----- .../Component/Validator/Constraints/Composite.php | 6 +++--- .../Component/Validator/Mapping/ClassMetadata.php | 4 ++-- .../Component/Validator/Mapping/MemberMetadata.php | 4 ++-- .../Component/VarExporter/Internal/Exporter.php | 2 +- .../Component/VarExporter/Internal/Registry.php | 6 +++--- .../VarExporter/Tests/VarExporterTest.php | 2 ++ 19 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php index 91b1af2892ff4..971f68cb74a58 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -196,6 +196,8 @@ function ($definition) { final class DummyClass implements DummyInterface, SunnyInterface { + private $ref; + public function dummy() { return $this; diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 7cbd9acdd5f31..38f975deb4162 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -253,7 +253,6 @@ public function testGetServiceIds() { $builder = new ContainerBuilder(); $builder->register('foo', 'stdClass'); - $builder->bar = $bar = new \stdClass(); $builder->register('bar', 'stdClass'); $this->assertEquals( [ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/StdClassDecorator.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/StdClassDecorator.php index 9131c53961e2f..20c78f2bf68be 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/StdClassDecorator.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/StdClassDecorator.php @@ -4,6 +4,8 @@ final class StdClassDecorator { + public $foo; + public function __construct(\stdClass $foo) { $this->foo = $foo; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php index dcac28effc4ee..bc68c74fd7776 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php @@ -142,6 +142,8 @@ public static function create($config) class FoobarCircular { + public $foo; + public function __construct(FooCircular $foo) { $this->foo = $foo; @@ -150,6 +152,8 @@ public function __construct(FooCircular $foo) class FooCircular { + public $bar; + public function __construct(BarCircular $bar) { $this->bar = $bar; @@ -158,6 +162,8 @@ public function __construct(BarCircular $bar) class BarCircular { + public $foobar; + public function addFoobar(FoobarCircular $foobar) { $this->foobar = $foobar; diff --git a/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php index 62097774cd38e..67e78ac25ffa1 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php @@ -458,6 +458,7 @@ public function __invoke() class TestEventListener { + public $name; public $preFooInvoked = false; public $postFooInvoked = false; diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForTest.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForTest.php index f3b1951b832a6..9b0ca43d1eb68 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForTest.php @@ -19,7 +19,7 @@ class KernelForTest extends Kernel { public function getBundleMap() { - return $this->bundleMap; + return []; } public function registerBundles(): iterable diff --git a/src/Symfony/Component/Mime/Part/DataPart.php b/src/Symfony/Component/Mime/Part/DataPart.php index 18e474df65bcb..75bcb64a9a9cb 100644 --- a/src/Symfony/Component/Mime/Part/DataPart.php +++ b/src/Symfony/Component/Mime/Part/DataPart.php @@ -20,6 +20,9 @@ */ class DataPart extends TextPart { + /** @internal */ + protected $_parent; + private static $mimeTypes; private $filename; @@ -32,6 +35,8 @@ class DataPart extends TextPart */ public function __construct($body, string $filename = null, string $contentType = null, string $encoding = null) { + unset($this->_parent); + if (null === $contentType) { $contentType = 'application/octet-stream'; } diff --git a/src/Symfony/Component/Mime/Part/SMimePart.php b/src/Symfony/Component/Mime/Part/SMimePart.php index 1dfc1aef0367a..b4c932a6787f4 100644 --- a/src/Symfony/Component/Mime/Part/SMimePart.php +++ b/src/Symfony/Component/Mime/Part/SMimePart.php @@ -18,6 +18,9 @@ */ class SMimePart extends AbstractPart { + /** @internal */ + protected $_headers; + private $body; private $type; private $subtype; @@ -28,6 +31,8 @@ class SMimePart extends AbstractPart */ public function __construct($body, string $type, string $subtype, array $parameters) { + unset($this->_headers); + parent::__construct(); if (!\is_string($body) && !is_iterable($body)) { diff --git a/src/Symfony/Component/Mime/Part/TextPart.php b/src/Symfony/Component/Mime/Part/TextPart.php index b3162e4655043..013a6dfe592d0 100644 --- a/src/Symfony/Component/Mime/Part/TextPart.php +++ b/src/Symfony/Component/Mime/Part/TextPart.php @@ -23,6 +23,9 @@ */ class TextPart extends AbstractPart { + /** @internal */ + protected $_headers; + private static $encoders = []; private $body; @@ -37,6 +40,8 @@ class TextPart extends AbstractPart */ public function __construct($body, ?string $charset = 'utf-8', string $subtype = 'plain', string $encoding = null) { + unset($this->_headers); + parent::__construct(); if (!\is_string($body) && !\is_resource($body)) { diff --git a/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php b/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php index 04a1212e21bfb..d64715a4c09bd 100644 --- a/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php +++ b/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php @@ -21,8 +21,17 @@ */ class AuthenticationException extends RuntimeException { + /** @internal */ + protected $serialized; + private $token; + public function __construct(string $message = '', int $code = 0, \Throwable $previous = null) + { + unset($this->serialized); + parent::__construct($message, $code, $previous); + } + /** * Get the token. * diff --git a/src/Symfony/Component/Security/Core/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php b/src/Symfony/Component/Security/Core/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php index 9726707506cbb..0bcba2d84a653 100644 --- a/src/Symfony/Component/Security/Core/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php @@ -17,6 +17,8 @@ class ChildCustomUserMessageAuthenticationException extends CustomUserMessageAuthenticationException { + public $childMember; + public function __serialize(): array { return [$this->childMember, parent::__serialize()]; diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index f1be53a271402..172bb9fb22704 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -96,7 +96,7 @@ CHANGELOG 3.2.0 ----- - * deprecated `Tests\Constraints\AbstractContraintValidatorTest` in favor of `Test\ConstraintValidatorTestCase` + * deprecated `Tests\Constraints\AbstractConstraintValidatorTest` in favor of `Test\ConstraintValidatorTestCase` * added support for PHP constants in YAML configuration files 3.1.0 diff --git a/src/Symfony/Component/Validator/Constraint.php b/src/Symfony/Component/Validator/Constraint.php index 2cddc8d21b5a1..17b93176d0f4a 100644 --- a/src/Symfony/Component/Validator/Constraint.php +++ b/src/Symfony/Component/Validator/Constraint.php @@ -25,8 +25,6 @@ * * Constraint instances are immutable and serializable. * - * @property string[] $groups The groups that the constraint belongs to - * * @author Bernhard Schussek */ abstract class Constraint @@ -58,6 +56,13 @@ abstract class Constraint */ public $payload; + /** + * The groups that the constraint belongs to. + * + * @var string[] + */ + public $groups; + /** * Returns the name of the given error code. * @@ -105,14 +110,13 @@ public static function getErrorName($errorCode) */ public function __construct($options = null) { + unset($this->groups); // enable lazy initialization + $defaultOption = $this->getDefaultOption(); $invalidOptions = []; $missingOptions = array_flip((array) $this->getRequiredOptions()); $knownOptions = get_class_vars(static::class); - // The "groups" option is added to the object lazily - $knownOptions['groups'] = true; - if (\is_array($options) && isset($options['value']) && !property_exists($this, 'value')) { if (null === $defaultOption) { throw new ConstraintDefinitionException(sprintf('No default option is configured for constraint "%s".', static::class)); diff --git a/src/Symfony/Component/Validator/Constraints/Composite.php b/src/Symfony/Component/Validator/Constraints/Composite.php index d8c6079ee2d57..8d8fe89fcb1ec 100644 --- a/src/Symfony/Component/Validator/Constraints/Composite.php +++ b/src/Symfony/Component/Validator/Constraints/Composite.php @@ -79,7 +79,7 @@ public function __construct($options = null) } } - if (!property_exists($this, 'groups')) { + if (!isset(((array) $this)['groups'])) { $mergedGroups = []; foreach ($nestedConstraints as $constraint) { @@ -96,7 +96,7 @@ public function __construct($options = null) } foreach ($nestedConstraints as $constraint) { - if (property_exists($constraint, 'groups')) { + if (isset(((array) $constraint)['groups'])) { $excessGroups = array_diff($constraint->groups, $this->groups); if (\count($excessGroups) > 0) { @@ -141,7 +141,7 @@ abstract protected function getCompositeOption(); * * @return Constraint[] */ - public function getNestedContraints() + public function getNestedConstraints() { /* @var Constraint[] $nestedConstraints */ return $this->{$this->getCompositeOption()}; diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php index 55f05f59e7fa0..482b892681b3c 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php @@ -490,8 +490,8 @@ private function checkConstraint(Constraint $constraint) } if ($constraint instanceof Composite) { - foreach ($constraint->getNestedContraints() as $nestedContraint) { - $this->checkConstraint($nestedContraint); + foreach ($constraint->getNestedConstraints() as $nestedConstraint) { + $this->checkConstraint($nestedConstraint); } } } diff --git a/src/Symfony/Component/Validator/Mapping/MemberMetadata.php b/src/Symfony/Component/Validator/Mapping/MemberMetadata.php index ab2e0a029fc4d..712e6751b0db3 100644 --- a/src/Symfony/Component/Validator/Mapping/MemberMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/MemberMetadata.php @@ -188,8 +188,8 @@ private function checkConstraint(Constraint $constraint) } if ($constraint instanceof Composite) { - foreach ($constraint->getNestedContraints() as $nestedContraint) { - $this->checkConstraint($nestedContraint); + foreach ($constraint->getNestedConstraints() as $nestedConstraint) { + $this->checkConstraint($nestedConstraint); } } } diff --git a/src/Symfony/Component/VarExporter/Internal/Exporter.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php index 44e2e7a833e61..0d7310c3fb094 100644 --- a/src/Symfony/Component/VarExporter/Internal/Exporter.php +++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php @@ -286,7 +286,7 @@ private static function exportRegistry(Registry $value, string $indent, string $ $r = '\\'.Registry::class; $j = -1; - foreach ($value as $k => $class) { + foreach ($value->classes as $k => $class) { if (':' === ($class[1] ?? null)) { $serializables[$k] = $class; continue; diff --git a/src/Symfony/Component/VarExporter/Internal/Registry.php b/src/Symfony/Component/VarExporter/Internal/Registry.php index 49f3c4833a1bb..24b77b9ef6f72 100644 --- a/src/Symfony/Component/VarExporter/Internal/Registry.php +++ b/src/Symfony/Component/VarExporter/Internal/Registry.php @@ -27,11 +27,11 @@ class Registry public static $cloneable = []; public static $instantiableWithoutConstructor = []; + public $classes = []; + public function __construct(array $classes) { - foreach ($classes as $i => $class) { - $this->$i = $class; - } + $this->classes = $classes; } public static function unserialize($objects, $serializables) diff --git a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php index 5bd0757e7c282..73b9f163e99b2 100644 --- a/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php +++ b/src/Symfony/Component/VarExporter/Tests/VarExporterTest.php @@ -397,6 +397,8 @@ public function __construct() class Php74Serializable implements \Serializable { + public $foo; + public function __serialize(): array { return [$this->foo = new \stdClass()]; From 2658fed9cabfcfacf8e920a54e4e84bd4c5e1344 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 15 Nov 2021 19:35:13 +0100 Subject: [PATCH 08/42] [DependencyInjection] fix creating 2nd container instances --- src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php | 2 +- .../Tests/Fixtures/php/services9_lazy_inlined_factories.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index d7ca758b6f6c7..a941bf4227ee2 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -1485,7 +1485,7 @@ private function addInlineRequires(bool $hasProxyClasses): string } if ($hasProxyClasses) { - $code .= "\n include __DIR__.'/proxy-classes.php';"; + $code .= "\n include_once __DIR__.'/proxy-classes.php';"; } return $code ? sprintf("\n \$this->privates['service_container'] = function () {%s\n };\n", $code) : ''; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt index bed495db0d3c6..78e4ff41c13a9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt @@ -60,7 +60,7 @@ class ProjectServiceContainer extends Container $this->aliases = []; $this->privates['service_container'] = function () { - include __DIR__.'/proxy-classes.php'; + include_once __DIR__.'/proxy-classes.php'; }; } From f3aedb18c1f06a6b3a89e73d8fa3bb4ae27ba51b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 15 Nov 2021 20:25:06 +0100 Subject: [PATCH 09/42] [Cache] fix releasing not acquired locks --- src/Symfony/Component/Cache/LockRegistry.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Cache/LockRegistry.php b/src/Symfony/Component/Cache/LockRegistry.php index af4c11382f070..3fcb4081c3dda 100644 --- a/src/Symfony/Component/Cache/LockRegistry.php +++ b/src/Symfony/Component/Cache/LockRegistry.php @@ -102,6 +102,7 @@ public static function compute(callable $callback, ItemInterface $item, bool &$s while (true) { try { + $locked = false; // race to get the lock in non-blocking mode if ($wouldBlock = \function_exists('sem_get')) { $locked = @sem_acquire($lock, true); @@ -137,10 +138,12 @@ public static function compute(callable $callback, ItemInterface $item, bool &$s flock($lock, \LOCK_SH); } } finally { - if (\function_exists('sem_get')) { - sem_remove($lock); - } else { - flock($lock, \LOCK_UN); + if ($locked) { + if (\function_exists('sem_get')) { + sem_remove($lock); + } else { + flock($lock, \LOCK_UN); + } } unset(self::$lockedKeys[$key]); } From e7e8a5bc89567bb020c5c774d3d3c853dccaec5e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 16 Nov 2021 08:10:03 +0100 Subject: [PATCH 10/42] Fix typos in a test message --- .../Tests/DependencyInjection/FrameworkExtensionTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index bd311d113895b..e6135d93ca320 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -1884,7 +1884,7 @@ public function testIfNotifierTransportsAreKnownByFrameworkExtension() foreach ((new Finder())->in(\dirname(__DIR__, 4).'/Component/Notifier/Bridge')->directories()->depth(0)->exclude('Mercure') as $bridgeDirectory) { $transportFactoryName = strtolower($bridgeDirectory->getFilename()); - $this->assertTrue($container->hasDefinition('notifier.transport_factory.'.$transportFactoryName), sprintf('Did you forget to add the TransportFactory: "%s" to the $classToServices array in the FrameworkBundleExtension?', $bridgeDirectory->getFilename())); + $this->assertTrue($container->hasDefinition('notifier.transport_factory.'.$transportFactoryName), sprintf('Did you forget to add the "%s" TransportFactory to the $classToServices array in FrameworkExtension?', $bridgeDirectory->getFilename())); } } From 2378a89332dfb4c16ae952186157bd9edc3df1f0 Mon Sep 17 00:00:00 2001 From: Roman Martinuk Date: Sun, 14 Nov 2021 11:16:02 +0300 Subject: [PATCH 11/42] [Cache] fix dbindex Redis --- .../Component/Cache/Tests/Adapter/RedisAdapterTest.php | 1 + src/Symfony/Component/Cache/Traits/RedisTrait.php | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php index d961187aeca3a..7bb16573db32f 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php @@ -112,6 +112,7 @@ public function testInvalidCreateConnection(string $dsn) public function provideInvalidCreateConnection(): array { return [ + ['redis://localhost/foo'], ['foo://localhost'], ['redis://'], ]; diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index 990678165624a..5365a53b18f15 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -147,9 +147,13 @@ public static function createConnection($dsn, array $options = []) } if (isset($params['host']) || isset($params['path'])) { - if (!isset($params['dbindex']) && isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) { - $params['dbindex'] = $m[1]; - $params['path'] = substr($params['path'], 0, -\strlen($m[0])); + if (!isset($params['dbindex']) && isset($params['path'])) { + if (preg_match('#/(\d+)$#', $params['path'], $m)) { + $params['dbindex'] = $m[1]; + $params['path'] = substr($params['path'], 0, -\strlen($m[0])); + } else { + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: "%s", the "dbindex" parameter must be a number.', $dsn)); + } } if (isset($params['host'])) { From 1a4452632ae1719deab18f834fd9f2d2bb92d618 Mon Sep 17 00:00:00 2001 From: Mathieu Santostefano Date: Wed, 10 Nov 2021 09:39:01 +0100 Subject: [PATCH 12/42] Fix Loco Provider --- .../Translation/Bridge/Loco/LocoProvider.php | 19 +++++++++-- .../Bridge/Loco/Tests/LocoProviderTest.php | 34 ++++++++++--------- .../Translation/Bridge/Loco/composer.json | 1 + 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php b/src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php index ea4232c4ee75a..b5544070a5678 100644 --- a/src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php +++ b/src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php @@ -14,6 +14,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\Translation\Exception\ProviderException; use Symfony\Component\Translation\Loader\LoaderInterface; +use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Provider\ProviderInterface; use Symfony\Component\Translation\TranslatorBag; use Symfony\Component\Translation\TranslatorBagInterface; @@ -60,7 +61,7 @@ public function write(TranslatorBagInterface $translatorBag): void } foreach ($catalogue->all() as $domain => $messages) { - $createdIds = $this->createAssets(array_keys($messages)); + $createdIds = $this->createAssets(array_keys($messages), $domain); if ($createdIds) { $this->tagsAssets($createdIds, $domain); } @@ -117,7 +118,18 @@ public function read(array $domains, array $locales): TranslatorBag throw new ProviderException('Unable to read the Loco response: '.$responseContent, $response); } - $translatorBag->addCatalogue($this->loader->load($responseContent, $locale, $domain)); + $locoCatalogue = $this->loader->load($responseContent, $locale, $domain); + $catalogue = new MessageCatalogue($locale); + + foreach ($locoCatalogue->all($domain) as $key => $message) { + if (str_starts_with($key, $domain.'__')) { + $key = substr($key, \strlen($domain) + 2); + } + + $catalogue->set($key, $message, $domain); + } + + $translatorBag->addCatalogue($catalogue); } return $translatorBag; @@ -166,13 +178,14 @@ private function getAssetsIds(string $domain): array }, $response->toArray(false)); } - private function createAssets(array $keys): array + private function createAssets(array $keys, string $domain): array { $responses = $createdIds = []; foreach ($keys as $key) { $responses[$key] = $this->client->request('POST', 'assets', [ 'body' => [ + 'id' => $domain.'__'.$key, // must be globally unique, not only per domain 'text' => $key, 'type' => 'text', 'default' => 'untranslated', diff --git a/src/Symfony/Component/Translation/Bridge/Loco/Tests/LocoProviderTest.php b/src/Symfony/Component/Translation/Bridge/Loco/Tests/LocoProviderTest.php index 3d3dd8af985ca..09c779324310b 100644 --- a/src/Symfony/Component/Translation/Bridge/Loco/Tests/LocoProviderTest.php +++ b/src/Symfony/Component/Translation/Bridge/Loco/Tests/LocoProviderTest.php @@ -63,6 +63,7 @@ public function testCompleteWriteProcess() $responses = [ 'createAsset1' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface { $expectedBody = http_build_query([ + 'id' => 'messages__a', 'text' => 'a', 'type' => 'text', 'default' => 'untranslated', @@ -72,7 +73,7 @@ public function testCompleteWriteProcess() $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame($expectedBody, $options['body']); - return new MockResponse('{"id": "1337"}', ['http_code' => 201]); + return new MockResponse('{"id": "messages__a"}', ['http_code' => 201]); }, 'getTags1' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface { $this->assertSame('GET', $method); @@ -93,12 +94,13 @@ public function testCompleteWriteProcess() $this->assertSame('POST', $method); $this->assertSame('https://localise.biz/api/tags/messages.json', $url); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); - $this->assertSame('1337', $options['body']); + $this->assertSame('messages__a', $options['body']); return new MockResponse(); }, 'createAsset2' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface { $expectedBody = http_build_query([ + 'id' => 'validators__post.num_comments', 'text' => 'post.num_comments', 'type' => 'text', 'default' => 'untranslated', @@ -108,7 +110,7 @@ public function testCompleteWriteProcess() $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame($expectedBody, $options['body']); - return new MockResponse('{"id": "1234"}', ['http_code' => 201]); + return new MockResponse('{"id": "validators__post.num_comments"}', ['http_code' => 201]); }, 'getTags2' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface { $this->assertSame('GET', $method); @@ -129,7 +131,7 @@ public function testCompleteWriteProcess() $this->assertSame('POST', $method); $this->assertSame('https://localise.biz/api/tags/validators.json', $url); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); - $this->assertSame('1234', $options['body']); + $this->assertSame('validators__post.num_comments', $options['body']); return new MockResponse(); }, @@ -146,11 +148,11 @@ public function testCompleteWriteProcess() $this->assertSame(['filter' => 'messages'], $options['query']); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); - return new MockResponse('[{"id":"1337"}]'); + return new MockResponse('[{"id":"messages__a"}]'); }, 'translateAsset1' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface { $this->assertSame('POST', $method); - $this->assertSame('https://localise.biz/api/translations/1337/en', $url); + $this->assertSame('https://localise.biz/api/translations/messages__a/en', $url); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame('trans_en_a', $options['body']); @@ -162,11 +164,11 @@ public function testCompleteWriteProcess() $this->assertSame(['filter' => 'validators'], $options['query']); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); - return new MockResponse('[{"id":"1234"}]'); + return new MockResponse('[{"id":"validators__post.num_comments"}]'); }, 'translateAsset2' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface { $this->assertSame('POST', $method); - $this->assertSame('https://localise.biz/api/translations/1234/en', $url); + $this->assertSame('https://localise.biz/api/translations/validators__post.num_comments/en', $url); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame('{count, plural, one {# comment} other {# comments}}', $options['body']); @@ -193,11 +195,11 @@ public function testCompleteWriteProcess() $this->assertSame(['filter' => 'messages'], $options['query']); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); - return new MockResponse('[{"id":"1337"}]'); + return new MockResponse('[{"id":"messages__a"}]'); }, 'translateAsset3' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface { $this->assertSame('POST', $method); - $this->assertSame('https://localise.biz/api/translations/1337/fr', $url); + $this->assertSame('https://localise.biz/api/translations/messages__a/fr', $url); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame('trans_fr_a', $options['body']); @@ -209,11 +211,11 @@ public function testCompleteWriteProcess() $this->assertSame(['filter' => 'validators'], $options['query']); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); - return new MockResponse('[{"id":"1234"}]'); + return new MockResponse('[{"id":"validators__post.num_comments"}]'); }, 'translateAsset4' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface { $this->assertSame('POST', $method); - $this->assertSame('https://localise.biz/api/translations/1234/fr', $url); + $this->assertSame('https://localise.biz/api/translations/validators__post.num_comments/fr', $url); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame('{count, plural, one {# commentaire} other {# commentaires}}', $options['body']); @@ -321,11 +323,11 @@ function (string $method, string $url, array $options = []): ResponseInterface { $this->assertSame('https://localise.biz/api/assets?filter=messages', $url); $this->assertSame(['filter' => 'messages'], $options['query']); - return new MockResponse('[{"id":"1337"}]'); + return new MockResponse('[{"id":"messages__a"}]'); }, function (string $method, string $url): MockResponse { $this->assertSame('DELETE', $method); - $this->assertSame('https://localise.biz/api/assets/1337.json', $url); + $this->assertSame('https://localise.biz/api/assets/messages__a.json', $url); return new MockResponse(); }, @@ -334,11 +336,11 @@ function (string $method, string $url, array $options = []): ResponseInterface { $this->assertSame('https://localise.biz/api/assets?filter=validators', $url); $this->assertSame(['filter' => 'validators'], $options['query']); - return new MockResponse('[{"id":"1234"}]'); + return new MockResponse('[{"id":"validators__post.num_comments"}]'); }, function (string $method, string $url): MockResponse { $this->assertSame('DELETE', $method); - $this->assertSame('https://localise.biz/api/assets/1234.json', $url); + $this->assertSame('https://localise.biz/api/assets/validators__post.num_comments.json', $url); return new MockResponse(); }, diff --git a/src/Symfony/Component/Translation/Bridge/Loco/composer.json b/src/Symfony/Component/Translation/Bridge/Loco/composer.json index 550a2f5b6de74..36d2eea52cb6d 100644 --- a/src/Symfony/Component/Translation/Bridge/Loco/composer.json +++ b/src/Symfony/Component/Translation/Bridge/Loco/composer.json @@ -19,6 +19,7 @@ "php": ">=7.2.5", "symfony/http-client": "^5.3", "symfony/config": "^5.3", + "symfony/polyfill-php80": "^1.23", "symfony/translation": "^5.3" }, "autoload": { From e76ac08bee57a8b768e396faf2f808765eab505f Mon Sep 17 00:00:00 2001 From: Antonio Jose Cerezo Aranda Date: Mon, 15 Nov 2021 17:07:57 +0100 Subject: [PATCH 13/42] [Cache] Fix calculate ttl in couchbase sdk 3.0 --- .../Cache/Adapter/CouchbaseCollectionAdapter.php | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/Symfony/Component/Cache/Adapter/CouchbaseCollectionAdapter.php b/src/Symfony/Component/Cache/Adapter/CouchbaseCollectionAdapter.php index b3c565bcab850..79f648531c230 100644 --- a/src/Symfony/Component/Cache/Adapter/CouchbaseCollectionAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/CouchbaseCollectionAdapter.php @@ -27,7 +27,6 @@ */ class CouchbaseCollectionAdapter extends AbstractAdapter { - private const THIRTY_DAYS_IN_SECONDS = 2592000; private const MAX_KEY_LENGTH = 250; /** @var Collection */ @@ -123,7 +122,7 @@ public static function createConnection($dsn, array $options = []) public static function isSupported(): bool { - return \extension_loaded('couchbase') && version_compare(phpversion('couchbase'), '3.0', '>=') && version_compare(phpversion('couchbase'), '4.0', '<'); + return \extension_loaded('couchbase') && version_compare(phpversion('couchbase'), '3.0.5', '>=') && version_compare(phpversion('couchbase'), '4.0', '<'); } private static function getOptions(string $options): array @@ -206,7 +205,6 @@ protected function doSave(array $values, $lifetime) return $failed; } - $lifetime = $this->normalizeExpiry($lifetime); $upsertOptions = new UpsertOptions(); $upsertOptions->expiry($lifetime); @@ -221,13 +219,4 @@ protected function doSave(array $values, $lifetime) return [] === $ko ? true : $ko; } - - private function normalizeExpiry(int $expiry): int - { - if ($expiry && $expiry > static::THIRTY_DAYS_IN_SECONDS) { - $expiry += time(); - } - - return $expiry; - } } From e3899f5d0f202f54a2c3e8ec99ca26b3771ae560 Mon Sep 17 00:00:00 2001 From: Andrii Dembitskyi Date: Tue, 16 Nov 2021 03:44:52 -0500 Subject: [PATCH 14/42] Fix deprecation message placeholders --- src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php b/src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php index 58e1693798339..4c1ce3a6cb737 100644 --- a/src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php @@ -70,7 +70,7 @@ public function __construct($connOrDsn, string $namespace = '', int $defaultLife } $this->conn = DriverManager::getConnection(['url' => $connOrDsn]); } else { - throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be "%s" or string, "%s" given.', Connection::class, __METHOD__, get_debug_type($connOrDsn))); + throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be "%s" or string, "%s" given.', __METHOD__, Connection::class, get_debug_type($connOrDsn))); } $this->table = $options['db_table'] ?? $this->table; From fc3ccddaed98df915f135749cdd06757b3400576 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 16 Nov 2021 10:39:17 +0100 Subject: [PATCH 15/42] Improve CI script a bit --- .github/workflows/unit-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index ec426733055b5..157f6462b4e67 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -66,7 +66,7 @@ jobs: SYMFONY_VERSIONS=$(git ls-remote -q --heads | cut -f2 | grep -o '/[1-9][0-9]*\.[0-9].*' | sort -V) SYMFONY_VERSION=$(grep ' VERSION = ' src/Symfony/Component/HttpKernel/Kernel.php | grep -P -o '[0-9]+\.[0-9]+') - SYMFONY_FEATURE_BRANCH=$(curl -s https://flex.symfony.com/versions.json | jq -r '."dev-name"') + SYMFONY_FEATURE_BRANCH=$(curl -s https://raw.githubusercontent.com/symfony/recipes/flex/main/index.json | jq -r '.versions."dev-name"') # Install the phpunit-bridge from a PR if required # @@ -187,8 +187,8 @@ jobs: (cd src/Symfony/Component/HttpFoundation; mv composer.bak composer.json) PATCHED_COMPONENTS=$(git diff --name-only src/ | grep composer.json || true) - # for x.4 branches, checkout and test previous major with the patched components (only for patched components) - if [[ $PATCHED_COMPONENTS && $SYMFONY_VERSION = *.4 ]]; then + # for 5.4 LTS, checkout and test previous major with the patched components (only for patched components) + if [[ $PATCHED_COMPONENTS && $SYMFONY_VERSION = 5.4 ]]; then export FLIP='^' SYMFONY_VERSION=$(echo $SYMFONY_VERSION | awk '{print $1 - 1}') echo -e "\\n\\e[33;1mChecking out Symfony $SYMFONY_VERSION and running tests with patched components as deps\\e[0m" From f608f0b7241a84f7893a621f7993aff4988306b3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 16 Nov 2021 09:58:02 +0100 Subject: [PATCH 16/42] [Process] intersect with getenv() to populate default envs --- src/Symfony/Component/Process/Process.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 9fafddd64413d..7709482017534 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -1671,13 +1671,8 @@ private function replacePlaceholders(string $commandline, array $env) private function getDefaultEnv(): array { - $env = []; - - foreach ($_SERVER as $k => $v) { - if (\is_string($v) && false !== $v = getenv($k)) { - $env[$k] = $v; - } - } + $env = getenv(); + $env = array_intersect_key($env, $_SERVER) ?: $env; foreach ($_ENV as $k => $v) { if (\is_string($v)) { From bdf9adc26579a187430826e50a1fcf77d26e4fab Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Tue, 16 Nov 2021 18:27:11 +0100 Subject: [PATCH 17/42] [ExpressionLanguage] Fix LexerTest number types --- .../ExpressionLanguage/Tests/LexerTest.php | 18 +++++++++++++----- .../ExpressionLanguage/Tests/ParserTest.php | 4 ++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php index 29d170d739beb..67e551f587eb7 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php @@ -70,7 +70,7 @@ public function getTokenizeData() '"foo"', ], [ - [new Token('number', '3', 1)], + [new Token('number', 3, 1)], '3', ], [ @@ -84,9 +84,9 @@ public function getTokenizeData() [ [ new Token('punctuation', '(', 1), - new Token('number', '3', 2), + new Token('number', 3, 2), new Token('operator', '+', 4), - new Token('number', '5', 6), + new Token('number', 5, 6), new Token('punctuation', ')', 7), new Token('operator', '~', 9), new Token('name', 'foo', 11), @@ -96,10 +96,10 @@ public function getTokenizeData() new Token('punctuation', '.', 21), new Token('name', 'baz', 22), new Token('punctuation', '[', 25), - new Token('number', '4', 26), + new Token('number', 4, 26), new Token('punctuation', ']', 27), new Token('operator', '-', 29), - new Token('number', '1990', 31), + new Token('number', 1990, 31), ], '(3 + 5) ~ foo("bar").baz[4] - 1.99E+3', ], @@ -107,6 +107,14 @@ public function getTokenizeData() [new Token('operator', '..', 1)], '..', ], + [ + [ + new Token('number', 23, 1), + new Token('operator', '..', 3), + new Token('number', 26, 5), + ], + '23..26', + ], [ [new Token('string', '#foo', 1)], "'#foo'", diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php index a57397143993a..eb20c8ea7ed2a 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php @@ -185,6 +185,10 @@ public function getParseData() 'not foo or foo.not', ['foo'], ], + [ + new Node\BinaryNode('..', new Node\ConstantNode(0), new Node\ConstantNode(3)), + '0..3', + ], ]; } From 672545d97c10c40eaa5660b85e73d4083d48d63d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20TAMARELLE?= Date: Mon, 15 Nov 2021 22:28:13 +0100 Subject: [PATCH 18/42] Add framework config for DBAL cache adapter --- UPGRADE-5.4.md | 1 + UPGRADE-6.0.md | 1 + ...ctrineDbalCacheAdapterSchemaSubscriber.php | 4 +-- ...doCacheAdapterDoctrineSchemaSubscriber.php | 6 +++-- ...neDbalCacheAdapterSchemaSubscriberTest.php | 9 ++++--- ...cheAdapterDoctrineSchemaSubscriberTest.php | 10 +++++-- .../Bundle/FrameworkBundle/CHANGELOG.md | 1 + .../DependencyInjection/Configuration.php | 1 + .../FrameworkExtension.php | 12 ++++++++- .../Resources/config/cache.php | 27 ++++++++++++++----- .../DependencyInjection/ConfigurationTest.php | 1 + .../Cache/Adapter/DoctrineDbalAdapter.php | 2 +- .../DoctrineSchemaConfiguratorInterface.php | 26 ------------------ src/Symfony/Component/Cache/LockRegistry.php | 1 - 14 files changed, 56 insertions(+), 46 deletions(-) delete mode 100644 src/Symfony/Component/Cache/Adapter/DoctrineSchemaConfiguratorInterface.php diff --git a/UPGRADE-5.4.md b/UPGRADE-5.4.md index 57d27502d6876..a2ce35807705d 100644 --- a/UPGRADE-5.4.md +++ b/UPGRADE-5.4.md @@ -37,6 +37,7 @@ FrameworkBundle * Deprecate the public `profiler` service to private * Deprecate `get()`, `has()`, `getDoctrine()`, and `dispatchMessage()` in `AbstractController`, use method/constructor injection instead * Deprecate the `cache.adapter.doctrine` service: The Doctrine Cache library is deprecated. Either switch to Symfony Cache or use the PSR-6 adapters provided by Doctrine Cache. + * In `framework.cache` configuration, using `cache.adapter.pdo` adapter with a Doctrine DBAL connection is deprecated, use `cache.adapter.doctrine_dbal` instead. HttpKernel ---------- diff --git a/UPGRADE-6.0.md b/UPGRADE-6.0.md index 7bcba1f6d0ddf..c5506acbea75e 100644 --- a/UPGRADE-6.0.md +++ b/UPGRADE-6.0.md @@ -109,6 +109,7 @@ FrameworkBundle * Remove the `AdapterInterface` autowiring alias, use `CacheItemPoolInterface` instead * Remove `get()`, `has()`, `getDoctrine()`, and `dispatchMessage()` in `AbstractController`, use method/constructor injection instead * Deprecate the `cache.adapter.doctrine` service: The Doctrine Cache library is deprecated. Either switch to Symfony Cache or use the PSR-6 adapters provided by Doctrine Cache. + * In `framework.cache` configuration, using the `cache.adapter.pdo` with a Doctrine DBAL connection is no longer supported, use `cache.adapter.doctrine_dbal` instead. HttpFoundation -------------- diff --git a/src/Symfony/Bridge/Doctrine/SchemaListener/DoctrineDbalCacheAdapterSchemaSubscriber.php b/src/Symfony/Bridge/Doctrine/SchemaListener/DoctrineDbalCacheAdapterSchemaSubscriber.php index e61564807befd..bf9b793175f3f 100644 --- a/src/Symfony/Bridge/Doctrine/SchemaListener/DoctrineDbalCacheAdapterSchemaSubscriber.php +++ b/src/Symfony/Bridge/Doctrine/SchemaListener/DoctrineDbalCacheAdapterSchemaSubscriber.php @@ -14,7 +14,7 @@ use Doctrine\Common\EventSubscriber; use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; use Doctrine\ORM\Tools\ToolEvents; -use Symfony\Component\Cache\Adapter\DoctrineSchemaConfiguratorInterface; +use Symfony\Component\Cache\Adapter\DoctrineDbalAdapter; /** * Automatically adds the cache table needed for the DoctrineDbalAdapter of @@ -27,7 +27,7 @@ final class DoctrineDbalCacheAdapterSchemaSubscriber implements EventSubscriber private $dbalAdapters; /** - * @param iterable $dbalAdapters + * @param iterable $dbalAdapters */ public function __construct(iterable $dbalAdapters) { diff --git a/src/Symfony/Bridge/Doctrine/SchemaListener/PdoCacheAdapterDoctrineSchemaSubscriber.php b/src/Symfony/Bridge/Doctrine/SchemaListener/PdoCacheAdapterDoctrineSchemaSubscriber.php index a46d4fa814cb4..98738ed58ec75 100644 --- a/src/Symfony/Bridge/Doctrine/SchemaListener/PdoCacheAdapterDoctrineSchemaSubscriber.php +++ b/src/Symfony/Bridge/Doctrine/SchemaListener/PdoCacheAdapterDoctrineSchemaSubscriber.php @@ -16,8 +16,6 @@ use Doctrine\ORM\Tools\ToolEvents; use Symfony\Component\Cache\Adapter\PdoAdapter; -trigger_deprecation('symfony/doctrine-bridge', '5.4', 'The "%s" class is deprecated, use "%s" instead.', PdoCacheAdapterDoctrineSchemaSubscriber::class, DoctrineDbalCacheAdapterSchemaSubscriber::class); - /** * Automatically adds the cache table needed for the PdoAdapter. * @@ -41,6 +39,10 @@ public function postGenerateSchema(GenerateSchemaEventArgs $event): void { $dbalConnection = $event->getEntityManager()->getConnection(); foreach ($this->pdoAdapters as $pdoAdapter) { + if (PdoAdapter::class !== \get_class($pdoAdapter)) { + trigger_deprecation('symfony/doctrine-bridge', '5.4', 'The "%s" class is deprecated, use "%s" instead.', self::class, DoctrineDbalCacheAdapterSchemaSubscriber::class); + } + $pdoAdapter->configureSchema($event->getSchema(), $dbalConnection); } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/SchemaListener/DoctrineDbalCacheAdapterSchemaSubscriberTest.php b/src/Symfony/Bridge/Doctrine/Tests/SchemaListener/DoctrineDbalCacheAdapterSchemaSubscriberTest.php index 8f1afa99b1319..0a65b6e6bc720 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/SchemaListener/DoctrineDbalCacheAdapterSchemaSubscriberTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/SchemaListener/DoctrineDbalCacheAdapterSchemaSubscriberTest.php @@ -17,7 +17,7 @@ use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\SchemaListener\DoctrineDbalCacheAdapterSchemaSubscriber; -use Symfony\Component\Cache\Adapter\DoctrineSchemaConfiguratorInterface; +use Symfony\Component\Cache\Adapter\DoctrineDbalAdapter; class DoctrineDbalCacheAdapterSchemaSubscriberTest extends TestCase { @@ -29,14 +29,15 @@ public function testPostGenerateSchema() $entityManager->expects($this->once()) ->method('getConnection') ->willReturn($dbalConnection); + $event = new GenerateSchemaEventArgs($entityManager, $schema); - $pdoAdapter = $this->createMock(DoctrineSchemaConfiguratorInterface::class); - $pdoAdapter->expects($this->once()) + $dbalAdapter = $this->createMock(DoctrineDbalAdapter::class); + $dbalAdapter->expects($this->once()) ->method('configureSchema') ->with($schema, $dbalConnection); - $subscriber = new DoctrineDbalCacheAdapterSchemaSubscriber([$pdoAdapter]); + $subscriber = new DoctrineDbalCacheAdapterSchemaSubscriber([$dbalAdapter]); $subscriber->postGenerateSchema($event); } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/SchemaListener/PdoCacheAdapterDoctrineSchemaSubscriberTest.php b/src/Symfony/Bridge/Doctrine/Tests/SchemaListener/PdoCacheAdapterDoctrineSchemaSubscriberTest.php index 90b76328db9f9..26f42a6d48731 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/SchemaListener/PdoCacheAdapterDoctrineSchemaSubscriberTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/SchemaListener/PdoCacheAdapterDoctrineSchemaSubscriberTest.php @@ -17,6 +17,7 @@ use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\SchemaListener\PdoCacheAdapterDoctrineSchemaSubscriber; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Cache\Adapter\PdoAdapter; /** @@ -24,20 +25,25 @@ */ class PdoCacheAdapterDoctrineSchemaSubscriberTest extends TestCase { + use ExpectDeprecationTrait; + public function testPostGenerateSchema() { $schema = new Schema(); $dbalConnection = $this->createMock(Connection::class); $entityManager = $this->createMock(EntityManagerInterface::class); - $entityManager->expects($this->once()) + $entityManager->expects($this->any()) ->method('getConnection') ->willReturn($dbalConnection); + $event = new GenerateSchemaEventArgs($entityManager, $schema); $pdoAdapter = $this->createMock(PdoAdapter::class); $pdoAdapter->expects($this->once()) ->method('configureSchema') - ->with($schema, $dbalConnection); + ->with($event->getSchema(), $event->getEntityManager()->getConnection()); + + $this->expectDeprecation('Since symfony/doctrine-bridge 5.4: The "Symfony\Bridge\Doctrine\SchemaListener\PdoCacheAdapterDoctrineSchemaSubscriber" class is deprecated, use "Symfony\Bridge\Doctrine\SchemaListener\DoctrineDbalCacheAdapterSchemaSubscriber" instead.'); $subscriber = new PdoCacheAdapterDoctrineSchemaSubscriber([$pdoAdapter]); $subscriber->postGenerateSchema($event); diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 21e565cefae1b..ea913ef985cc3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -21,6 +21,7 @@ CHANGELOG * Add support for `statusCode` default parameter when loading a template directly from route using the `Symfony\Bundle\FrameworkBundle\Controller\TemplateController` controller * Deprecate `translation:update` command, use `translation:extract` instead * Add `PhpStanExtractor` support for the PropertyInfo component + * Add `cache.adapter.doctrine_dbal` service to replace `cache.adapter.pdo` when a Doctrine DBAL connection is used. 5.3 --- diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 76955b05d566f..6b4b18ffebbef 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -1077,6 +1077,7 @@ private function addCacheSection(ArrayNodeDefinition $rootNode, callable $willBe ->scalarNode('default_psr6_provider')->end() ->scalarNode('default_redis_provider')->defaultValue('redis://localhost')->end() ->scalarNode('default_memcached_provider')->defaultValue('memcached://localhost')->end() + ->scalarNode('default_doctrine_dbal_provider')->defaultValue('database_connection')->end() ->scalarNode('default_pdo_provider')->defaultValue($willBeAvailable('doctrine/dbal', Connection::class) ? 'database_connection' : null)->end() ->arrayNode('pools') ->useAttributeAsKey('name') diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index c73b3a7c3463c..6f2f0ad09bc75 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -34,6 +34,8 @@ use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\ChainAdapter; +use Symfony\Component\Cache\Adapter\DoctrineAdapter; +use Symfony\Component\Cache\Adapter\DoctrineDbalAdapter; use Symfony\Component\Cache\Adapter\TagAwareAdapter; use Symfony\Component\Cache\DependencyInjection\CachePoolPass; use Symfony\Component\Cache\Marshaller\DefaultMarshaller; @@ -2159,6 +2161,14 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con $container->removeDefinition('cache.default_marshaller'); } + if (!class_exists(DoctrineAdapter::class)) { + $container->removeDefinition('cache.adapter.doctrine'); + } + + if (!class_exists(DoctrineDbalAdapter::class)) { + $container->removeDefinition('cache.adapter.doctrine_dbal'); + } + $version = new Parameter('container.build_id'); $container->getDefinition('cache.adapter.apcu')->replaceArgument(2, $version); $container->getDefinition('cache.adapter.system')->replaceArgument(2, $version); @@ -2171,7 +2181,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con // Inline any env vars referenced in the parameter $container->setParameter('cache.prefix.seed', $container->resolveEnvPlaceholders($container->getParameter('cache.prefix.seed'), true)); } - foreach (['doctrine', 'psr6', 'redis', 'memcached', 'pdo'] as $name) { + foreach (['doctrine', 'psr6', 'redis', 'memcached', 'doctrine_dbal', 'pdo'] as $name) { if (isset($config[$name = 'default_'.$name.'_provider'])) { $container->setAlias('cache.'.$name, new Alias(CachePoolPass::getServiceProvider($container, $config[$name]), false)); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.php index ef18c7cea2f4a..e333e5f54712c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.php @@ -17,6 +17,7 @@ use Symfony\Component\Cache\Adapter\ApcuAdapter; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\DoctrineAdapter; +use Symfony\Component\Cache\Adapter\DoctrineDbalAdapter; use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\Cache\Adapter\MemcachedAdapter; use Symfony\Component\Cache\Adapter\PdoAdapter; @@ -93,10 +94,8 @@ ->call('setLogger', [service('logger')->ignoreOnInvalid()]) ->tag('cache.pool', ['clearer' => 'cache.default_clearer', 'reset' => 'reset']) ->tag('monolog.logger', ['channel' => 'cache']) - ; - if (class_exists(DoctrineAdapter::class)) { - $container->services()->set('cache.adapter.doctrine', DoctrineAdapter::class) + ->set('cache.adapter.doctrine', DoctrineAdapter::class) ->abstract() ->args([ abstract_arg('Doctrine provider service'), @@ -110,11 +109,8 @@ 'reset' => 'reset', ]) ->tag('monolog.logger', ['channel' => 'cache']) - ->deprecate('symfony/framework-bundle', '5.4', 'The abstract service "%service_id%" is deprecated.') - ; - } + ->deprecate('symfony/framework-bundle', '5.4', 'The "%service_id%" service inherits from "cache.adapter.doctrine" which is deprecated.') - $container->services() ->set('cache.adapter.filesystem', FilesystemAdapter::class) ->abstract() ->args([ @@ -188,6 +184,23 @@ ]) ->tag('monolog.logger', ['channel' => 'cache']) + ->set('cache.adapter.doctrine_dbal', DoctrineDbalAdapter::class) + ->abstract() + ->args([ + abstract_arg('DBAL connection service'), + '', // namespace + 0, // default lifetime + [], // table options + service('cache.default_marshaller')->ignoreOnInvalid(), + ]) + ->call('setLogger', [service('logger')->ignoreOnInvalid()]) + ->tag('cache.pool', [ + 'provider' => 'cache.default_doctrine_dbal_provider', + 'clearer' => 'cache.default_clearer', + 'reset' => 'reset', + ]) + ->tag('monolog.logger', ['channel' => 'cache']) + ->set('cache.adapter.pdo', PdoAdapter::class) ->abstract() ->args([ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 8e3dc42faffb3..0b6ccbf3afab3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -504,6 +504,7 @@ protected static function getBundleDefaultConfig() 'directory' => '%kernel.cache_dir%/pools/app', 'default_redis_provider' => 'redis://localhost', 'default_memcached_provider' => 'memcached://localhost', + 'default_doctrine_dbal_provider' => 'database_connection', 'default_pdo_provider' => ContainerBuilder::willBeAvailable('doctrine/dbal', Connection::class, ['symfony/framework-bundle']) ? 'database_connection' : null, 'prefix_seed' => '_%kernel.project_dir%.%kernel.container_class%', ], diff --git a/src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php b/src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php index 4c1ce3a6cb737..73f0ea6bcc480 100644 --- a/src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php @@ -23,7 +23,7 @@ use Symfony\Component\Cache\Marshaller\MarshallerInterface; use Symfony\Component\Cache\PruneableInterface; -final class DoctrineDbalAdapter extends AbstractAdapter implements PruneableInterface, DoctrineSchemaConfiguratorInterface +class DoctrineDbalAdapter extends AbstractAdapter implements PruneableInterface { protected $maxIdLength = 255; diff --git a/src/Symfony/Component/Cache/Adapter/DoctrineSchemaConfiguratorInterface.php b/src/Symfony/Component/Cache/Adapter/DoctrineSchemaConfiguratorInterface.php deleted file mode 100644 index 57812cabaf396..0000000000000 --- a/src/Symfony/Component/Cache/Adapter/DoctrineSchemaConfiguratorInterface.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Cache\Adapter; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Schema\Schema; - -/** - * @internal - */ -interface DoctrineSchemaConfiguratorInterface -{ - /** - * Adds the Table to the Schema if the adapter uses this Connection. - */ - public function configureSchema(Schema $schema, Connection $forConnection): void; -} diff --git a/src/Symfony/Component/Cache/LockRegistry.php b/src/Symfony/Component/Cache/LockRegistry.php index 3fcb4081c3dda..910c11fae29c4 100644 --- a/src/Symfony/Component/Cache/LockRegistry.php +++ b/src/Symfony/Component/Cache/LockRegistry.php @@ -43,7 +43,6 @@ final class LockRegistry __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'CouchbaseCollectionAdapter.php', __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'DoctrineAdapter.php', __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'DoctrineDbalAdapter.php', - __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'DoctrineSchemaConfiguratorInterface.php', __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'FilesystemAdapter.php', __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'FilesystemTagAwareAdapter.php', __DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'MemcachedAdapter.php', From 588e36d2d88b3144ade7ac6671030c28a63f5216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Wed, 17 Nov 2021 00:51:15 +0100 Subject: [PATCH 19/42] AddMake ExpressionVoter Cacheable --- .../Authorization/Voter/ExpressionVoter.php | 12 ++++++++- .../AccessDecisionManagerTest.php | 25 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php index f02c42460ec37..16280725cd7a0 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php @@ -24,7 +24,7 @@ * * @author Fabien Potencier */ -class ExpressionVoter implements VoterInterface +class ExpressionVoter implements CacheableVoterInterface { private $expressionLanguage; private $trustResolver; @@ -39,6 +39,16 @@ public function __construct(ExpressionLanguage $expressionLanguage, Authenticati $this->roleHierarchy = $roleHierarchy; } + public function supportsAttribute(string $attribute): bool + { + return false; + } + + public function supportsType(string $subjectType): bool + { + return true; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php index dd36b428e8ffc..5f606cc68d0fe 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php @@ -313,6 +313,31 @@ public function testCacheableVotersNotCalled() $this->assertFalse($manager->decide($token, ['foo'], 'bar')); } + public function testCacheableVotersWithMultipleAttributesAndNonString() + { + $token = $this->createMock(TokenInterface::class); + $voter = $this->getMockBuilder(CacheableVoterInterface::class)->getMockForAbstractClass(); + $voter + ->expects($this->once()) + ->method('supportsAttribute') + ->with('foo') + ->willReturn(false); + $voter + // Voter does not support "foo", but given 1337 is not a string, it implicitly supports it. + ->expects($this->once()) + ->method('supportsType') + ->with('string') + ->willReturn(true); + $voter + ->expects($this->once()) + ->method('vote') + ->with($token, 'bar', ['foo', 1337]) + ->willReturn(VoterInterface::ACCESS_GRANTED); + + $manager = new AccessDecisionManager([$voter]); + $this->assertTrue($manager->decide($token, ['foo', 1337], 'bar', true)); + } + protected function getVoters($grants, $denies, $abstains) { $voters = []; From 2c945808a992f2f5c744b38652192b36e985472c Mon Sep 17 00:00:00 2001 From: Shakhobiddin <38453814+shokhaa@users.noreply.github.com> Date: Wed, 17 Nov 2021 11:42:14 +0500 Subject: [PATCH 20/42] Update validators.uz.xlf --- .../Validator/Resources/translations/validators.uz.xlf | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf index b32fa31b15442..1a21028cce82a 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf @@ -394,6 +394,16 @@ This value is not a valid CSS color. Bu qiymat haqiqiy CSS rangi emas. + + This value is not a valid CIDR notation. + Qiymat CIDR belgisiga mos kelmaydi. + + + The value of the netmask should be between {{ min }} and {{ max }}. + Tarmoq niqobining qiymati {{ min }} va {{ max }} oralig'ida bo'lishi kerak. + + + From f7b5297f15b1c6f6b49af947fdab289555f1e537 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 17 Nov 2021 09:14:18 +0100 Subject: [PATCH 21/42] [DependencyInjection] fix preloading --- src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php | 2 +- .../Tests/Fixtures/php/services10_as_files.txt | 2 +- .../Tests/Fixtures/php/services9_as_files.txt | 2 +- .../Tests/Fixtures/php/services9_inlined_factories.txt | 2 +- .../Tests/Fixtures/php/services9_lazy_inlined_factories.txt | 2 +- .../Tests/Fixtures/php/services_non_shared_lazy_as_files.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index a941bf4227ee2..5f0dc1c07e081 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -331,7 +331,7 @@ class %s extends {$options['class']} } require $autoloadFile; -(require __DIR__.'/Container{$hash}/{$options['class']}.php')->set(\\Container{$hash}\\{$options['class']}::class, null); +(require __DIR__.'/{$options['class']}.php')->set(\\Container{$hash}\\{$options['class']}::class, null); $preloadedFiles \$classes = []; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10_as_files.txt index 7a6cb0656442b..7730b9452b3c7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10_as_files.txt @@ -133,7 +133,7 @@ if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) { } require dirname(__DIR__, %d).'%svendor/autoload.php'; -(require __DIR__.'/Container%s/ProjectServiceContainer.php')->set(\Container%s\ProjectServiceContainer::class, null); +(require __DIR__.'/ProjectServiceContainer.php')->set(\Container%s\ProjectServiceContainer::class, null); require __DIR__.'/Container%s/getClosureService.php'; $classes = []; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt index 379d58439b5db..336ddec58bc0a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt @@ -907,7 +907,7 @@ if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) { } require dirname(__DIR__, %d).'%svendor/autoload.php'; -(require __DIR__.'/Container%s/ProjectServiceContainer.php')->set(\Container%s\ProjectServiceContainer::class, null); +(require __DIR__.'/ProjectServiceContainer.php')->set(\Container%s\ProjectServiceContainer::class, null); require __DIR__.'/Container%s/getThrowingOneService.php'; require __DIR__.'/Container%s/getTaggedIteratorService.php'; require __DIR__.'/Container%s/getServiceFromStaticMethodService.php'; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt index c10143b960247..de5b168b94782 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt @@ -562,7 +562,7 @@ if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) { } require dirname(__DIR__, %d).'%svendor/autoload.php'; -(require __DIR__.'/Container%s/ProjectServiceContainer.php')->set(\Container%s\ProjectServiceContainer::class, null); +(require __DIR__.'/ProjectServiceContainer.php')->set(\Container%s\ProjectServiceContainer::class, null); $classes = []; $classes[] = 'Bar\FooClass'; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt index 78e4ff41c13a9..77c69a19dd170 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt @@ -186,7 +186,7 @@ if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) { } require dirname(__DIR__, %d).'%svendor/autoload.php'; -(require __DIR__.'/Container%s/ProjectServiceContainer.php')->set(\Container%s\ProjectServiceContainer::class, null); +(require __DIR__.'/ProjectServiceContainer.php')->set(\Container%s\ProjectServiceContainer::class, null); $classes = []; $classes[] = 'Bar\FooClass'; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt index abde6c81a956f..e19a03c30b54f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt @@ -144,7 +144,7 @@ if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) { } require dirname(__DIR__, %d).'%svendor/autoload.php'; -(require __DIR__.'/Container%s/ProjectServiceContainer.php')->set(\Container%s\ProjectServiceContainer::class, null); +(require __DIR__.'/ProjectServiceContainer.php')->set(\Container%s\ProjectServiceContainer::class, null); require __DIR__.'/Container%s/proxy.php'; require __DIR__.'/Container%s/getNonSharedFooService.php'; From f26af99cc837a370d3fa8926d7a64d81d0d201fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Wed, 17 Nov 2021 11:45:54 +0100 Subject: [PATCH 22/42] Fix CS in composer.json --- src/Symfony/Component/Notifier/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Notifier/composer.json b/src/Symfony/Component/Notifier/composer.json index 53d50421fe170..ceec00e14a2bd 100644 --- a/src/Symfony/Component/Notifier/composer.json +++ b/src/Symfony/Component/Notifier/composer.json @@ -23,7 +23,7 @@ "require-dev": { "symfony/event-dispatcher-contracts": "^2", "symfony/http-client-contracts": "^2", - "symfony/messenger": "^4.4 || ^5.0" + "symfony/messenger": "^4.4|^5.0" }, "conflict": { "symfony/http-kernel": "<4.4", From 6689516e89f0d4f0edf04fa98c829ff2cdc4a1a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Wed, 17 Nov 2021 11:48:20 +0100 Subject: [PATCH 23/42] Improve recommendation message for "composer req" --- src/Symfony/Component/HttpClient/CachingHttpClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpClient/CachingHttpClient.php b/src/Symfony/Component/HttpClient/CachingHttpClient.php index 680a589a86b40..e1d7023d9a05b 100644 --- a/src/Symfony/Component/HttpClient/CachingHttpClient.php +++ b/src/Symfony/Component/HttpClient/CachingHttpClient.php @@ -42,7 +42,7 @@ class CachingHttpClient implements HttpClientInterface, ResetInterface public function __construct(HttpClientInterface $client, StoreInterface $store, array $defaultOptions = []) { if (!class_exists(HttpClientKernel::class)) { - throw new \LogicException(sprintf('Using "%s" requires that the HttpKernel component version 4.3 or higher is installed, try running "composer require symfony/http-kernel:^4.3".', __CLASS__)); + throw new \LogicException(sprintf('Using "%s" requires that the HttpKernel component version 4.3 or higher is installed, try running "composer require symfony/http-kernel:^5.4".', __CLASS__)); } $this->client = $client; From ba6126f591079021b30ba9120e10129450604f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Wed, 17 Nov 2021 11:54:15 +0100 Subject: [PATCH 24/42] Fix API gateway service name --- .../DependencyInjection/FrameworkExtension.php | 4 ++-- .../FrameworkBundle/Resources/config/notifier_transports.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 0ac9a0bb50271..669a5a9ff5dcb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -2421,7 +2421,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ $container->getDefinition('notifier.channel_policy')->setArgument(0, $config['channel_policy']); $classToServices = [ - AllMySmsTransportFactory::class => 'notifier.transport_factory.all-my-sms', + AllMySmsTransportFactory::class => 'notifier.transport_factory.allmysms', ClickatellTransportFactory::class => 'notifier.transport_factory.clickatell', DiscordTransportFactory::class => 'notifier.transport_factory.discord', EsendexTransportFactory::class => 'notifier.transport_factory.esendex', @@ -2429,7 +2429,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ FakeSmsTransportFactory::class => 'notifier.transport_factory.fakesms', FirebaseTransportFactory::class => 'notifier.transport_factory.firebase', FreeMobileTransportFactory::class => 'notifier.transport_factory.freemobile', - GatewayApiTransportFactory::class => 'notifier.transport_factory.gateway-api', + GatewayApiTransportFactory::class => 'notifier.transport_factory.gatewayapi', GitterTransportFactory::class => 'notifier.transport_factory.gitter', GoogleChatTransportFactory::class => 'notifier.transport_factory.googlechat', InfobipTransportFactory::class => 'notifier.transport_factory.infobip', diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index 531b19e762081..eae1e0166acae 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -85,7 +85,7 @@ ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') - ->set('notifier.transport_factory.all-my-sms', AllMySmsTransportFactory::class) + ->set('notifier.transport_factory.allmysms', AllMySmsTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') @@ -157,7 +157,7 @@ ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') - ->set('notifier.transport_factory.gateway-api', GatewayApiTransportFactory::class) + ->set('notifier.transport_factory.gatewayapi', GatewayApiTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') From ccf394ecfb4c6297924e48c979446455fefd4302 Mon Sep 17 00:00:00 2001 From: Andrejs Leonovs <58843458+latvianization@users.noreply.github.com> Date: Wed, 17 Nov 2021 11:21:43 +0200 Subject: [PATCH 25/42] Update validators.lv.xlf Typo --- .../Validator/Resources/translations/validators.lv.xlf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.lv.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.lv.xlf index fa85ecdd64877..fc71d5f9943c5 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.lv.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.lv.xlf @@ -268,7 +268,7 @@ This value should be less than or equal to {{ compared_value }}. - TŠai vērtībai ir jābūt mazākai vai vienādai ar {{ compared_value }}. + Šai vērtībai ir jābūt mazākai vai vienādai ar {{ compared_value }}. This value should not be equal to {{ compared_value }}. From 7d218cc253dab9f2f982a8f6682e427913873aa3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 17 Nov 2021 13:15:18 +0100 Subject: [PATCH 26/42] fix cs --- .../Validator/Resources/translations/validators.uz.xlf | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf index 1a21028cce82a..d1ecaf1b70a29 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.uz.xlf @@ -402,8 +402,6 @@ The value of the netmask should be between {{ min }} and {{ max }}. Tarmoq niqobining qiymati {{ min }} va {{ max }} oralig'ida bo'lishi kerak. - - From 77f66244b6d552c84e235e97b0923040d5fbc039 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Wed, 17 Nov 2021 09:26:06 -0500 Subject: [PATCH 27/42] remove FlattenExceptionNormalizer definition if serializer not available --- .../DependencyInjection/FrameworkExtension.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 669a5a9ff5dcb..ac05cf1ad4c39 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1891,6 +1891,10 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder $loader->load('messenger.php'); + if (!interface_exists(DenormalizerInterface::class)) { + $container->removeDefinition('serializer.normalizer.flatten_exception'); + } + if (ContainerBuilder::willBeAvailable('symfony/amqp-messenger', AmqpTransportFactory::class, ['symfony/framework-bundle', 'symfony/messenger'])) { $container->getDefinition('messenger.transport.amqp.factory')->addTag('messenger.transport_factory'); } From c9be9704f0d3f31dd660faf7aac862723e6fa34c Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Wed, 17 Nov 2021 15:39:49 +0100 Subject: [PATCH 28/42] [FrameworkBundle] Fix default PHP attributes support in validation and serializer configuration when doctrine/annotations is not installed with PHP 8 --- .../FrameworkBundle/DependencyInjection/Configuration.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 68b6db95ee874..a05a5ba06f7a2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -859,7 +859,7 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e ->{$enableIfStandalone('symfony/validator', Validation::class)}() ->children() ->scalarNode('cache')->end() - ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && $willBeAvailable('doctrine/annotations', Annotation::class, 'symfony/validator') ? 'defaultTrue' : 'defaultFalse'}()->end() + ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && (\PHP_VERSION_ID >= 80000 || $willBeAvailable('doctrine/annotations', Annotation::class, 'symfony/validator')) ? 'defaultTrue' : 'defaultFalse'}()->end() ->arrayNode('static_method') ->defaultValue(['loadValidatorMetadata']) ->prototype('scalar')->end() @@ -942,8 +942,8 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e private function addAnnotationsSection(ArrayNodeDefinition $rootNode, callable $willBeAvailable) { - $doctrineCache = $willBeAvailable('doctrine/cache', Cache::class, 'doctrine/annotation'); - $psr6Cache = $willBeAvailable('symfony/cache', PsrCachedReader::class, 'doctrine/annotation'); + $doctrineCache = $willBeAvailable('doctrine/cache', Cache::class, 'doctrine/annotations'); + $psr6Cache = $willBeAvailable('symfony/cache', PsrCachedReader::class, 'doctrine/annotations'); $rootNode ->children() @@ -968,7 +968,7 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $e ->info('serializer configuration') ->{$enableIfStandalone('symfony/serializer', Serializer::class)}() ->children() - ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && $willBeAvailable('doctrine/annotations', Annotation::class, 'symfony/serializer') ? 'defaultTrue' : 'defaultFalse'}()->end() + ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && (\PHP_VERSION_ID >= 80000 || $willBeAvailable('doctrine/annotations', Annotation::class, 'symfony/serializer')) ? 'defaultTrue' : 'defaultFalse'}()->end() ->scalarNode('name_converter')->end() ->scalarNode('circular_reference_handler')->end() ->scalarNode('max_depth_handler')->end() From 2f52fa9597b513fc86577a03fe8b43364178d8cb Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 17 Nov 2021 16:11:44 +0100 Subject: [PATCH 29/42] [Serializer] fix support for unset properties on PHP < 7.4 --- .../Serializer/Normalizer/ObjectNormalizer.php | 18 ++++++------------ .../Normalizer/PropertyNormalizer.php | 18 ++++++------------ .../Tests/Normalizer/ObjectNormalizerTest.php | 10 ++++++++++ .../Normalizer/PropertyNormalizerTest.php | 10 ++++++++++ 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index b96ce3b1c5446..1a3ed992e260b 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -103,23 +103,17 @@ protected function extractAttributes($object, $format = null, array $context = [ } } - $checkPropertyInitialization = \PHP_VERSION_ID >= 70400; - // properties + $propertyValues = (array) $object; foreach ($reflClass->getProperties() as $reflProperty) { - $isPublic = $reflProperty->isPublic(); - - if ($checkPropertyInitialization) { - if (!$isPublic) { - $reflProperty->setAccessible(true); - } - if (!$reflProperty->isInitialized($object)) { + if (!\array_key_exists($reflProperty->name, $propertyValues)) { + if ($reflProperty->isPublic() + || ($reflProperty->isProtected() && !\array_key_exists("\0*\0{$reflProperty->name}", $propertyValues)) + || ($reflProperty->isPrivate() && !\array_key_exists("\0{$reflProperty->class}\0{$reflProperty->name}", $propertyValues)) + ) { unset($attributes[$reflProperty->name]); - continue; } - } - if (!$isPublic) { continue; } diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index 9e2a53216e001..83ec5325b4127 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -101,21 +101,15 @@ protected function extractAttributes($object, $format = null, array $context = [ { $reflectionObject = new \ReflectionObject($object); $attributes = []; - $checkPropertyInitialization = \PHP_VERSION_ID >= 70400; + $propertyValues = (array) $object; do { foreach ($reflectionObject->getProperties() as $property) { - if ($checkPropertyInitialization) { - if (!$property->isPublic()) { - $property->setAccessible(true); - } - - if (!$property->isInitialized($object)) { - continue; - } - } - - if (!$this->isAllowedAttribute($reflectionObject->getName(), $property->name, $format, $context)) { + if (($property->isPublic() && !\array_key_exists($property->name, $propertyValues)) + || ($property->isProtected() && !\array_key_exists("\0*\0{$property->name}", $propertyValues)) + || ($property->isPrivate() && !\array_key_exists("\0{$property->class}\0{$property->name}", $propertyValues)) + || !$this->isAllowedAttribute($reflectionObject->getName(), $property->name, $format, $context) + ) { continue; } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 477027cef4a3e..94c5db93624e9 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -132,6 +132,16 @@ public function testNormalizeObjectWithUninitializedProperties() ); } + public function testNormalizeObjectWithUnsetProperties() + { + $obj = new ObjectInner(); + unset($obj->foo); + $this->assertEquals( + ['bar' => null], + $this->normalizer->normalize($obj, 'any') + ); + } + /** * @requires PHP 7.4 */ diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php index a2aefb4eda1d6..98c1fc601542e 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php @@ -101,6 +101,16 @@ public function testNormalizeObjectWithUninitializedProperties() ); } + public function testNormalizeObjectWithUnsetProperties() + { + $obj = new PropertyDummy(); + unset($obj->foo); + $this->assertEquals( + ['bar' => null, 'camelCase' => null], + $this->normalizer->normalize($obj, 'any') + ); + } + public function testDenormalize() { $obj = $this->normalizer->denormalize( From c684d25ad776a9abf7247df50d8bb2e39a8d0bb3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 17 Nov 2021 13:26:18 +0100 Subject: [PATCH 30/42] [FrameworkBundle] remove references to legacy doctrine cache provider --- .../DependencyInjection/Configuration.php | 1 - .../DependencyInjection/FrameworkExtension.php | 12 +----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index d9fdf8176184a..f4511198d4a3b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -1045,7 +1045,6 @@ private function addCacheSection(ArrayNodeDefinition $rootNode, callable $willBe ->defaultValue('cache.adapter.system') ->end() ->scalarNode('directory')->defaultValue('%kernel.cache_dir%/pools/app')->end() - ->scalarNode('default_doctrine_provider')->end() ->scalarNode('default_psr6_provider')->end() ->scalarNode('default_redis_provider')->defaultValue('redis://localhost')->end() ->scalarNode('default_memcached_provider')->defaultValue('memcached://localhost')->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 198f07a8ca0ae..ff669caacaf5b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -33,8 +33,6 @@ use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\ChainAdapter; -use Symfony\Component\Cache\Adapter\DoctrineAdapter; -use Symfony\Component\Cache\Adapter\DoctrineDbalAdapter; use Symfony\Component\Cache\Adapter\TagAwareAdapter; use Symfony\Component\Cache\DependencyInjection\CachePoolPass; use Symfony\Component\Cache\Marshaller\DefaultMarshaller; @@ -2080,14 +2078,6 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con $container->removeDefinition('cache.default_marshaller'); } - if (!class_exists(DoctrineAdapter::class)) { - $container->removeDefinition('cache.adapter.doctrine'); - } - - if (!class_exists(DoctrineDbalAdapter::class)) { - $container->removeDefinition('cache.adapter.doctrine_dbal'); - } - $version = new Parameter('container.build_id'); $container->getDefinition('cache.adapter.apcu')->replaceArgument(2, $version); $container->getDefinition('cache.adapter.system')->replaceArgument(2, $version); @@ -2100,7 +2090,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con // Inline any env vars referenced in the parameter $container->setParameter('cache.prefix.seed', $container->resolveEnvPlaceholders($container->getParameter('cache.prefix.seed'), true)); } - foreach (['doctrine', 'psr6', 'redis', 'memcached', 'doctrine_dbal', 'pdo'] as $name) { + foreach (['psr6', 'redis', 'memcached', 'doctrine_dbal', 'pdo'] as $name) { if (isset($config[$name = 'default_'.$name.'_provider'])) { $container->setAlias('cache.'.$name, new Alias(CachePoolPass::getServiceProvider($container, $config[$name]), false)); } From 9c3cca15a22702b0b6c33c5dab8035fd805a084f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 17 Nov 2021 15:25:10 +0100 Subject: [PATCH 31/42] Remove more dynamic properties --- .../Command/CacheClearCommand/CacheClearCommandTest.php | 6 +++--- src/Symfony/Component/HttpClient/Internal/AmpBody.php | 1 + src/Symfony/Component/HttpClient/RetryableHttpClient.php | 2 +- src/Symfony/Component/RateLimiter/Policy/TokenBucket.php | 3 +++ 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php index 2c25b8e9f27ca..8a5a023e3b7ac 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php @@ -74,13 +74,13 @@ function () use ($file) { $containerRef = new \ReflectionClass($containerClass); $containerFile = \dirname($containerRef->getFileName(), 2).'/'.$containerClass.'.php'; $containerMetaFile = $containerFile.'.meta'; - $this->kernelRef = new \ReflectionObject($this->kernel); - $this->kernelFile = $this->kernelRef->getFileName(); + $kernelRef = new \ReflectionObject($this->kernel); + $kernelFile = $kernelRef->getFileName(); /** @var ResourceInterface[] $meta */ $meta = unserialize(file_get_contents($containerMetaFile)); $found = false; foreach ($meta as $resource) { - if ((string) $resource === $this->kernelFile) { + if ((string) $resource === $kernelFile) { $found = true; break; } diff --git a/src/Symfony/Component/HttpClient/Internal/AmpBody.php b/src/Symfony/Component/HttpClient/Internal/AmpBody.php index bd4542001c7a6..b99742b13bf80 100644 --- a/src/Symfony/Component/HttpClient/Internal/AmpBody.php +++ b/src/Symfony/Component/HttpClient/Internal/AmpBody.php @@ -26,6 +26,7 @@ class AmpBody implements RequestBody, InputStream { private $body; + private $info; private $onProgress; private $offset = 0; private $length = -1; diff --git a/src/Symfony/Component/HttpClient/RetryableHttpClient.php b/src/Symfony/Component/HttpClient/RetryableHttpClient.php index 97b48da423a85..7fb381f226446 100644 --- a/src/Symfony/Component/HttpClient/RetryableHttpClient.php +++ b/src/Symfony/Component/HttpClient/RetryableHttpClient.php @@ -72,7 +72,7 @@ public function request(string $method, string $url, array $options = []): Respo if ('' !== $context->getInfo('primary_ip')) { $shouldRetry = $this->strategy->shouldRetry($context, null, $exception); if (null === $shouldRetry) { - throw new \LogicException(sprintf('The "%s::shouldRetry()" method must not return null when called with an exception.', \get_class($this->decider))); + throw new \LogicException(sprintf('The "%s::shouldRetry()" method must not return null when called with an exception.', \get_class($this->strategy))); } if (false === $shouldRetry) { diff --git a/src/Symfony/Component/RateLimiter/Policy/TokenBucket.php b/src/Symfony/Component/RateLimiter/Policy/TokenBucket.php index b55f45d18e384..9edb0536a98ba 100644 --- a/src/Symfony/Component/RateLimiter/Policy/TokenBucket.php +++ b/src/Symfony/Component/RateLimiter/Policy/TokenBucket.php @@ -21,6 +21,7 @@ */ final class TokenBucket implements LimiterStateInterface { + private $stringRate; private $id; private $rate; @@ -47,6 +48,8 @@ final class TokenBucket implements LimiterStateInterface */ public function __construct(string $id, int $initialTokens, Rate $rate, float $timer = null) { + unset($this->stringRate); + if ($initialTokens < 1) { throw new \InvalidArgumentException(sprintf('Cannot set the limit of "%s" to 0, as that would never accept any hit.', TokenBucketLimiter::class)); } From 36ee39fa7b3f8756ac3bd8f6649dafc88ab41fee Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 18 Nov 2021 11:50:29 +0100 Subject: [PATCH 32/42] [Serializer] fix support for lazy properties --- .../Normalizer/ObjectNormalizer.php | 4 ++-- .../Normalizer/PropertyNormalizer.php | 10 ++++++---- .../Tests/Normalizer/ObjectNormalizerTest.php | 20 +++++++++++++++++++ .../Normalizer/PropertyNormalizerTest.php | 20 +++++++++++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index 1a3ed992e260b..874b4788d9295 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -104,9 +104,9 @@ protected function extractAttributes($object, $format = null, array $context = [ } // properties - $propertyValues = (array) $object; + $propertyValues = !method_exists($object, '__get') ? (array) $object : null; foreach ($reflClass->getProperties() as $reflProperty) { - if (!\array_key_exists($reflProperty->name, $propertyValues)) { + if (null !== $propertyValues && !\array_key_exists($reflProperty->name, $propertyValues)) { if ($reflProperty->isPublic() || ($reflProperty->isProtected() && !\array_key_exists("\0*\0{$reflProperty->name}", $propertyValues)) || ($reflProperty->isPrivate() && !\array_key_exists("\0{$reflProperty->class}\0{$reflProperty->name}", $propertyValues)) diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index 83ec5325b4127..9989c60e40643 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -101,13 +101,15 @@ protected function extractAttributes($object, $format = null, array $context = [ { $reflectionObject = new \ReflectionObject($object); $attributes = []; - $propertyValues = (array) $object; + $propertyValues = !method_exists($object, '__get') ? (array) $object : null; do { foreach ($reflectionObject->getProperties() as $property) { - if (($property->isPublic() && !\array_key_exists($property->name, $propertyValues)) - || ($property->isProtected() && !\array_key_exists("\0*\0{$property->name}", $propertyValues)) - || ($property->isPrivate() && !\array_key_exists("\0{$property->class}\0{$property->name}", $propertyValues)) + if ((null !== $propertyValues && ( + ($property->isPublic() && !\array_key_exists($property->name, $propertyValues)) + || ($property->isProtected() && !\array_key_exists("\0*\0{$property->name}", $propertyValues)) + || ($property->isPrivate() && !\array_key_exists("\0{$property->class}\0{$property->name}", $propertyValues)) + )) || !$this->isAllowedAttribute($reflectionObject->getName(), $property->name, $format, $context) ) { continue; diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 94c5db93624e9..50ed2ad0f0c43 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -142,6 +142,16 @@ public function testNormalizeObjectWithUnsetProperties() ); } + public function testNormalizeObjectWithLazyProperties() + { + $obj = new LazyObjectInner(); + unset($obj->foo); + $this->assertEquals( + ['foo' => 123, 'bar' => null], + $this->normalizer->normalize($obj, 'any') + ); + } + /** * @requires PHP 7.4 */ @@ -1093,6 +1103,16 @@ class ObjectInner public $bar; } +class LazyObjectInner extends ObjectInner +{ + public function __get($name) + { + if ('foo' === $name) { + return $this->foo = 123; + } + } +} + class FormatAndContextAwareNormalizer extends ObjectNormalizer { protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = []): bool diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php index 98c1fc601542e..be8b17124b592 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php @@ -111,6 +111,16 @@ public function testNormalizeObjectWithUnsetProperties() ); } + public function testNormalizeObjectWithLazyProperties() + { + $obj = new LazyPropertyDummy(); + unset($obj->foo); + $this->assertEquals( + ['foo' => 123, 'bar' => null, 'camelCase' => null], + $this->normalizer->normalize($obj, 'any') + ); + } + public function testDenormalize() { $obj = $this->normalizer->denormalize( @@ -508,6 +518,16 @@ public function setCamelCase($camelCase) } } +class LazyPropertyDummy extends PropertyDummy +{ + public function __get($name) + { + if ('foo' === $name) { + return $this->foo = 123; + } + } +} + class PropertyConstructorDummy { protected $foo; From a86f586abb22923e750f7f9ba646802e86d1ae01 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 18 Nov 2021 11:36:30 +0100 Subject: [PATCH 33/42] [Serializer] fix reading unset properties --- .../PropertyAccess/PropertyAccessor.php | 4 +++ .../Normalizer/AbstractObjectNormalizer.php | 29 +------------------ .../Normalizer/PropertyNormalizer.php | 13 +++++++++ 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 583bd886911fe..21b0718161d6a 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -470,6 +470,10 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid throw $e; } } elseif (PropertyReadInfo::TYPE_PROPERTY === $type) { + if (!method_exists($object, '__get') && !\array_key_exists($name, (array) $object)) { + throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not initialized.', $class, $name)); + } + $result[self::VALUE] = $object->$name; if (isset($zval[self::REF]) && $access->canBeReference()) { diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index c872e36ed7f04..c5c79a205433e 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -123,10 +123,6 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory $this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] = array_merge($this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] ?? [], [self::CIRCULAR_REFERENCE_LIMIT_COUNTERS]); - if (\PHP_VERSION_ID >= 70400) { - $this->defaultContext[self::SKIP_UNINITIALIZED_VALUES] = true; - } - $this->propertyTypeExtractor = $propertyTypeExtractor; if (null === $classDiscriminatorResolver && null !== $classMetadataFactory) { @@ -194,12 +190,7 @@ public function normalize($object, string $format = null, array $context = []) try { $attributeValue = $this->getAttributeValue($object, $attribute, $format, $attributeContext); } catch (UninitializedPropertyException $e) { - if ($this->shouldSkipUninitializedValues($context)) { - continue; - } - throw $e; - } catch (\Error $e) { - if ($this->shouldSkipUninitializedValues($context) && $this->isUninitializedValueError($e)) { + if ($context[self::SKIP_UNINITIALIZED_VALUES] ?? $this->defaultContext[self::SKIP_UNINITIALIZED_VALUES] ?? true) { continue; } throw $e; @@ -733,22 +724,4 @@ private function getCacheKey(?string $format, array $context) return false; } } - - private function shouldSkipUninitializedValues(array $context): bool - { - return $context[self::SKIP_UNINITIALIZED_VALUES] - ?? $this->defaultContext[self::SKIP_UNINITIALIZED_VALUES] - ?? false; - } - - /** - * This error may occur when specific object normalizer implementation gets attribute value - * by accessing a public uninitialized property or by calling a method accessing such property. - */ - private function isUninitializedValueError(\Error $e): bool - { - return \PHP_VERSION_ID >= 70400 - && str_starts_with($e->getMessage(), 'Typed property') - && str_ends_with($e->getMessage(), 'must not be accessed before initialization'); - } } diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index e0116d6c9008c..450092ed59681 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Serializer\Normalizer; +use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException; + /** * Converts between objects and arrays by mapping properties. * @@ -131,6 +133,17 @@ protected function getAttributeValue(object $object, string $attribute, string $ $reflectionProperty->setAccessible(true); } + if (!method_exists($object, '__get')) { + $propertyValues = (array) $object; + + if (($reflectionProperty->isPublic() && !\array_key_exists($reflectionProperty->name, $propertyValues)) + || ($reflectionProperty->isProtected() && !\array_key_exists("\0*\0{$reflectionProperty->name}", $propertyValues)) + || ($reflectionProperty->isPrivate() && !\array_key_exists("\0{$reflectionProperty->class}\0{$reflectionProperty->name}", $propertyValues)) + ) { + throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not initialized.', $reflectionProperty->class, $reflectionProperty->name)); + } + } + return $reflectionProperty->getValue($object); } From 1d7f7742d0ecb6cb1126a8e2e9fed3b95622ecd2 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 18 Nov 2021 12:20:21 +0100 Subject: [PATCH 34/42] Fix merge --- .../FrameworkBundle/Resources/config/notifier_transports.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index 1ac3c937b547c..18620b30832cb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -67,6 +67,8 @@ ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.fake-sms" instead.') ->alias('notifier.transport_factory.freemobile', 'notifier.transport_factory.free-mobile') ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.free-mobile" instead.') + ->alias('notifier.transport_factory.gatewayapi', 'notifier.transport_factory.gateway-api') + ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.gateway-api" instead.') ->alias('notifier.transport_factory.googlechat', 'notifier.transport_factory.google-chat') ->deprecate('symfony/framework-bundle', '5.4', 'The "%alias_id% service is deprecated, use "notifier.transport_factory.google-chat" instead.') ->alias('notifier.transport_factory.lightsms', 'notifier.transport_factory.light-sms') @@ -192,7 +194,7 @@ ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') - ->set('notifier.transport_factory.gatewayapi', GatewayApiTransportFactory::class) + ->set('notifier.transport_factory.gateway-api', GatewayApiTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') From 86608d6386587c33a86f85179047cb61b1ae6b3f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 18 Nov 2021 12:50:08 +0100 Subject: [PATCH 35/42] [PropertyAccess] fix error msg when accessing typed but uninitializer public properties --- src/Symfony/Component/PropertyAccess/PropertyAccessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 21b0718161d6a..3101e9d0011b4 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -470,7 +470,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid throw $e; } } elseif (PropertyReadInfo::TYPE_PROPERTY === $type) { - if (!method_exists($object, '__get') && !\array_key_exists($name, (array) $object)) { + if (!method_exists($object, '__get') && !\array_key_exists($name, (array) $object) && !(new \ReflectionProperty($class, $name))->hasType()) { throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not initialized.', $class, $name)); } From 095475a1dd3f03aa93e36747bcb977171829f1bd Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 18 Nov 2021 12:52:39 +0100 Subject: [PATCH 36/42] [PropertyAccess] Fix on PHP < 7.4 --- src/Symfony/Component/PropertyAccess/PropertyAccessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 3101e9d0011b4..ee38448b842d2 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -470,7 +470,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid throw $e; } } elseif (PropertyReadInfo::TYPE_PROPERTY === $type) { - if (!method_exists($object, '__get') && !\array_key_exists($name, (array) $object) && !(new \ReflectionProperty($class, $name))->hasType()) { + if (!method_exists($object, '__get') && !\array_key_exists($name, (array) $object) && (\PHP_VERSION_ID < 70400 || !(new \ReflectionProperty($class, $name))->hasType())) { throw new UninitializedPropertyException(sprintf('The property "%s::$%s" is not initialized.', $class, $name)); } From 3c4afc4c4326c465d1bc292e7d229e3037d391cb Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 18 Nov 2021 13:06:27 +0100 Subject: [PATCH 37/42] [serializer] fix dealing with uninitialized properties --- .../Normalizer/AbstractObjectNormalizer.php | 16 ++++++++++++++++ .../Serializer/Normalizer/PropertyNormalizer.php | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index c5c79a205433e..245c16ca6abdb 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -194,6 +194,11 @@ public function normalize($object, string $format = null, array $context = []) continue; } throw $e; + } catch (\Error $e) { + if (($context[self::SKIP_UNINITIALIZED_VALUES] ?? $this->defaultContext[self::SKIP_UNINITIALIZED_VALUES] ?? true) && $this->isUninitializedValueError($e)) { + continue; + } + throw $e; } if ($maxDepthReached) { @@ -724,4 +729,15 @@ private function getCacheKey(?string $format, array $context) return false; } } + + /** + * This error may occur when specific object normalizer implementation gets attribute value + * by accessing a public uninitialized property or by calling a method accessing such property. + */ + private function isUninitializedValueError(\Error $e): bool + { + return \PHP_VERSION_ID >= 70400 + && str_starts_with($e->getMessage(), 'Typed property') + && str_ends_with($e->getMessage(), 'must not be accessed before initialization'); + } } diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index 450092ed59681..d84dd6335d481 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -133,6 +133,10 @@ protected function getAttributeValue(object $object, string $attribute, string $ $reflectionProperty->setAccessible(true); } + if (\PHP_VERSION_ID >= 70400 && $reflectionProperty->hasType()) { + return $reflectionProperty->getValue($object); + } + if (!method_exists($object, '__get')) { $propertyValues = (array) $object; From e50f000409c45ed68e1c8ba45affc661eab4b013 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 18 Nov 2021 13:17:05 +0100 Subject: [PATCH 38/42] [Serializer] fix low-deps --- src/Symfony/Component/Serializer/composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index 3cebf5b8537ff..0374c9d3e9149 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -33,7 +33,7 @@ "symfony/http-foundation": "^4.4|^5.0|^6.0", "symfony/http-kernel": "^4.4|^5.0|^6.0", "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/property-access": "^5.1|^6.0", + "symfony/property-access": "^5.4|^6.0", "symfony/property-info": "^5.3|^6.0", "symfony/uid": "^5.1|^6.0", "symfony/validator": "^4.4|^5.0|^6.0", @@ -46,7 +46,7 @@ "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", "symfony/dependency-injection": "<4.4", - "symfony/property-access": "<5.1", + "symfony/property-access": "<5.4", "symfony/property-info": "<5.3", "symfony/yaml": "<4.4" }, From 6852ca763ea005bb6e45bf75a8cc0c0937a933cc Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 18 Nov 2021 15:03:40 +0100 Subject: [PATCH 39/42] Add a setter on DateTimeNormalizer to change the default context at runtime --- .../Component/Serializer/Normalizer/DateTimeNormalizer.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index 82b9b70deb2ac..144db87b01aa7 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -38,6 +38,11 @@ class DateTimeNormalizer implements NormalizerInterface, DenormalizerInterface, ]; public function __construct(array $defaultContext = []) + { + $this->setDefaultContext($defaultContext); + } + + public function setDefaultContext(array $defaultContext): void { $this->defaultContext = array_merge($this->defaultContext, $defaultContext); } From ccfce10b26c03ea0425c867441b4d0885aaa892b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Nov 2021 16:41:28 +0100 Subject: [PATCH 40/42] [WebProfilerBundle] Tweak the colors of the security panel --- .../Resources/views/Collector/security.html.twig | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig index b332ba5ddb596..0771f15a803b7 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig @@ -4,15 +4,7 @@ {% block toolbar %} {% if collector.firewall %} - {% if collector.token %} - {% set is_authenticated = collector.enabled and collector.authenticated %} - {% set color_code = not is_authenticated ? 'yellow' %} - {% elseif collector.enabled %} - {% set color_code = collector.authenticatorManagerEnabled ? 'yellow' : 'red' %} - {% else %} - {% set color_code = '' %} - {% endif %} - + {% set color_code = collector.enabled and not collector.authenticatorManagerEnabled ? 'yellow' %} {% set icon %} {{ include('@Security/Collector/icon.svg') }} {{ collector.user|default('n/a') }} @@ -38,7 +30,7 @@
Authenticated - {{ is_authenticated ? 'Yes' : 'No' }} + {{ collector.authenticated ? 'Yes' : 'No' }}
From aefb2558e9fa6022684ee94003cc3e3a45da3c29 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 18 Nov 2021 16:32:29 +0100 Subject: [PATCH 41/42] Update CHANGELOG for 6.0.0-BETA3 --- CHANGELOG-6.0.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG-6.0.md b/CHANGELOG-6.0.md index 7bb175f02e3bc..0023f5b2f2c73 100644 --- a/CHANGELOG-6.0.md +++ b/CHANGELOG-6.0.md @@ -7,6 +7,27 @@ in 6.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/v6.0.0...v6.0.1 +* 6.0.0-BETA3 (2021-11-18) + + * feature #44125 Add a setter on DateTimeNormalizer to change the default context at runtime (Seldaek) + * bug #44110 [FrameworkBundle] Fix default PHP attributes support in validation and serializer configuration when doctrine/annotations is not installed with PHP 8 (fancyweb) + * bug #44115 [WebProfilerBundle] Tweak the colors of the security panel (javiereguiluz) + * bug #44121 [Serializer] fix support for lazy properties (nicolas-grekas) + * bug #44108 [FrameworkBundle][Messenger] remove `FlattenExceptionNormalizer` definition if serializer not available (kbond) + * bug #44111 [Serializer] fix support for unset properties on PHP < 7.4 (nicolas-grekas) + * bug #44098 [DependencyInjection] fix preloading (nicolas-grekas) + * bug #44065 [FrameworkBundle] Add framework config for DBAL cache adapter (GromNaN) + * bug #44096 Make ExpressionVoter Cacheable (jderusse) + * bug #44070 [Process] intersect with getenv() to populate default envs (nicolas-grekas) + * feature #43181 Allow AbstractDoctrineExtension implementations to support the newer bundle structure (mbabker) + * bug #44060 [Cache] Fix calculate ttl in couchbase sdk 3.0 (ajcerezo) + * bug #43990 [Translation] [Loco] Generate id parameter instead of letting Loco do it (welcoMattic) + * bug #44043 [Cache] fix dbindex Redis (a1812) + * feature #44015 [Cache] Decrease the probability of invalidation loss on tag eviction (nicolas-grekas) + * bug #44064 [Cache] fix releasing not acquired locks (nicolas-grekas) + * bug #44063 [DependencyInjection] fix creating 2nd container instances (nicolas-grekas) + * bug #44056 [DependencyInjection] Fix YamlFileLoader return type (1ed) + * 6.0.0-BETA2 (2021-11-14) * bug #44051 [Notifier] Fix package name (fabpot) From 3f9e1c5cc74899238f01ef7907b47dd08916e53f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 18 Nov 2021 16:32:32 +0100 Subject: [PATCH 42/42] Update VERSION for 6.0.0-BETA3 --- 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 28c499510d984..c5583e49738d0 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -78,12 +78,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.0.0-DEV'; + public const VERSION = '6.0.0-BETA3'; public const VERSION_ID = 60000; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 0; public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = 'BETA3'; public const END_OF_MAINTENANCE = '07/2022'; public const END_OF_LIFE = '07/2022';