diff --git a/Command/SecretsRevealCommand.php b/Command/SecretsRevealCommand.php index 150186b1d..c2110ee76 100644 --- a/Command/SecretsRevealCommand.php +++ b/Command/SecretsRevealCommand.php @@ -61,6 +61,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (!\array_key_exists($name, $secrets)) { $io->error(\sprintf('The secret "%s" does not exist.', $name)); + return self::INVALID; + } elseif (null === $secrets[$name]) { + $io->error(\sprintf('The secret "%s" could not be decrypted.', $name)); + return self::INVALID; } diff --git a/Command/TranslationDebugCommand.php b/Command/TranslationDebugCommand.php index 9cdfdae04..a320130d5 100644 --- a/Command/TranslationDebugCommand.php +++ b/Command/TranslationDebugCommand.php @@ -69,7 +69,7 @@ protected function configure(): void ->setDefinition([ new InputArgument('locale', InputArgument::REQUIRED, 'The locale'), new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages'), - new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'The messages domain'), + new InputOption('domain', null, InputOption::VALUE_REQUIRED, 'The messages domain'), new InputOption('only-missing', null, InputOption::VALUE_NONE, 'Display only missing messages'), new InputOption('only-unused', null, InputOption::VALUE_NONE, 'Display only unused messages'), new InputOption('all', null, InputOption::VALUE_NONE, 'Load messages from all registered bundles'), diff --git a/Command/TranslationExtractCommand.php b/Command/TranslationExtractCommand.php index d7967bbe8..c8e61b61a 100644 --- a/Command/TranslationExtractCommand.php +++ b/Command/TranslationExtractCommand.php @@ -72,15 +72,15 @@ protected function configure(): void ->setDefinition([ new InputArgument('locale', InputArgument::REQUIRED, 'The locale'), new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages'), - new InputOption('prefix', null, InputOption::VALUE_OPTIONAL, 'Override the default prefix', '__'), + new InputOption('prefix', null, InputOption::VALUE_REQUIRED, 'Override the default prefix', '__'), new InputOption('no-fill', null, InputOption::VALUE_NONE, 'Extract translation keys without filling in values'), - new InputOption('format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'xlf12'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'Override the default output format', 'xlf12'), new InputOption('dump-messages', null, InputOption::VALUE_NONE, 'Should the messages be dumped in the console'), new InputOption('force', null, InputOption::VALUE_NONE, 'Should the extract be done'), new InputOption('clean', null, InputOption::VALUE_NONE, 'Should clean not found messages'), - new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'Specify the domain to extract'), - new InputOption('sort', null, InputOption::VALUE_OPTIONAL, 'Return list of messages sorted alphabetically'), - new InputOption('as-tree', null, InputOption::VALUE_OPTIONAL, 'Dump the messages as a tree-like structure: The given value defines the level where to switch to inline YAML'), + new InputOption('domain', null, InputOption::VALUE_REQUIRED, 'Specify the domain to extract'), + new InputOption('sort', null, InputOption::VALUE_REQUIRED, 'Return list of messages sorted alphabetically'), + new InputOption('as-tree', null, InputOption::VALUE_REQUIRED, 'Dump the messages as a tree-like structure: The given value defines the level where to switch to inline YAML'), ]) ->setHelp(<<<'EOF' The %command.name% command extracts translation strings from templates diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index f4e137f04..d042d44b5 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1096,7 +1096,7 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e ->validate()->castToArray()->end() ->end() ->scalarNode('translation_domain')->defaultValue('validators')->end() - ->enumNode('email_validation_mode')->values((class_exists(Email::class) ? Email::VALIDATION_MODES : ['html5-allow-no-tld', 'html5', 'strict']) + ['loose'])->defaultValue('html5')->end() + ->enumNode('email_validation_mode')->values(array_merge(class_exists(Email::class) ? Email::VALIDATION_MODES : ['html5-allow-no-tld', 'html5', 'strict'], ['loose']))->defaultValue('html5')->end() ->arrayNode('mapping') ->addDefaultsIfNotSet() ->fixXmlConfig('path') diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 347f3ed65..d3cefbb28 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -61,6 +61,7 @@ use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; +use Symfony\Component\DependencyInjection\Attribute\Target; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; @@ -302,6 +303,10 @@ public function load(array $configs, ContainerBuilder $container): void // Load Cache configuration first as it is used by other components $loader->load('cache.php'); + if (!interface_exists(NamespacedPoolInterface::class)) { + $container->removeAlias(NamespacedPoolInterface::class); + } + $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); @@ -1769,10 +1774,6 @@ private function registerValidationConfiguration(array $config, ContainerBuilder throw new LogicException('Validation support cannot be enabled as the Validator component is not installed. Try running "composer require symfony/validator".'); } - if (!isset($config['email_validation_mode'])) { - $config['email_validation_mode'] = 'loose'; - } - $loader->load('validator.php'); $validatorBuilder = $container->getDefinition('validator.builder'); @@ -2293,7 +2294,7 @@ private function registerSchedulerConfiguration(ContainerBuilder $container, Php } // BC layer Scheduler < 7.3 - if (!class_exists(SchedulerTriggerNormalizer::class)) { + if (!ContainerBuilder::willBeAvailable('symfony/serializer', DenormalizerInterface::class, ['symfony/framework-bundle', 'symfony/scheduler']) || !class_exists(SchedulerTriggerNormalizer::class)) { $container->removeDefinition('serializer.normalizer.scheduler_trigger'); } } @@ -2371,16 +2372,18 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder $defaultMiddleware['after'][0]['arguments'] = [$bus['default_middleware']['allow_no_senders']]; $defaultMiddleware['after'][1]['arguments'] = [$bus['default_middleware']['allow_no_handlers']]; - // argument to add_bus_name_stamp_middleware - $defaultMiddleware['before'][0]['arguments'] = [$busId]; - $middleware = array_merge($defaultMiddleware['before'], $middleware, $defaultMiddleware['after']); } - foreach ($middleware as $middlewareItem) { + foreach ($middleware as $key => $middlewareItem) { if (!$validationEnabled && \in_array($middlewareItem['id'], ['validation', 'messenger.middleware.validation'], true)) { throw new LogicException('The Validation middleware is only available when the Validator component is installed and enabled. Try running "composer require symfony/validator".'); } + + // argument to add_bus_name_stamp_middleware + if ('add_bus_name_stamp_middleware' === $middlewareItem['id']) { + $middleware[$key]['arguments'] = [$busId]; + } } if ($container->getParameter('kernel.debug') && class_exists(Stopwatch::class)) { @@ -3299,7 +3302,12 @@ private function registerRateLimiterConfiguration(array $config, ContainerBuilde if (interface_exists(RateLimiterFactoryInterface::class)) { $container->registerAliasForArgument($limiterId, RateLimiterFactoryInterface::class, $name.'.limiter'); - $factoryAlias->setDeprecated('symfony/dependency-injection', '7.3', 'The "%alias_id%" autowiring alias is deprecated and will be removed in 8.0, use "RateLimiterFactoryInterface" instead.'); + $factoryAlias->setDeprecated('symfony/framework-bundle', '7.3', \sprintf('The "%%alias_id%%" autowiring alias is deprecated and will be removed in 8.0, use "%s $%s" instead.', RateLimiterFactoryInterface::class, (new Target($name.'.limiter'))->getParsedName())); + $internalAliasId = \sprintf('.%s $%s.limiter', RateLimiterFactory::class, $name); + + if ($container->hasAlias($internalAliasId)) { + $container->getAlias($internalAliasId)->setDeprecated('symfony/framework-bundle', '7.3', \sprintf('The "%%alias_id%%" autowiring alias is deprecated and will be removed in 8.0, use "%s $%s" instead.', RateLimiterFactoryInterface::class, (new Target($name.'.limiter'))->getParsedName())); + } } } diff --git a/Resources/config/cache.php b/Resources/config/cache.php index 3d96ba059..ae9d426a4 100644 --- a/Resources/config/cache.php +++ b/Resources/config/cache.php @@ -28,6 +28,7 @@ use Symfony\Component\Cache\Messenger\EarlyExpirationHandler; use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer; use Symfony\Contracts\Cache\CacheInterface; +use Symfony\Contracts\Cache\NamespacedPoolInterface; use Symfony\Contracts\Cache\TagAwareCacheInterface; return static function (ContainerConfigurator $container) { @@ -250,6 +251,8 @@ ->alias(CacheInterface::class, 'cache.app') + ->alias(NamespacedPoolInterface::class, 'cache.app') + ->alias(TagAwareCacheInterface::class, 'cache.app.taggable') ; }; diff --git a/Secrets/AbstractVault.php b/Secrets/AbstractVault.php index 882ec7862..788601d2e 100644 --- a/Secrets/AbstractVault.php +++ b/Secrets/AbstractVault.php @@ -31,6 +31,9 @@ abstract public function reveal(string $name): ?string; abstract public function remove(string $name): bool; + /** + * @return array + */ abstract public function list(bool $reveal = false): array; protected function validateName(string $name): void diff --git a/Secrets/DotenvVault.php b/Secrets/DotenvVault.php index 15952611a..3fab5f4e2 100644 --- a/Secrets/DotenvVault.php +++ b/Secrets/DotenvVault.php @@ -89,13 +89,13 @@ public function list(bool $reveal = false): array foreach ($_ENV as $k => $v) { if ('' !== ($v ?? '') && preg_match('/^\w+$/D', $k)) { - $secrets[$k] = $reveal ? $v : null; + $secrets[$k] = \is_string($v) && $reveal ? $v : null; } } foreach ($_SERVER as $k => $v) { if ('' !== ($v ?? '') && preg_match('/^\w+$/D', $k)) { - $secrets[$k] = $reveal ? $v : null; + $secrets[$k] = \is_string($v) && $reveal ? $v : null; } } diff --git a/Test/KernelTestCase.php b/Test/KernelTestCase.php index b2c2eb4d2..87925f73c 100644 --- a/Test/KernelTestCase.php +++ b/Test/KernelTestCase.php @@ -39,6 +39,14 @@ protected function tearDown(): void static::$booted = false; } + public static function tearDownAfterClass(): void + { + static::ensureKernelShutdown(); + static::$class = null; + static::$kernel = null; + static::$booted = false; + } + /** * @throws \RuntimeException * @throws \LogicException diff --git a/Tests/Command/SecretsRevealCommandTest.php b/Tests/Command/SecretsRevealCommandTest.php index 94643db2c..d77d303d5 100644 --- a/Tests/Command/SecretsRevealCommandTest.php +++ b/Tests/Command/SecretsRevealCommandTest.php @@ -46,6 +46,19 @@ public function testInvalidName() $this->assertStringContainsString('The secret "undefinedKey" does not exist.', trim($tester->getDisplay(true))); } + public function testFailedDecrypt() + { + $vault = $this->createMock(AbstractVault::class); + $vault->method('list')->willReturn(['secretKey' => null]); + + $command = new SecretsRevealCommand($vault); + + $tester = new CommandTester($command); + $this->assertSame(Command::INVALID, $tester->execute(['name' => 'secretKey'])); + + $this->assertStringContainsString('The secret "secretKey" could not be decrypted.', trim($tester->getDisplay(true))); + } + /** * @backupGlobals enabled */ diff --git a/Tests/DependencyInjection/Fixtures/php/messenger_bus_name_stamp.php b/Tests/DependencyInjection/Fixtures/php/messenger_bus_name_stamp.php new file mode 100644 index 000000000..452594d45 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/messenger_bus_name_stamp.php @@ -0,0 +1,25 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'lock' => false, + 'messenger' => [ + 'default_bus' => 'messenger.bus.commands', + 'buses' => [ + 'messenger.bus.commands' => [ + 'default_middleware' => false, + 'middleware' => [ + 'add_bus_name_stamp_middleware', + 'send_message', + 'handle_message', + ], + ], + 'messenger.bus.events' => [ + 'default_middleware' => true, + ], + ], + ], +]); diff --git a/Tests/DependencyInjection/Fixtures/xml/messenger_bus_name_stamp.xml b/Tests/DependencyInjection/Fixtures/xml/messenger_bus_name_stamp.xml new file mode 100644 index 000000000..5e0b17851 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/messenger_bus_name_stamp.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + diff --git a/Tests/DependencyInjection/Fixtures/yml/messenger_bus_name_stamp.yml b/Tests/DependencyInjection/Fixtures/yml/messenger_bus_name_stamp.yml new file mode 100644 index 000000000..79f8d7c87 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/messenger_bus_name_stamp.yml @@ -0,0 +1,18 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + lock: false + messenger: + default_bus: messenger.bus.commands + buses: + messenger.bus.commands: + default_middleware: false + middleware: + - "add_bus_name_stamp_middleware" + - "send_message" + - "handle_message" + messenger.bus.events: + default_middleware: true diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 5ef658693..b5f5f1ef5 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -1099,6 +1099,28 @@ public function testMessengerWithMultipleBusesWithoutDeduplicateMiddleware() $this->assertSame('messenger.bus.commands', (string) $container->getAlias('messenger.default_bus')); } + public function testMessengerWithAddBusNameStampMiddleware() + { + $container = $this->createContainerFromFile('messenger_bus_name_stamp'); + + $this->assertTrue($container->has('messenger.bus.commands')); + $this->assertEquals([ + ['id' => 'add_bus_name_stamp_middleware', 'arguments' => ['messenger.bus.commands']], + ['id' => 'send_message', 'arguments' => []], + ['id' => 'handle_message', 'arguments' => []], + ], $container->getParameter('messenger.bus.commands.middleware')); + $this->assertTrue($container->has('messenger.bus.events')); + $this->assertSame([], $container->getDefinition('messenger.bus.events')->getArgument(0)); + $this->assertEquals([ + ['id' => 'add_bus_name_stamp_middleware', 'arguments' => ['messenger.bus.events']], + ['id' => 'reject_redelivered_message_middleware'], + ['id' => 'dispatch_after_current_bus'], + ['id' => 'failed_message_processing_middleware'], + ['id' => 'send_message', 'arguments' => [true]], + ['id' => 'handle_message', 'arguments' => [false]], + ], $container->getParameter('messenger.bus.events.middleware')); + } + public function testMessengerWithMultipleBusesWithDeduplicateMiddleware() { if (!class_exists(DeduplicateMiddleware::class)) { diff --git a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index f69a53932..65826f698 100644 --- a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -456,6 +456,7 @@ public static function emailValidationModeProvider() foreach (Email::VALIDATION_MODES as $mode) { yield [$mode]; } + yield ['loose']; } } diff --git a/Tests/Functional/ContainerDebugCommandTest.php b/Tests/Functional/ContainerDebugCommandTest.php index 8d3f15ba6..d21d4d113 100644 --- a/Tests/Functional/ContainerDebugCommandTest.php +++ b/Tests/Functional/ContainerDebugCommandTest.php @@ -53,7 +53,7 @@ public function testNoDebug() public function testNoDumpedXML() { - static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => true, 'debug.container.dump' => false]); + static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'no_dump.yml', 'debug' => true]); $application = new Application(static::$kernel); $application->setAutoExit(false); diff --git a/Tests/Functional/app/ContainerDebug/no_dump.yml b/Tests/Functional/app/ContainerDebug/no_dump.yml new file mode 100644 index 000000000..a9c709e9a --- /dev/null +++ b/Tests/Functional/app/ContainerDebug/no_dump.yml @@ -0,0 +1,5 @@ +imports: + - { resource: config.yml } + +parameters: + debug.container.dump: false