From 28ab2e7a229600018c27513d49ef0aff08867d63 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 May 2023 17:24:39 +0200 Subject: [PATCH 001/281] [7.0] Bump to PHP 8.2 minimum --- Console/Descriptor/JsonDescriptor.php | 2 +- Console/Descriptor/MarkdownDescriptor.php | 2 +- Console/Descriptor/TextDescriptor.php | 2 +- Console/Descriptor/XmlDescriptor.php | 2 +- composer.json | 134 +++++++++++----------- 5 files changed, 71 insertions(+), 71 deletions(-) diff --git a/Console/Descriptor/JsonDescriptor.php b/Console/Descriptor/JsonDescriptor.php index 09e975ad4..81993fd59 100644 --- a/Console/Descriptor/JsonDescriptor.php +++ b/Console/Descriptor/JsonDescriptor.php @@ -370,7 +370,7 @@ private function getCallableData(mixed $callable): array } $data['name'] = $r->name; - if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { + if ($class = $r->getClosureCalledClass()) { $data['class'] = $class->name; if (!$r->getClosureThis()) { $data['static'] = true; diff --git a/Console/Descriptor/MarkdownDescriptor.php b/Console/Descriptor/MarkdownDescriptor.php index 1289c8ded..4304b3c05 100644 --- a/Console/Descriptor/MarkdownDescriptor.php +++ b/Console/Descriptor/MarkdownDescriptor.php @@ -393,7 +393,7 @@ protected function describeCallable(mixed $callable, array $options = []): void } $string .= "\n".sprintf('- Name: `%s`', $r->name); - if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { + if ($class = $r->getClosureCalledClass()) { $string .= "\n".sprintf('- Class: `%s`', $class->name); if (!$r->getClosureThis()) { $string .= "\n- Static: yes"; diff --git a/Console/Descriptor/TextDescriptor.php b/Console/Descriptor/TextDescriptor.php index 519d99f3a..e053b8c5e 100644 --- a/Console/Descriptor/TextDescriptor.php +++ b/Console/Descriptor/TextDescriptor.php @@ -615,7 +615,7 @@ private function formatCallable(mixed $callable): string if (str_contains($r->name, '{closure}')) { return 'Closure()'; } - if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { + if ($class = $r->getClosureCalledClass()) { return sprintf('%s::%s()', $class->name, $r->name); } diff --git a/Console/Descriptor/XmlDescriptor.php b/Console/Descriptor/XmlDescriptor.php index 79253d53f..2ff2fa14d 100644 --- a/Console/Descriptor/XmlDescriptor.php +++ b/Console/Descriptor/XmlDescriptor.php @@ -564,7 +564,7 @@ private function getCallableDocument(mixed $callable): \DOMDocument } $callableXML->setAttribute('name', $r->name); - if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { + if ($class = $r->getClosureCalledClass()) { $callableXML->setAttribute('class', $class->name); if (!$r->getClosureThis()) { $callableXML->setAttribute('static', 'true'); diff --git a/composer.json b/composer.json index 0fde32286..876681757 100644 --- a/composer.json +++ b/composer.json @@ -16,59 +16,59 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "composer-runtime-api": ">=2.1", "ext-xml": "*", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/config": "^6.1|^7.0", - "symfony/dependency-injection": "^6.3|^7.0", + "symfony/cache": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.1|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.3|^7.0", - "symfony/http-kernel": "^6.3|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", "symfony/polyfill-mbstring": "~1.0", - "symfony/filesystem": "^5.4|^6.0|^7.0", - "symfony/finder": "^5.4|^6.0|^7.0", - "symfony/routing": "^5.4|^6.0|^7.0" + "symfony/filesystem": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0" }, "require-dev": { "doctrine/annotations": "^1.13.1|^2", "doctrine/persistence": "^1.3|^2|^3", - "symfony/asset": "^5.4|^6.0|^7.0", - "symfony/asset-mapper": "^6.3|^7.0", - "symfony/browser-kit": "^5.4|^6.0|^7.0", - "symfony/console": "^5.4.9|^6.0.9|^7.0", - "symfony/clock": "^6.2|^7.0", - "symfony/css-selector": "^5.4|^6.0|^7.0", - "symfony/dom-crawler": "^6.3|^7.0", - "symfony/dotenv": "^5.4|^6.0|^7.0", + "symfony/asset": "^6.4|^7.0", + "symfony/asset-mapper": "^6.4|^7.0", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/css-selector": "^6.4|^7.0", + "symfony/dom-crawler": "^6.4|^7.0", + "symfony/dotenv": "^6.4|^7.0", "symfony/polyfill-intl-icu": "~1.0", - "symfony/form": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/html-sanitizer": "^6.1|^7.0", - "symfony/http-client": "^6.3|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/mailer": "^5.4|^6.0|^7.0", - "symfony/messenger": "^6.3|^7.0", - "symfony/mime": "^6.2|^7.0", - "symfony/notifier": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/rate-limiter": "^5.4|^6.0|^7.0", - "symfony/scheduler": "^6.3|^7.0", - "symfony/security-bundle": "^5.4|^6.0|^7.0", - "symfony/semaphore": "^5.4|^6.0|^7.0", - "symfony/serializer": "^6.3|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/string": "^5.4|^6.0|^7.0", - "symfony/translation": "^6.2.8|^7.0", - "symfony/twig-bundle": "^5.4|^6.0|^7.0", - "symfony/validator": "^6.3|^7.0", - "symfony/workflow": "^5.4|^6.0|^7.0", - "symfony/yaml": "^5.4|^6.0|^7.0", - "symfony/property-info": "^5.4|^6.0|^7.0", - "symfony/uid": "^5.4|^6.0|^7.0", - "symfony/web-link": "^5.4|^6.0|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/html-sanitizer": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/mailer": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/notifier": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/scheduler": "^6.4|^7.0", + "symfony/security-bundle": "^6.4|^7.0", + "symfony/semaphore": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/string": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/twig-bundle": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/workflow": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "symfony/web-link": "^6.4|^7.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "twig/twig": "^2.10|^3.0" }, @@ -78,29 +78,29 @@ "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", "phpunit/phpunit": "<5.4.3", - "symfony/asset": "<5.4", - "symfony/clock": "<6.3", - "symfony/console": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/dom-crawler": "<6.3", - "symfony/http-client": "<6.3", - "symfony/form": "<5.4", - "symfony/lock": "<5.4", - "symfony/mailer": "<5.4", - "symfony/messenger": "<6.3", - "symfony/mime": "<6.2", - "symfony/property-info": "<5.4", - "symfony/property-access": "<5.4", - "symfony/serializer": "<6.3", - "symfony/security-csrf": "<5.4", - "symfony/security-core": "<5.4", - "symfony/stopwatch": "<5.4", - "symfony/translation": "<6.2.8", - "symfony/twig-bridge": "<5.4", - "symfony/twig-bundle": "<5.4", - "symfony/validator": "<6.3", - "symfony/web-profiler-bundle": "<5.4", - "symfony/workflow": "<5.4" + "symfony/asset": "<6.4", + "symfony/clock": "<6.4", + "symfony/console": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/dom-crawler": "<6.4", + "symfony/http-client": "<6.4", + "symfony/form": "<6.4", + "symfony/lock": "<6.4", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/property-info": "<6.4", + "symfony/property-access": "<6.4", + "symfony/serializer": "<6.4", + "symfony/security-csrf": "<6.4", + "symfony/security-core": "<6.4", + "symfony/stopwatch": "<6.4", + "symfony/translation": "<6.4", + "symfony/twig-bridge": "<6.4", + "symfony/twig-bundle": "<6.4", + "symfony/validator": "<6.4", + "symfony/web-profiler-bundle": "<6.4", + "symfony/workflow": "<6.4" }, "autoload": { "psr-4": { "Symfony\\Bundle\\FrameworkBundle\\": "" }, From b2166d2abcddd8d34f97b60f60d0cc466674e0c7 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 6 Jun 2023 15:19:43 +0200 Subject: [PATCH 002/281] [DependencyInjection] Remove deprecations across the component --- Console/Application.php | 10 +-- Controller/ControllerResolver.php | 5 -- Tests/Controller/ControllerResolverTest.php | 82 --------------------- 3 files changed, 1 insertion(+), 96 deletions(-) diff --git a/Console/Application.php b/Console/Application.php index 02709ba64..0451b31fe 100644 --- a/Console/Application.php +++ b/Console/Application.php @@ -19,7 +19,6 @@ use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\HttpKernel\KernelInterface; @@ -112,14 +111,7 @@ public function get(string $name): Command { $this->registerCommands(); - $command = parent::get($name); - - if ($command instanceof ContainerAwareInterface) { - trigger_deprecation('symfony/dependency-injection', '6.4', 'Relying on "%s" to get the container in "%s" is deprecated, register the command as a service and use dependency injection instead.', ContainerAwareInterface::class, get_debug_type($command)); - $command->setContainer($this->kernel->getContainer()); - } - - return $command; + return parent::get($name); } public function all(string $namespace = null): array diff --git a/Controller/ControllerResolver.php b/Controller/ControllerResolver.php index 3449740bf..af8b49429 100644 --- a/Controller/ControllerResolver.php +++ b/Controller/ControllerResolver.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\Controller; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\HttpKernel\Controller\ContainerControllerResolver; /** @@ -25,10 +24,6 @@ protected function instantiateController(string $class): object { $controller = parent::instantiateController($class); - if ($controller instanceof ContainerAwareInterface) { - trigger_deprecation('symfony/dependency-injection', '6.4', 'Relying on "%s" to get the container in "%s" is deprecated, register the controller as a service and use dependency injection instead.', ContainerAwareInterface::class, get_debug_type($controller)); - $controller->setContainer($this->container); - } if ($controller instanceof AbstractController) { if (null === $previousContainer = $controller->setContainer($this->container)) { throw new \LogicException(sprintf('"%s" has no container set, did you forget to define it as a service subscriber?', $class)); diff --git a/Tests/Controller/ControllerResolverTest.php b/Tests/Controller/ControllerResolverTest.php index 5488111ee..0c51924a4 100644 --- a/Tests/Controller/ControllerResolverTest.php +++ b/Tests/Controller/ControllerResolverTest.php @@ -13,74 +13,15 @@ use Psr\Container\ContainerInterface as Psr11ContainerInterface; use Psr\Log\LoggerInterface; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver; use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Tests\Controller\ContainerControllerResolverTest; class ControllerResolverTest extends ContainerControllerResolverTest { - use ExpectDeprecationTrait; - - /** - * @group legacy - */ - public function testGetControllerOnContainerAware() - { - $resolver = $this->createControllerResolver(); - $request = Request::create('/'); - $request->attributes->set('_controller', 'Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController::testAction'); - - $this->expectDeprecation('Since symfony/dependency-injection 6.4: Relying on "Symfony\Component\DependencyInjection\ContainerAwareInterface" to get the container in "Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController" is deprecated, register the controller as a service and use dependency injection instead.'); - $controller = $resolver->getController($request); - - $this->assertInstanceOf(ContainerAwareController::class, $controller[0]); - $this->assertInstanceOf(ContainerInterface::class, $controller[0]->getContainer()); - $this->assertSame('testAction', $controller[1]); - } - - /** - * @group legacy - */ - public function testGetControllerOnContainerAwareInvokable() - { - $resolver = $this->createControllerResolver(); - $request = Request::create('/'); - $request->attributes->set('_controller', 'Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController'); - - $this->expectDeprecation('Since symfony/dependency-injection 6.4: Relying on "Symfony\Component\DependencyInjection\ContainerAwareInterface" to get the container in "Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController" is deprecated, register the controller as a service and use dependency injection instead.'); - $controller = $resolver->getController($request); - - $this->assertInstanceOf(ContainerAwareController::class, $controller); - $this->assertInstanceOf(ContainerInterface::class, $controller->getContainer()); - } - - /** - * @group legacy - */ - public function testContainerAwareControllerGetsContainerWhenNotSet() - { - class_exists(AbstractControllerTest::class); - - $controller = new ContainerAwareController(); - - $container = new Container(); - $container->set(TestAbstractController::class, $controller); - - $resolver = $this->createControllerResolver(null, $container); - - $request = Request::create('/'); - $request->attributes->set('_controller', TestAbstractController::class.'::testAction'); - - $this->expectDeprecation('Since symfony/dependency-injection 6.4: Relying on "Symfony\Component\DependencyInjection\ContainerAwareInterface" to get the container in "Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController" is deprecated, register the controller as a service and use dependency injection instead.'); - $this->assertSame([$controller, 'testAction'], $resolver->getController($request)); - $this->assertSame($container, $controller->getContainer()); - } - public function testAbstractControllerGetsContainerWhenNotSet() { $this->expectException(\LogicException::class); @@ -183,29 +124,6 @@ protected function createMockContainer() } } -class ContainerAwareController implements ContainerAwareInterface -{ - private ?ContainerInterface $container = null; - - public function setContainer(?ContainerInterface $container): void - { - $this->container = $container; - } - - public function getContainer(): ?ContainerInterface - { - return $this->container; - } - - public function testAction() - { - } - - public function __invoke() - { - } -} - class DummyController extends AbstractController { public function getContainer(): Psr11ContainerInterface From 4694cf6431933394449e914a0d6543670eff8331 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 2 Jul 2023 13:49:44 +0200 Subject: [PATCH 003/281] [Bridges][Bundles] Convert to native return types --- Command/AbstractConfigCommand.php | 10 ++-------- Console/Application.php | 10 ++-------- .../Compiler/AddDebugLogProcessorPass.php | 10 ++-------- .../AddExpressionLanguageProvidersPass.php | 5 +---- .../Compiler/AssetsContextPass.php | 5 +---- .../Compiler/ContainerBuilderDebugDumpPass.php | 5 +---- .../Compiler/DataCollectorTranslatorPass.php | 5 +---- .../Compiler/LoggingTranslatorPass.php | 5 +---- DependencyInjection/Compiler/ProfilerPass.php | 5 +---- ...moveUnusedSessionMarshallingHandlerPass.php | 5 +---- .../TestServiceContainerRealRefPass.php | 5 +---- .../TestServiceContainerWeakRefPass.php | 5 +---- .../Compiler/UnusedTagsPass.php | 5 +---- .../Compiler/WorkflowGuardListenerPass.php | 5 +---- DependencyInjection/FrameworkExtension.php | 4 +--- FrameworkBundle.php | 10 ++-------- Kernel/MicroKernelTrait.php | 5 +---- KernelBrowser.php | 18 +++--------------- Routing/AnnotatedRouteControllerLoader.php | 4 +--- Secrets/AbstractVault.php | 5 +---- Test/KernelTestCase.php | 4 +--- Translation/Translator.php | 15 +++------------ 22 files changed, 30 insertions(+), 120 deletions(-) diff --git a/Command/AbstractConfigCommand.php b/Command/AbstractConfigCommand.php index 94b95e502..479bbfe6a 100644 --- a/Command/AbstractConfigCommand.php +++ b/Command/AbstractConfigCommand.php @@ -28,10 +28,7 @@ */ abstract class AbstractConfigCommand extends ContainerDebugCommand { - /** - * @return void - */ - protected function listBundles(OutputInterface|StyleInterface $output) + protected function listBundles(OutputInterface|StyleInterface $output): void { $title = 'Available registered bundles with their extension alias if available'; $headers = ['Bundle name', 'Extension alias']; @@ -159,10 +156,7 @@ protected function findExtension(string $name): ExtensionInterface throw new LogicException($message); } - /** - * @return void - */ - public function validateConfiguration(ExtensionInterface $extension, mixed $configuration) + public function validateConfiguration(ExtensionInterface $extension, mixed $configuration): void { if (!$configuration) { throw new \LogicException(sprintf('The extension with alias "%s" does not have its getConfiguration() method setup.', $extension->getAlias())); diff --git a/Console/Application.php b/Console/Application.php index 0451b31fe..80a2f4935 100644 --- a/Console/Application.php +++ b/Console/Application.php @@ -51,10 +51,7 @@ public function getKernel(): KernelInterface return $this->kernel; } - /** - * @return void - */ - public function reset() + public function reset(): void { if ($this->kernel->getContainer()->has('services_resetter')) { $this->kernel->getContainer()->get('services_resetter')->reset(); @@ -133,10 +130,7 @@ public function add(Command $command): ?Command return parent::add($command); } - /** - * @return void - */ - protected function registerCommands() + protected function registerCommands(): void { if ($this->commandsRegistered) { return; diff --git a/DependencyInjection/Compiler/AddDebugLogProcessorPass.php b/DependencyInjection/Compiler/AddDebugLogProcessorPass.php index d0aca7a06..5c8a470c1 100644 --- a/DependencyInjection/Compiler/AddDebugLogProcessorPass.php +++ b/DependencyInjection/Compiler/AddDebugLogProcessorPass.php @@ -17,10 +17,7 @@ class AddDebugLogProcessorPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasDefinition('profiler')) { return; @@ -37,10 +34,7 @@ public function process(ContainerBuilder $container) $definition->addMethodCall('pushProcessor', [new Reference('debug.log_processor')]); } - /** - * @return void - */ - public static function configureLogger(mixed $logger) + public static function configureLogger(mixed $logger): void { if (\is_object($logger) && method_exists($logger, 'removeDebugLogger') && \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { $logger->removeDebugLogger(); diff --git a/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php b/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php index 5442b2773..c816ef403 100644 --- a/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php +++ b/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php @@ -22,10 +22,7 @@ */ class AddExpressionLanguageProvidersPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { // routing if ($container->has('router.default')) { diff --git a/DependencyInjection/Compiler/AssetsContextPass.php b/DependencyInjection/Compiler/AssetsContextPass.php index e8c2ad3a0..c4b99c568 100644 --- a/DependencyInjection/Compiler/AssetsContextPass.php +++ b/DependencyInjection/Compiler/AssetsContextPass.php @@ -18,10 +18,7 @@ class AssetsContextPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasDefinition('assets.context')) { return; diff --git a/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php b/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php index 1e08ef314..e4023e623 100644 --- a/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php +++ b/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php @@ -25,10 +25,7 @@ */ class ContainerBuilderDebugDumpPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->getParameter('debug.container.dump')) { return; diff --git a/DependencyInjection/Compiler/DataCollectorTranslatorPass.php b/DependencyInjection/Compiler/DataCollectorTranslatorPass.php index e66e98b45..c6730ceb0 100644 --- a/DependencyInjection/Compiler/DataCollectorTranslatorPass.php +++ b/DependencyInjection/Compiler/DataCollectorTranslatorPass.php @@ -20,10 +20,7 @@ */ class DataCollectorTranslatorPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->has('translator')) { return; diff --git a/DependencyInjection/Compiler/LoggingTranslatorPass.php b/DependencyInjection/Compiler/LoggingTranslatorPass.php index b7cb920bf..3f4a3926c 100644 --- a/DependencyInjection/Compiler/LoggingTranslatorPass.php +++ b/DependencyInjection/Compiler/LoggingTranslatorPass.php @@ -22,10 +22,7 @@ */ class LoggingTranslatorPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasAlias('logger') || !$container->hasAlias('translator')) { return; diff --git a/DependencyInjection/Compiler/ProfilerPass.php b/DependencyInjection/Compiler/ProfilerPass.php index c2d669fe1..68ee76d4b 100644 --- a/DependencyInjection/Compiler/ProfilerPass.php +++ b/DependencyInjection/Compiler/ProfilerPass.php @@ -24,10 +24,7 @@ */ class ProfilerPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (false === $container->hasDefinition('profiler')) { return; diff --git a/DependencyInjection/Compiler/RemoveUnusedSessionMarshallingHandlerPass.php b/DependencyInjection/Compiler/RemoveUnusedSessionMarshallingHandlerPass.php index fedc30d06..7f0ec5f89 100644 --- a/DependencyInjection/Compiler/RemoveUnusedSessionMarshallingHandlerPass.php +++ b/DependencyInjection/Compiler/RemoveUnusedSessionMarshallingHandlerPass.php @@ -19,10 +19,7 @@ */ class RemoveUnusedSessionMarshallingHandlerPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasDefinition('session.marshalling_handler')) { return; diff --git a/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php b/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php index aed3b1340..68a6ee103 100644 --- a/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php +++ b/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php @@ -21,10 +21,7 @@ */ class TestServiceContainerRealRefPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasDefinition('test.private_services_locator')) { return; diff --git a/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php b/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php index 6e7669a71..3b3dfcc06 100644 --- a/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php +++ b/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php @@ -21,10 +21,7 @@ */ class TestServiceContainerWeakRefPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasDefinition('test.private_services_locator')) { return; diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index b04516410..ae5daac8e 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -105,10 +105,7 @@ class UnusedTagsPass implements CompilerPassInterface 'workflow', ]; - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { $tags = array_unique(array_merge($container->findTags(), self::KNOWN_TAGS)); diff --git a/DependencyInjection/Compiler/WorkflowGuardListenerPass.php b/DependencyInjection/Compiler/WorkflowGuardListenerPass.php index bda9ca951..394628c5d 100644 --- a/DependencyInjection/Compiler/WorkflowGuardListenerPass.php +++ b/DependencyInjection/Compiler/WorkflowGuardListenerPass.php @@ -21,10 +21,7 @@ */ class WorkflowGuardListenerPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasParameter('workflow.has_guard_listeners')) { return; diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index cb855a9c2..4b79ab736 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -207,11 +207,9 @@ class FrameworkExtension extends Extension /** * Responds to the app.config configuration parameter. * - * @return void - * * @throws LogicException */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config')); diff --git a/FrameworkBundle.php b/FrameworkBundle.php index 339b28e83..fa289c683 100644 --- a/FrameworkBundle.php +++ b/FrameworkBundle.php @@ -90,10 +90,7 @@ class_exists(Registry::class); */ class FrameworkBundle extends Bundle { - /** - * @return void - */ - public function boot() + public function boot(): void { $_ENV['DOCTRINE_DEPRECATIONS'] = $_SERVER['DOCTRINE_DEPRECATIONS'] ??= 'trigger'; @@ -115,10 +112,7 @@ public function boot() } } - /** - * @return void - */ - public function build(ContainerBuilder $container) + public function build(ContainerBuilder $container): void { parent::build($container); diff --git a/Kernel/MicroKernelTrait.php b/Kernel/MicroKernelTrait.php index f82e1fb20..878c36af7 100644 --- a/Kernel/MicroKernelTrait.php +++ b/Kernel/MicroKernelTrait.php @@ -129,10 +129,7 @@ public function registerBundles(): iterable } } - /** - * @return void - */ - public function registerContainerConfiguration(LoaderInterface $loader) + public function registerContainerConfiguration(LoaderInterface $loader): void { $loader->load(function (ContainerBuilder $container) use ($loader) { $container->loadFromExtension('framework', [ diff --git a/KernelBrowser.php b/KernelBrowser.php index 0379be8f5..694714faa 100644 --- a/KernelBrowser.php +++ b/KernelBrowser.php @@ -39,9 +39,6 @@ public function __construct(KernelInterface $kernel, array $server = [], History parent::__construct($kernel, $server, $history, $cookieJar); } - /** - * Returns the container. - */ public function getContainer(): ContainerInterface { $container = $this->kernel->getContainer(); @@ -49,9 +46,6 @@ public function getContainer(): ContainerInterface return $container->has('test.service_container') ? $container->get('test.service_container') : $container; } - /** - * Returns the kernel. - */ public function getKernel(): KernelInterface { return $this->kernel; @@ -73,10 +67,8 @@ public function getProfile(): HttpProfile|false|null * Enables the profiler for the very next request. * * If the profiler is not enabled, the call to this method does nothing. - * - * @return void */ - public function enableProfiler() + public function enableProfiler(): void { if ($this->getContainer()->has('profiler')) { $this->profiler = true; @@ -88,20 +80,16 @@ public function enableProfiler() * * By default, the Client reboots the Kernel for each request. This method * allows to keep the same kernel across requests. - * - * @return void */ - public function disableReboot() + public function disableReboot(): void { $this->reboot = false; } /** * Enables kernel reboot between requests. - * - * @return void */ - public function enableReboot() + public function enableReboot(): void { $this->reboot = true; } diff --git a/Routing/AnnotatedRouteControllerLoader.php b/Routing/AnnotatedRouteControllerLoader.php index ec03f8497..ac760405c 100644 --- a/Routing/AnnotatedRouteControllerLoader.php +++ b/Routing/AnnotatedRouteControllerLoader.php @@ -24,10 +24,8 @@ class AnnotatedRouteControllerLoader extends AnnotationClassLoader { /** * Configures the _controller default parameter of a given Route instance. - * - * @return void */ - protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot) + protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot): void { if ('__invoke' === $method->getName()) { $route->setDefault('_controller', $class->getName()); diff --git a/Secrets/AbstractVault.php b/Secrets/AbstractVault.php index b3eb0c6bc..ac2078f6d 100644 --- a/Secrets/AbstractVault.php +++ b/Secrets/AbstractVault.php @@ -40,10 +40,7 @@ protected function validateName(string $name): void } } - /** - * @return string - */ - protected function getPrettyPath(string $path) + protected function getPrettyPath(string $path): string { return str_replace(getcwd().\DIRECTORY_SEPARATOR, '', $path); } diff --git a/Test/KernelTestCase.php b/Test/KernelTestCase.php index bb5560a7b..639214a43 100644 --- a/Test/KernelTestCase.php +++ b/Test/KernelTestCase.php @@ -84,10 +84,8 @@ protected static function bootKernel(array $options = []): KernelInterface * used by other services. * * Using this method is the best way to get a container from your test code. - * - * @return Container */ - protected static function getContainer(): ContainerInterface + protected static function getContainer(): Container { if (!static::$booted) { static::bootKernel(); diff --git a/Translation/Translator.php b/Translation/Translator.php index dac3b6394..3cce60ec1 100644 --- a/Translation/Translator.php +++ b/Translation/Translator.php @@ -118,10 +118,7 @@ public function warmUp(string $cacheDir): array return []; } - /** - * @return void - */ - public function addResource(string $format, mixed $resource, string $locale, string $domain = null) + public function addResource(string $format, mixed $resource, string $locale, string $domain = null): void { if ($this->resourceFiles) { $this->addResourceFiles(); @@ -129,10 +126,7 @@ public function addResource(string $format, mixed $resource, string $locale, str $this->resources[] = [$format, $resource, $locale, $domain]; } - /** - * @return void - */ - protected function initializeCatalogue(string $locale) + protected function initializeCatalogue(string $locale): void { $this->initialize(); parent::initializeCatalogue($locale); @@ -151,10 +145,7 @@ protected function doLoadCatalogue(string $locale): void } } - /** - * @return void - */ - protected function initialize() + protected function initialize(): void { if ($this->resourceFiles) { $this->addResourceFiles(); From 894b1be3e34b27004d1134623db1752b7b127351 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 3 Jul 2023 15:44:01 +0200 Subject: [PATCH 004/281] - --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e59213f5..effe9a430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.0 +--- + + * Remove command `translation:update`, use `translation:extract` instead + 6.3 --- From 008116925e2f07b6b929d6f9ad5f1b95df86eeec Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 30 Jun 2023 18:53:02 +0200 Subject: [PATCH 005/281] Remove BC layers related to new methods and new parameters --- CHANGELOG.md | 5 +++++ Command/TranslationUpdateCommand.php | 4 ---- Tests/Command/TranslationUpdateCommandTest.php | 8 -------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e59213f5..effe9a430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.0 +--- + + * Remove command `translation:update`, use `translation:extract` instead + 6.3 --- diff --git a/Command/TranslationUpdateCommand.php b/Command/TranslationUpdateCommand.php index 15c536ea9..f49849d4d 100644 --- a/Command/TranslationUpdateCommand.php +++ b/Command/TranslationUpdateCommand.php @@ -127,10 +127,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io = new SymfonyStyle($input, $output); $errorIo = $output instanceof ConsoleOutputInterface ? new SymfonyStyle($input, $output->getErrorOutput()) : $io; - if ('translation:update' === $input->getFirstArgument()) { - $errorIo->caution('Command "translation:update" is deprecated since version 5.4 and will be removed in Symfony 6.0. Use "translation:extract" instead.'); - } - $io = new SymfonyStyle($input, $output); $errorIo = $io->getErrorStyle(); diff --git a/Tests/Command/TranslationUpdateCommandTest.php b/Tests/Command/TranslationUpdateCommandTest.php index f883fac0c..89e983d4e 100644 --- a/Tests/Command/TranslationUpdateCommandTest.php +++ b/Tests/Command/TranslationUpdateCommandTest.php @@ -29,14 +29,6 @@ class TranslationUpdateCommandTest extends TestCase private $fs; private $translationDir; - public function testDumpMessagesAndCleanWithDeprecatedCommandName() - { - $tester = $this->createCommandTester(['messages' => ['foo' => 'foo']]); - $tester->execute(['command' => 'translation:update', 'locale' => 'en', 'bundle' => 'foo', '--dump-messages' => true, '--clean' => true]); - $this->assertMatchesRegularExpression('/foo/', $tester->getDisplay()); - $this->assertMatchesRegularExpression('/1 message was successfully extracted/', $tester->getDisplay()); - } - public function testDumpMessagesAndClean() { $tester = $this->createCommandTester(['messages' => ['foo' => 'foo']]); From 58c5dca4e674576ac473d5ac17a771732ce05725 Mon Sep 17 00:00:00 2001 From: Frank Fiebig Date: Tue, 4 Jul 2023 11:21:50 +0200 Subject: [PATCH 006/281] [Lock] 7.0 remove deprecations in Lock Component --- DependencyInjection/FrameworkExtension.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 4b79ab736..f5ddf2fac 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -95,7 +95,6 @@ use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\BackedEnumValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\UidValueResolver; -use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; @@ -596,8 +595,6 @@ public function load(array $configs, ContainerBuilder $container): void ->addTag('container.service_locator'); $container->registerForAutoconfiguration(ServiceSubscriberInterface::class) ->addTag('container.service_subscriber'); - $container->registerForAutoconfiguration(ArgumentValueResolverInterface::class) - ->addTag('controller.argument_value_resolver'); $container->registerForAutoconfiguration(ValueResolverInterface::class) ->addTag('controller.argument_value_resolver'); $container->registerForAutoconfiguration(AbstractController::class) From 7f96ac7269dda50bc93f589aa973a82cf73a9a80 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 4 Jul 2023 14:50:59 +0200 Subject: [PATCH 007/281] [7.0] Remove remaining deprecated code paths --- CHANGELOG.md | 6 +++ Command/WorkflowDumpCommand.php | 48 ++++------------- Controller/AbstractController.php | 16 +----- DependencyInjection/Configuration.php | 46 +--------------- DependencyInjection/FrameworkExtension.php | 45 +--------------- KernelBrowser.php | 4 -- Resources/config/annotations.php | 2 +- Resources/config/http_client.php | 2 - Resources/config/mailer_transports.php | 5 -- Resources/config/messenger.php | 3 -- Resources/config/notifier.php | 3 -- Resources/config/schema/symfony-1.0.xsd | 30 ++--------- Resources/config/serializer.php | 7 --- Resources/config/translation.php | 5 -- Resources/config/workflow.php | 4 -- Tests/Controller/AbstractControllerTest.php | 52 ------------------- .../DependencyInjection/ConfigurationTest.php | 1 - ...ssenger_with_disabled_reset_on_message.php | 20 ------- ...r_with_explict_reset_on_message_legacy.php | 20 ------- .../Fixtures/xml/exceptions_legacy.xml | 16 ------ ...ssenger_with_disabled_reset_on_message.xml | 22 -------- ...r_with_explict_reset_on_message_legacy.xml | 22 -------- .../FrameworkExtensionTestCase.php | 36 +------------ .../XmlFrameworkExtensionTest.php | 34 ------------ composer.json | 1 - 25 files changed, 23 insertions(+), 427 deletions(-) delete mode 100644 Tests/DependencyInjection/Fixtures/php/messenger_with_disabled_reset_on_message.php delete mode 100644 Tests/DependencyInjection/Fixtures/php/messenger_with_explict_reset_on_message_legacy.php delete mode 100644 Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml delete mode 100644 Tests/DependencyInjection/Fixtures/xml/messenger_with_disabled_reset_on_message.xml delete mode 100644 Tests/DependencyInjection/Fixtures/xml/messenger_with_explict_reset_on_message_legacy.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index effe9a430..79c157980 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ CHANGELOG --- * Remove command `translation:update`, use `translation:extract` instead + * Make the `http_method_override` config option default to `false` + * Remove `AbstractController::renderForm()`, use `render()` instead + * Remove the `Symfony\Component\Serializer\Normalizer\ObjectNormalizer` and + `Symfony\Component\Serializer\Normalizer\PropertyNormalizer` autowiring aliases, type-hint against + `Symfony\Component\Serializer\Normalizer\NormalizerInterface` or implement `NormalizerAwareInterface` instead + * Remove the `Http\Client\HttpClient` service, use `Psr\Http\Client\ClientInterface` instead 6.3 --- diff --git a/Command/WorkflowDumpCommand.php b/Command/WorkflowDumpCommand.php index f84a560c6..f69a0e11e 100644 --- a/Command/WorkflowDumpCommand.php +++ b/Command/WorkflowDumpCommand.php @@ -21,7 +21,6 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\DependencyInjection\ServiceLocator; -use Symfony\Component\Workflow\Definition; use Symfony\Component\Workflow\Dumper\GraphvizDumper; use Symfony\Component\Workflow\Dumper\MermaidDumper; use Symfony\Component\Workflow\Dumper\PlantUmlDumper; @@ -37,33 +36,16 @@ #[AsCommand(name: 'workflow:dump', description: 'Dump a workflow')] class WorkflowDumpCommand extends Command { - /** - * string is the service id. - * - * @var array - */ - private array $definitions = []; - - private ServiceLocator $workflows; - private const DUMP_FORMAT_OPTIONS = [ 'puml', 'mermaid', 'dot', ]; - public function __construct($workflows) - { + public function __construct( + private ServiceLocator $workflows, + ) { parent::__construct(); - - if ($workflows instanceof ServiceLocator) { - $this->workflows = $workflows; - } elseif (\is_array($workflows)) { - $this->definitions = $workflows; - trigger_deprecation('symfony/framework-bundle', '6.2', 'Passing an array of definitions in "%s()" is deprecated. Inject a ServiceLocator filled with all workflows instead.', __METHOD__); - } else { - throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be an array or a ServiceLocator, "%s" given.', __METHOD__, \gettype($workflows))); - } } protected function configure(): void @@ -92,20 +74,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $workflowName = $input->getArgument('name'); - if (isset($this->workflows)) { - if (!$this->workflows->has($workflowName)) { - throw new InvalidArgumentException(sprintf('The workflow named "%s" cannot be found.', $workflowName)); - } - $workflow = $this->workflows->get($workflowName); - $type = $workflow instanceof StateMachine ? 'state_machine' : 'workflow'; - $definition = $workflow->getDefinition(); - } elseif (isset($this->definitions['workflow.'.$workflowName])) { - $definition = $this->definitions['workflow.'.$workflowName]; - $type = 'workflow'; - } elseif (isset($this->definitions['state_machine.'.$workflowName])) { - $definition = $this->definitions['state_machine.'.$workflowName]; - $type = 'state_machine'; + if (!$this->workflows->has($workflowName)) { + throw new InvalidArgumentException(sprintf('The workflow named "%s" cannot be found.', $workflowName)); } + $workflow = $this->workflows->get($workflowName); + $type = $workflow instanceof StateMachine ? 'state_machine' : 'workflow'; + $definition = $workflow->getDefinition(); if (null === $definition) { throw new InvalidArgumentException(sprintf('No service found for "workflow.%1$s" nor "state_machine.%1$s".', $workflowName)); @@ -147,11 +121,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('name')) { - if (isset($this->workflows)) { - $suggestions->suggestValues(array_keys($this->workflows->getProvidedServices())); - } else { - $suggestions->suggestValues(array_keys($this->definitions)); - } + $suggestions->suggestValues(array_keys($this->workflows->getProvidedServices())); } if ($input->mustSuggestOptionValuesFor('dump-format')) { diff --git a/Controller/AbstractController.php b/Controller/AbstractController.php index 13e155235..4af31ad7d 100644 --- a/Controller/AbstractController.php +++ b/Controller/AbstractController.php @@ -186,7 +186,7 @@ protected function addFlash(string $type, mixed $message): void } if (!$session instanceof FlashBagAwareSessionInterface) { - trigger_deprecation('symfony/framework-bundle', '6.2', 'Calling "addFlash()" method when the session does not implement %s is deprecated.', FlashBagAwareSessionInterface::class); + throw new \LogicException(sprintf('You cannot use the addFlash method because class "%s" doesn\'t implement "%s".', get_debug_type($session), FlashBagAwareSessionInterface::class)); } $session->getFlashBag()->add($type, $message); @@ -268,20 +268,6 @@ protected function render(string $view, array $parameters = [], Response $respon return $response; } - /** - * Renders a view and sets the appropriate status code when a form is listed in parameters. - * - * If an invalid form is found in the list of parameters, a 422 status code is returned. - * - * @deprecated since Symfony 6.2, use render() instead - */ - protected function renderForm(string $view, array $parameters = [], Response $response = null): Response - { - trigger_deprecation('symfony/framework-bundle', '6.2', 'The "%s::renderForm()" method is deprecated, use "render()" instead.', get_debug_type($this)); - - return $this->render($view, $parameters, $response); - } - /** * Streams a view. */ diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 708b43b84..8ca6a54c0 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -81,23 +81,12 @@ public function getConfigTreeBuilder(): TreeBuilder return $v; }) ->end() - ->validate() - ->always(function ($v) { - if (!isset($v['http_method_override'])) { - trigger_deprecation('symfony/framework-bundle', '6.1', 'Not setting the "framework.http_method_override" config option is deprecated. It will default to "false" in 7.0.'); - - $v['http_method_override'] = true; - } - - return $v; - }) - ->end() ->fixXmlConfig('enabled_locale') ->children() ->scalarNode('secret')->end() ->booleanNode('http_method_override') ->info("Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. Note: When using the HttpCache, you need to call the method in your front controller instead") - ->treatNullLike(false) + ->defaultFalse() ->end() ->scalarNode('trust_x_sendfile_type_header') ->info('Set true to enable support for xsendfile in binary file responses.') @@ -244,9 +233,6 @@ private function addFormSection(ArrayNodeDefinition $rootNode, callable $enableI ->scalarNode('field_name')->defaultValue('_token')->end() ->end() ->end() - ->booleanNode('legacy_error_messages') - ->setDeprecated('symfony/framework-bundle', '6.2') - ->end() ->end() ->end() ->end() @@ -1303,27 +1289,6 @@ private function addExceptionsSection(ArrayNodeDefinition $rootNode): void ->arrayNode('exceptions') ->info('Exception handling configuration') ->useAttributeAsKey('class') - ->beforeNormalization() - // Handle legacy XML configuration - ->ifArray() - ->then(function (array $v): array { - if (!\array_key_exists('exception', $v)) { - return $v; - } - - trigger_deprecation('symfony/framework-bundle', '6.3', '"framework:exceptions" tag is deprecated. Unwrap it and replace your "framework:exception" tags\' "name" attribute by "class".'); - - $v = $v['exception']; - unset($v['exception']); - - foreach ($v as &$exception) { - $exception['class'] = $exception['name']; - unset($exception['name']); - } - - return $v; - }) - ->end() ->prototype('array') ->children() ->scalarNode('log_level') @@ -1620,15 +1585,6 @@ function ($a) { ->defaultNull() ->info('Transport name to send failed messages to (after all retries have failed).') ->end() - ->booleanNode('reset_on_message') - ->defaultTrue() - ->info('Reset container services after each message.') - ->setDeprecated('symfony/framework-bundle', '6.1', 'Option "%node%" at "%path%" is deprecated. It does nothing and will be removed in version 7.0.') - ->validate() - ->ifTrue(static fn ($v) => true !== $v) - ->thenInvalid('The "framework.messenger.reset_on_message" configuration option can be set to "true" only. To prevent services resetting after each message you can set the "--no-reset" option in "messenger:consume" command.') - ->end() - ->end() ->arrayNode('stop_worker_on_signals') ->defaultValue([]) ->info('A list of signals that should stop the worker; defaults to SIGTERM and SIGINT.') diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index f5ddf2fac..34e8c93b9 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -111,7 +111,6 @@ use Symfony\Component\Messenger\Bridge as MessengerBridge; use Symfony\Component\Messenger\Command\StatsCommand; use Symfony\Component\Messenger\Handler\BatchHandlerInterface; -use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Middleware\RouterContextMiddleware; @@ -213,7 +212,7 @@ public function load(array $configs, ContainerBuilder $container): void $loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config')); if (class_exists(InstalledVersions::class) && InstalledVersions::isInstalled('symfony/symfony') && 'symfony/symfony' !== (InstalledVersions::getRootPackage()['name'] ?? '')) { - trigger_deprecation('symfony/symfony', '6.1', 'Requiring the "symfony/symfony" package is deprecated; replace it with standalone components instead.'); + throw new \LogicException('Requiring the "symfony/symfony" package is unsupported; replace it with standalone components instead.'); } $loader->load('web.php'); @@ -647,8 +646,6 @@ public function load(array $configs, ContainerBuilder $container): void ->addTag('validator.constraint_validator'); $container->registerForAutoconfiguration(ObjectInitializerInterface::class) ->addTag('validator.initializer'); - $container->registerForAutoconfiguration(MessageHandlerInterface::class) - ->addTag('messenger.message_handler'); $container->registerForAutoconfiguration(BatchHandlerInterface::class) ->addTag('messenger.message_handler'); $container->registerForAutoconfiguration(MessengerTransportFactoryInterface::class) @@ -2591,7 +2588,6 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co MailerBridge\Mailjet\Transport\MailjetTransportFactory::class => 'mailer.transport_factory.mailjet', MailerBridge\MailPace\Transport\MailPaceTransportFactory::class => 'mailer.transport_factory.mailpace', MailerBridge\Mailchimp\Transport\MandrillTransportFactory::class => 'mailer.transport_factory.mailchimp', - MailerBridge\OhMySmtp\Transport\OhMySmtpTransportFactory::class => 'mailer.transport_factory.ohmysmtp', MailerBridge\Postmark\Transport\PostmarkTransportFactory::class => 'mailer.transport_factory.postmark', MailerBridge\Sendgrid\Transport\SendgridTransportFactory::class => 'mailer.transport_factory.sendgrid', MailerBridge\Sendinblue\Transport\SendinblueTransportFactory::class => 'mailer.transport_factory.sendinblue', @@ -2893,45 +2889,6 @@ private function registerRateLimiterConfiguration(array $config, ContainerBuilde } } - /** - * @deprecated since Symfony 6.2 - * - * @return void - */ - public static function registerRateLimiter(ContainerBuilder $container, string $name, array $limiterConfig) - { - trigger_deprecation('symfony/framework-bundle', '6.2', 'The "%s()" method is deprecated.', __METHOD__); - - // default configuration (when used by other DI extensions) - $limiterConfig += ['lock_factory' => 'lock.factory', 'cache_pool' => 'cache.rate_limiter']; - - $limiter = $container->setDefinition($limiterId = 'limiter.'.$name, new ChildDefinition('limiter')); - - if (null !== $limiterConfig['lock_factory']) { - if (!interface_exists(LockInterface::class)) { - throw new LogicException(sprintf('Rate limiter "%s" requires the Lock component to be installed. Try running "composer require symfony/lock".', $name)); - } - if (!$container->hasDefinition('lock.factory.abstract')) { - throw new LogicException(sprintf('Rate limiter "%s" requires the Lock component to be configured.', $name)); - } - - $limiter->replaceArgument(2, new Reference($limiterConfig['lock_factory'])); - } - unset($limiterConfig['lock_factory']); - - if (null === $storageId = $limiterConfig['storage_service'] ?? null) { - $container->register($storageId = 'limiter.storage.'.$name, CacheStorage::class)->addArgument(new Reference($limiterConfig['cache_pool'])); - } - - $limiter->replaceArgument(1, new Reference($storageId)); - unset($limiterConfig['storage_service'], $limiterConfig['cache_pool']); - - $limiterConfig['id'] = $name; - $limiter->replaceArgument(0, $limiterConfig); - - $container->registerAliasForArgument($limiterId, RateLimiterFactory::class, $name.'.limiter'); - } - private function registerUidConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void { $loader->load('uid.php'); diff --git a/KernelBrowser.php b/KernelBrowser.php index 694714faa..794f4f454 100644 --- a/KernelBrowser.php +++ b/KernelBrowser.php @@ -110,10 +110,6 @@ public function loginUser(object $user, string $firewallContext = 'main'): stati } $token = new TestBrowserToken($user->getRoles(), $user, $firewallContext); - // required for compatibility with Symfony 5.4 - if (method_exists($token, 'isAuthenticated')) { - $token->setAuthenticated(true, false); - } $container = $this->getContainer(); $container->get('security.untracked_token_storage')->setToken($token); diff --git a/Resources/config/annotations.php b/Resources/config/annotations.php index f043c87ec..0f561d9d3 100644 --- a/Resources/config/annotations.php +++ b/Resources/config/annotations.php @@ -22,7 +22,7 @@ return static function (ContainerConfigurator $container) { $container->services() ->set('annotations.reader', AnnotationReader::class) - ->call('addGlobalIgnoredName', ['required']) // @deprecated since Symfony 6.3 + ->call('addGlobalIgnoredName', ['required']) ->set('annotations.cached_reader', PsrCachedReader::class) ->args([ diff --git a/Resources/config/http_client.php b/Resources/config/http_client.php index 23b794f45..ed932fdf8 100644 --- a/Resources/config/http_client.php +++ b/Resources/config/http_client.php @@ -58,8 +58,6 @@ ]) ->alias(HttpAsyncClient::class, 'httplug.http_client') - ->alias(\Http\Client\HttpClient::class, 'httplug.http_client') - ->deprecate('symfony/framework-bundle', '6.3', 'The "%alias_id%" service is deprecated, use "'.ClientInterface::class.'" instead.') ->set('http_client.abstract_retry_strategy', GenericRetryStrategy::class) ->abstract() diff --git a/Resources/config/mailer_transports.php b/Resources/config/mailer_transports.php index d352eb5be..bc5900a76 100644 --- a/Resources/config/mailer_transports.php +++ b/Resources/config/mailer_transports.php @@ -19,7 +19,6 @@ use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory; use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetTransportFactory; use Symfony\Component\Mailer\Bridge\MailPace\Transport\MailPaceTransportFactory; -use Symfony\Component\Mailer\Bridge\OhMySmtp\Transport\OhMySmtpTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory; use Symfony\Component\Mailer\Bridge\Sendinblue\Transport\SendinblueTransportFactory; @@ -92,10 +91,6 @@ ->parent('mailer.transport_factory.abstract') ->tag('mailer.transport_factory') - ->set('mailer.transport_factory.ohmysmtp', OhMySmtpTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - ->set('mailer.transport_factory.smtp', EsmtpTransportFactory::class) ->parent('mailer.transport_factory.abstract') ->tag('mailer.transport_factory', ['priority' => -100]) diff --git a/Resources/config/messenger.php b/Resources/config/messenger.php index 3fe593ac6..0096ff1e0 100644 --- a/Resources/config/messenger.php +++ b/Resources/config/messenger.php @@ -209,9 +209,6 @@ ->tag('kernel.event_subscriber') ->tag('monolog.logger', ['channel' => 'messenger']) - ->alias('messenger.listener.stop_worker_on_sigterm_signal_listener', 'messenger.listener.stop_worker_signals_listener') - ->deprecate('6.3', 'symfony/messenger', 'The "%alias_id%" service is deprecated, use "messenger.listener.stop_worker_signals_listener" instead.') - ->set('messenger.listener.stop_worker_on_stop_exception_listener', StopWorkerOnCustomStopExceptionListener::class) ->tag('kernel.event_subscriber') diff --git a/Resources/config/notifier.php b/Resources/config/notifier.php index 6ce674148..3bd19b8dd 100644 --- a/Resources/config/notifier.php +++ b/Resources/config/notifier.php @@ -129,8 +129,5 @@ ->set('notifier.notification_logger_listener', NotificationLoggerListener::class) ->tag('kernel.event_subscriber') - ->alias('notifier.logger_notification_listener', 'notifier.notification_logger_listener') - ->deprecate('symfony/framework-bundle', '6.3', 'The "%alias_id%" service is deprecated, use "notifier.notification_logger_listener" instead.') - ; }; diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index a2abb5bc0..69475e307 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -31,8 +31,7 @@ - - + @@ -67,7 +66,6 @@ - @@ -409,31 +407,10 @@ - - - - - - - + - - - - - - - - - - - - - - - - + @@ -585,7 +562,6 @@ - diff --git a/Resources/config/serializer.php b/Resources/config/serializer.php index 6459dfa44..94107237f 100644 --- a/Resources/config/serializer.php +++ b/Resources/config/serializer.php @@ -42,7 +42,6 @@ use Symfony\Component\Serializer\Normalizer\FormErrorNormalizer; use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer; use Symfony\Component\Serializer\Normalizer\MimeMessageNormalizer; -use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Normalizer\ProblemNormalizer; @@ -127,9 +126,6 @@ ]) ->tag('serializer.normalizer', ['priority' => -1000]) - ->alias(ObjectNormalizer::class, 'serializer.normalizer.object') - ->deprecate('symfony/serializer', '6.2', 'The "%alias_id%" service alias is deprecated, type-hint against "'.NormalizerInterface::class.'" or implement "'.NormalizerAwareInterface::class.'" instead.') - ->set('serializer.normalizer.property', PropertyNormalizer::class) ->args([ service('serializer.mapping.class_metadata_factory'), @@ -139,9 +135,6 @@ null, ]) - ->alias(PropertyNormalizer::class, 'serializer.normalizer.property') - ->deprecate('symfony/serializer', '6.2', 'The "%alias_id%" service alias is deprecated, type-hint against "'.NormalizerInterface::class.'" or implement "'.NormalizerAwareInterface::class.'" instead.') - ->set('serializer.denormalizer.array', ArrayDenormalizer::class) ->tag('serializer.normalizer', ['priority' => -990]) diff --git a/Resources/config/translation.php b/Resources/config/translation.php index dcfa2bc15..a450e6894 100644 --- a/Resources/config/translation.php +++ b/Resources/config/translation.php @@ -27,7 +27,6 @@ use Symfony\Component\Translation\Extractor\ChainExtractor; use Symfony\Component\Translation\Extractor\ExtractorInterface; use Symfony\Component\Translation\Extractor\PhpAstExtractor; -use Symfony\Component\Translation\Extractor\PhpExtractor; use Symfony\Component\Translation\Extractor\Visitor\ConstraintVisitor; use Symfony\Component\Translation\Extractor\Visitor\TranslatableMessageVisitor; use Symfony\Component\Translation\Extractor\Visitor\TransMethodVisitor; @@ -152,10 +151,6 @@ ->set('translation.dumper.res', IcuResFileDumper::class) ->tag('translation.dumper', ['alias' => 'res']) - ->set('translation.extractor.php', PhpExtractor::class) - ->deprecate('symfony/framework-bundle', '6.2', 'The "%service_id%" service is deprecated, use "translation.extractor.php_ast" instead.') - ->tag('translation.extractor', ['alias' => 'php']) - ->set('translation.extractor.php_ast', PhpAstExtractor::class) ->args([tagged_iterator('translation.extractor.visitor')]) ->tag('translation.extractor', ['alias' => 'php']) diff --git a/Resources/config/workflow.php b/Resources/config/workflow.php index 85d786537..2c47fa960 100644 --- a/Resources/config/workflow.php +++ b/Resources/config/workflow.php @@ -40,10 +40,6 @@ ->set('workflow.marking_store.method', MethodMarkingStore::class) ->abstract() ->set('.workflow.registry', Registry::class) - ->alias(Registry::class, '.workflow.registry') - ->deprecate('symfony/workflow', '6.2', 'The "%alias_id%" alias is deprecated, inject the workflow directly.') - ->alias('workflow.registry', '.workflow.registry') - ->deprecate('symfony/workflow', '6.2', 'The "%alias_id%" alias is deprecated, inject the workflow directly.') ->set('workflow.security.expression_language', ExpressionLanguage::class) ; }; diff --git a/Tests/Controller/AbstractControllerTest.php b/Tests/Controller/AbstractControllerTest.php index efa9c7bec..14e5abd01 100644 --- a/Tests/Controller/AbstractControllerTest.php +++ b/Tests/Controller/AbstractControllerTest.php @@ -471,58 +471,6 @@ public function testRenderWithFormSubmittedAndInvalid() $this->assertSame('bar', $response->getContent()); } - /** - * @group legacy - */ - public function testRenderForm() - { - $formView = new FormView(); - - $form = $this->getMockBuilder(FormInterface::class)->getMock(); - $form->expects($this->once())->method('createView')->willReturn($formView); - - $twig = $this->getMockBuilder(Environment::class)->disableOriginalConstructor()->getMock(); - $twig->expects($this->once())->method('render')->with('foo', ['bar' => $formView])->willReturn('bar'); - - $container = new Container(); - $container->set('twig', $twig); - - $controller = $this->createController(); - $controller->setContainer($container); - - $response = $controller->renderForm('foo', ['bar' => $form]); - - $this->assertTrue($response->isSuccessful()); - $this->assertSame('bar', $response->getContent()); - } - - /** - * @group legacy - */ - public function testRenderFormSubmittedAndInvalid() - { - $formView = new FormView(); - - $form = $this->getMockBuilder(FormInterface::class)->getMock(); - $form->expects($this->once())->method('createView')->willReturn($formView); - $form->expects($this->once())->method('isSubmitted')->willReturn(true); - $form->expects($this->once())->method('isValid')->willReturn(false); - - $twig = $this->getMockBuilder(Environment::class)->disableOriginalConstructor()->getMock(); - $twig->expects($this->once())->method('render')->with('foo', ['bar' => $formView])->willReturn('bar'); - - $container = new Container(); - $container->set('twig', $twig); - - $controller = $this->createController(); - $controller->setContainer($container); - - $response = $controller->renderForm('foo', ['bar' => $form]); - - $this->assertSame(422, $response->getStatusCode()); - $this->assertSame('bar', $response->getContent()); - } - public function testStreamTwig() { $twig = $this->createMock(Environment::class); diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index e3ecd982d..2e291157a 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -676,7 +676,6 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor ], 'default_bus' => null, 'buses' => ['messenger.bus.default' => ['default_middleware' => ['enabled' => true, 'allow_no_handlers' => false, 'allow_no_senders' => true], 'middleware' => []]], - 'reset_on_message' => true, 'stop_worker_on_signals' => [], ], 'disallow_search_engine_index' => true, diff --git a/Tests/DependencyInjection/Fixtures/php/messenger_with_disabled_reset_on_message.php b/Tests/DependencyInjection/Fixtures/php/messenger_with_disabled_reset_on_message.php deleted file mode 100644 index 3cb006c46..000000000 --- a/Tests/DependencyInjection/Fixtures/php/messenger_with_disabled_reset_on_message.php +++ /dev/null @@ -1,20 +0,0 @@ -loadFromExtension('framework', [ - 'http_method_override' => false, - 'messenger' => [ - 'reset_on_message' => false, - 'routing' => [ - FooMessage::class => ['sender.bar', 'sender.biz'], - BarMessage::class => 'sender.foo', - ], - 'transports' => [ - 'sender.biz' => 'null://', - 'sender.bar' => 'null://', - 'sender.foo' => 'null://', - ], - ], -]); diff --git a/Tests/DependencyInjection/Fixtures/php/messenger_with_explict_reset_on_message_legacy.php b/Tests/DependencyInjection/Fixtures/php/messenger_with_explict_reset_on_message_legacy.php deleted file mode 100644 index ee689ae09..000000000 --- a/Tests/DependencyInjection/Fixtures/php/messenger_with_explict_reset_on_message_legacy.php +++ /dev/null @@ -1,20 +0,0 @@ -loadFromExtension('framework', [ - 'http_method_override' => false, - 'messenger' => [ - 'reset_on_message' => true, - 'routing' => [ - FooMessage::class => ['sender.bar', 'sender.biz'], - BarMessage::class => 'sender.foo', - ], - 'transports' => [ - 'sender.biz' => 'null://', - 'sender.bar' => 'null://', - 'sender.foo' => 'null://', - ], - ], -]); diff --git a/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml b/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml deleted file mode 100644 index 2e6048fa7..000000000 --- a/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - diff --git a/Tests/DependencyInjection/Fixtures/xml/messenger_with_disabled_reset_on_message.xml b/Tests/DependencyInjection/Fixtures/xml/messenger_with_disabled_reset_on_message.xml deleted file mode 100644 index c0bc33bcd..000000000 --- a/Tests/DependencyInjection/Fixtures/xml/messenger_with_disabled_reset_on_message.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/Tests/DependencyInjection/Fixtures/xml/messenger_with_explict_reset_on_message_legacy.xml b/Tests/DependencyInjection/Fixtures/xml/messenger_with_explict_reset_on_message_legacy.xml deleted file mode 100644 index 4c208aad2..000000000 --- a/Tests/DependencyInjection/Fixtures/xml/messenger_with_explict_reset_on_message_legacy.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 97831b04e..968195153 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -14,7 +14,6 @@ use Doctrine\Common\Annotations\Annotation; use Psr\Cache\CacheItemPoolInterface; use Psr\Log\LoggerAwareInterface; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddAnnotationsCachedReaderPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; @@ -93,8 +92,6 @@ abstract class FrameworkExtensionTestCase extends TestCase { - use ExpectDeprecationTrait; - private static $containerCache = []; abstract protected function loadFromFile(ContainerBuilder $container, $file); @@ -507,7 +504,7 @@ public function testWorkflowServicesCanBeEnabled() { $container = $this->createContainerFromFile('workflows_enabled'); - $this->assertTrue($container->has(Workflow\Registry::class)); + $this->assertTrue($container->hasDefinition('.workflow.registry')); $this->assertTrue($container->hasDefinition('console.command.workflow_dump')); } @@ -791,26 +788,6 @@ public function testMessengerServicesRemovedWhenDisabled() $this->assertFalse($container->hasDefinition('cache.messenger.restart_workers_signal')); } - /** - * @group legacy - */ - public function testMessengerWithExplictResetOnMessageLegacy() - { - $this->expectDeprecation('Since symfony/framework-bundle 6.1: Option "reset_on_message" at "framework.messenger" is deprecated. It does nothing and will be removed in version 7.0.'); - - $container = $this->createContainerFromFile('messenger_with_explict_reset_on_message_legacy'); - - $this->assertTrue($container->hasDefinition('console.command.messenger_consume_messages')); - $this->assertTrue($container->hasAlias('messenger.default_bus')); - $this->assertTrue($container->getAlias('messenger.default_bus')->isPublic()); - $this->assertTrue($container->hasDefinition('messenger.transport.amqp.factory')); - $this->assertTrue($container->hasDefinition('messenger.transport.redis.factory')); - $this->assertTrue($container->hasDefinition('messenger.transport_factory')); - $this->assertSame(TransportFactory::class, $container->getDefinition('messenger.transport_factory')->getClass()); - $this->assertTrue($container->hasDefinition('messenger.listener.reset_services')); - $this->assertSame('messenger.listener.reset_services', (string) $container->getDefinition('console.command.messenger_consume_messages')->getArgument(5)); - } - public function testMessenger() { $container = $this->createContainerFromFile('messenger', [], true, false); @@ -1117,17 +1094,6 @@ public function testMessengerInvalidWildcardRouting() $this->createContainerFromFile('messenger_routing_invalid_transport'); } - /** - * @group legacy - */ - public function testMessengerWithDisabledResetOnMessage() - { - $this->expectException(InvalidConfigurationException::class); - $this->expectExceptionMessage('The "framework.messenger.reset_on_message" configuration option can be set to "true" only. To prevent services resetting after each message you can set the "--no-reset" option in "messenger:consume" command.'); - - $this->createContainerFromFile('messenger_with_disabled_reset_on_message'); - } - public function testTranslator() { $container = $this->createContainerFromFile('full'); diff --git a/Tests/DependencyInjection/XmlFrameworkExtensionTest.php b/Tests/DependencyInjection/XmlFrameworkExtensionTest.php index 36d3f5e37..40b3588d7 100644 --- a/Tests/DependencyInjection/XmlFrameworkExtensionTest.php +++ b/Tests/DependencyInjection/XmlFrameworkExtensionTest.php @@ -33,40 +33,6 @@ public function testMessengerMiddlewareFactoryErroneousFormat() $this->markTestSkipped('XML configuration will not allow erroneous format.'); } - public function testLegacyExceptionsConfig() - { - $container = $this->createContainerFromFile('exceptions_legacy'); - - $configuration = $container->getDefinition('exception_listener')->getArgument(3); - - $this->assertSame([ - \Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class, - \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class, - \Symfony\Component\HttpKernel\Exception\ConflictHttpException::class, - \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class, - ], array_keys($configuration)); - - $this->assertEqualsCanonicalizing([ - 'log_level' => 'info', - 'status_code' => 422, - ], $configuration[\Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class]); - - $this->assertEqualsCanonicalizing([ - 'log_level' => 'info', - 'status_code' => null, - ], $configuration[\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class]); - - $this->assertEqualsCanonicalizing([ - 'log_level' => 'info', - 'status_code' => null, - ], $configuration[\Symfony\Component\HttpKernel\Exception\ConflictHttpException::class]); - - $this->assertEqualsCanonicalizing([ - 'log_level' => null, - 'status_code' => 500, - ], $configuration[\Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class]); - } - public function testRateLimiter() { $container = $this->createContainerFromFile('rate_limiter'); diff --git a/composer.json b/composer.json index 722a4c5f1..069dcc7b4 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,6 @@ "symfony/cache": "^6.4|^7.0", "symfony/config": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", - "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.4|^7.0", "symfony/event-dispatcher": "^6.4|^7.0", "symfony/http-foundation": "^6.4|^7.0", From 230c79ae9f1f5837e2fac0a9fb6cb0c8ad075f33 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 2 Jul 2023 23:52:21 +0200 Subject: [PATCH 008/281] [Components] Convert to native return types --- Command/BuildDebugContainerTrait.php | 2 +- Test/KernelTestCase.php | 1 - Tests/Controller/ControllerResolverTest.php | 1 - Tests/Functional/app/AppKernel.php | 2 +- Tests/Kernel/ConcreteMicroKernel.php | 2 +- Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php | 2 +- 6 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Command/BuildDebugContainerTrait.php b/Command/BuildDebugContainerTrait.php index 83268ae71..2f625e9e3 100644 --- a/Command/BuildDebugContainerTrait.php +++ b/Command/BuildDebugContainerTrait.php @@ -55,7 +55,7 @@ protected function getContainerBuilder(KernelInterface $kernel): ContainerBuilde $this->prepareContainer($containerBuilder); return $containerBuilder; - }, $kernel, \get_class($kernel)); + }, $kernel, $kernel::class); $container = $buildContainer(); (new XmlFileLoader($container, new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump')); $locatorPass = new ServiceLocatorTagPass(); diff --git a/Test/KernelTestCase.php b/Test/KernelTestCase.php index 639214a43..e5ef9112c 100644 --- a/Test/KernelTestCase.php +++ b/Test/KernelTestCase.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Contracts\Service\ResetInterface; diff --git a/Tests/Controller/ControllerResolverTest.php b/Tests/Controller/ControllerResolverTest.php index 77e5ba14b..0c51924a4 100644 --- a/Tests/Controller/ControllerResolverTest.php +++ b/Tests/Controller/ControllerResolverTest.php @@ -15,7 +15,6 @@ use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver; -use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ContainerAwareController; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/Tests/Functional/app/AppKernel.php b/Tests/Functional/app/AppKernel.php index 3f99eff48..80888c4a7 100644 --- a/Tests/Functional/app/AppKernel.php +++ b/Tests/Functional/app/AppKernel.php @@ -89,7 +89,7 @@ public function __sleep(): array return ['varDir', 'testCase', 'rootConfig', 'environment', 'debug']; } - public function __wakeup() + public function __wakeup(): void { foreach ($this as $k => $v) { if (\is_object($v)) { diff --git a/Tests/Kernel/ConcreteMicroKernel.php b/Tests/Kernel/ConcreteMicroKernel.php index def880b23..a51078adb 100644 --- a/Tests/Kernel/ConcreteMicroKernel.php +++ b/Tests/Kernel/ConcreteMicroKernel.php @@ -68,7 +68,7 @@ public function __sleep(): array throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } - public function __wakeup() + public function __wakeup(): void { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } diff --git a/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php b/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php index bf529a580..b15729362 100644 --- a/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php +++ b/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php @@ -69,7 +69,7 @@ public function __sleep(): array throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } - public function __wakeup() + public function __wakeup(): void { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } From feae13a7313320d89dc9d1d2736548501a797cca Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 9 Jul 2023 09:55:52 +0200 Subject: [PATCH 009/281] [Mailer] Remove the Sendinblue bridge --- DependencyInjection/FrameworkExtension.php | 1 - Resources/config/mailer_transports.php | 5 ----- 2 files changed, 6 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index cd70004a6..dea020b8f 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2591,7 +2591,6 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co MailerBridge\Mailchimp\Transport\MandrillTransportFactory::class => 'mailer.transport_factory.mailchimp', MailerBridge\Postmark\Transport\PostmarkTransportFactory::class => 'mailer.transport_factory.postmark', MailerBridge\Sendgrid\Transport\SendgridTransportFactory::class => 'mailer.transport_factory.sendgrid', - MailerBridge\Sendinblue\Transport\SendinblueTransportFactory::class => 'mailer.transport_factory.sendinblue', MailerBridge\Amazon\Transport\SesTransportFactory::class => 'mailer.transport_factory.amazon', ]; diff --git a/Resources/config/mailer_transports.php b/Resources/config/mailer_transports.php index 6e2c816f7..fb9d4567e 100644 --- a/Resources/config/mailer_transports.php +++ b/Resources/config/mailer_transports.php @@ -22,7 +22,6 @@ use Symfony\Component\Mailer\Bridge\MailPace\Transport\MailPaceTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory; -use Symfony\Component\Mailer\Bridge\Sendinblue\Transport\SendinblueTransportFactory; use Symfony\Component\Mailer\Transport\AbstractTransportFactory; use Symfony\Component\Mailer\Transport\NativeTransportFactory; use Symfony\Component\Mailer\Transport\NullTransportFactory; @@ -92,10 +91,6 @@ ->parent('mailer.transport_factory.abstract') ->tag('mailer.transport_factory') - ->set('mailer.transport_factory.sendinblue', SendinblueTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - ->set('mailer.transport_factory.smtp', EsmtpTransportFactory::class) ->parent('mailer.transport_factory.abstract') ->tag('mailer.transport_factory', ['priority' => -100]) From 34ea139335191a46bf2948074932df69fe8c4ba5 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 9 Jul 2023 09:58:43 +0200 Subject: [PATCH 010/281] [Notifier] Remove the Sendinblue bridge --- DependencyInjection/FrameworkExtension.php | 1 - Resources/config/notifier_transports.php | 4 ---- 2 files changed, 5 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index cd70004a6..d5970e407 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2759,7 +2759,6 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\RocketChat\RocketChatTransportFactory::class => 'notifier.transport_factory.rocket-chat', NotifierBridge\Sendberry\SendberryTransportFactory::class => 'notifier.transport_factory.sendberry', NotifierBridge\SimpleTextin\SimpleTextinTransportFactory::class => 'notifier.transport_factory.simple-textin', - NotifierBridge\Sendinblue\SendinblueTransportFactory::class => 'notifier.transport_factory.sendinblue', NotifierBridge\Sinch\SinchTransportFactory::class => 'notifier.transport_factory.sinch', NotifierBridge\Slack\SlackTransportFactory::class => 'notifier.transport_factory.slack', NotifierBridge\Sms77\Sms77TransportFactory::class => 'notifier.transport_factory.sms77', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index 445fe2a47..b1e5e9699 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -126,10 +126,6 @@ ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') - ->set('notifier.transport_factory.sendinblue', Bridge\Sendinblue\SendinblueTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - ->set('notifier.transport_factory.iqsms', Bridge\Iqsms\IqsmsTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') From 85500259147c01b4384d1c5624a51455d6b43e35 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sat, 15 Jul 2023 16:02:21 +0200 Subject: [PATCH 011/281] [FrameworkBundle] Remove doctrine/annotations integration --- CHANGELOG.md | 1 + CacheWarmer/AnnotationsCacheWarmer.php | 114 ----------- .../Compiler/UnusedTagsPass.php | 3 +- DependencyInjection/Configuration.php | 19 +- DependencyInjection/FrameworkExtension.php | 61 +----- Resources/config/annotations.php | 71 ------- .../AnnotationsCacheWarmerTest.php | 191 ------------------ .../DependencyInjection/ConfigurationTest.php | 5 +- .../Fixtures/php/legacy_annotations.php | 10 - .../php/validation_legacy_annotations.php | 15 -- .../Fixtures/xml/legacy_annotations.xml | 16 -- .../xml/validation_legacy_annotations.xml | 17 -- .../Fixtures/yml/legacy_annotations.yml | 7 - .../yml/validation_legacy_annotations.yml | 13 -- .../FrameworkExtensionTestCase.php | 58 +----- Tests/Functional/AutowiringTypesTest.php | 31 --- .../AutowiringTypes/AutowiredServices.php | 19 +- .../AutowiringTypes/no_annotations_cache.yml | 8 - .../app/AutowiringTypes/with_annotations.yml | 6 - composer.json | 2 - 20 files changed, 24 insertions(+), 643 deletions(-) delete mode 100644 CacheWarmer/AnnotationsCacheWarmer.php delete mode 100644 Resources/config/annotations.php delete mode 100644 Tests/CacheWarmer/AnnotationsCacheWarmerTest.php delete mode 100644 Tests/DependencyInjection/Fixtures/php/legacy_annotations.php delete mode 100644 Tests/DependencyInjection/Fixtures/php/validation_legacy_annotations.php delete mode 100644 Tests/DependencyInjection/Fixtures/xml/legacy_annotations.xml delete mode 100644 Tests/DependencyInjection/Fixtures/xml/validation_legacy_annotations.xml delete mode 100644 Tests/DependencyInjection/Fixtures/yml/legacy_annotations.yml delete mode 100644 Tests/DependencyInjection/Fixtures/yml/validation_legacy_annotations.yml delete mode 100644 Tests/Functional/app/AutowiringTypes/no_annotations_cache.yml delete mode 100644 Tests/Functional/app/AutowiringTypes/with_annotations.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index fb6e47029..bf516bbe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ CHANGELOG `Symfony\Component\Serializer\Normalizer\PropertyNormalizer` autowiring aliases, type-hint against `Symfony\Component\Serializer\Normalizer\NormalizerInterface` or implement `NormalizerAwareInterface` instead * Remove the `Http\Client\HttpClient` service, use `Psr\Http\Client\ClientInterface` instead + * Remove the integration of Doctrine annotations, use native attributes instead 6.4 --- diff --git a/CacheWarmer/AnnotationsCacheWarmer.php b/CacheWarmer/AnnotationsCacheWarmer.php deleted file mode 100644 index 279dc4ec9..000000000 --- a/CacheWarmer/AnnotationsCacheWarmer.php +++ /dev/null @@ -1,114 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; - -use Doctrine\Common\Annotations\AnnotationException; -use Doctrine\Common\Annotations\PsrCachedReader; -use Doctrine\Common\Annotations\Reader; -use Symfony\Component\Cache\Adapter\ArrayAdapter; -use Symfony\Component\Cache\Adapter\PhpArrayAdapter; - -/** - * Warms up annotation caches for classes found in composer's autoload class map - * and declared in DI bundle extensions using the addAnnotatedClassesToCache method. - * - * @author Titouan Galopin - * - * @deprecated since Symfony 6.4 without replacement - */ -class AnnotationsCacheWarmer extends AbstractPhpFileCacheWarmer -{ - /** - * @param string $phpArrayFile The PHP file where annotations are cached - */ - public function __construct( - private readonly Reader $annotationReader, - string $phpArrayFile, - private readonly ?string $excludeRegexp = null, - private readonly bool $debug = false, - /* bool $triggerDeprecation = true, */ - ) { - if (\func_num_args() < 5 || func_get_arg(4)) { - trigger_deprecation('symfony/framework-bundle', '6.4', 'The "%s" class is deprecated without replacement.', __CLASS__); - } - - parent::__construct($phpArrayFile); - } - - protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter): bool - { - $annotatedClassPatterns = $cacheDir.'/annotations.map'; - - if (!is_file($annotatedClassPatterns)) { - return true; - } - - $annotatedClasses = include $annotatedClassPatterns; - $reader = new PsrCachedReader($this->annotationReader, $arrayAdapter, $this->debug); - - foreach ($annotatedClasses as $class) { - if (null !== $this->excludeRegexp && preg_match($this->excludeRegexp, $class)) { - continue; - } - try { - $this->readAllComponents($reader, $class); - } catch (\Exception $e) { - $this->ignoreAutoloadException($class, $e); - } - } - - return true; - } - - /** - * @return string[] A list of classes to preload on PHP 7.4+ - */ - protected function warmUpPhpArrayAdapter(PhpArrayAdapter $phpArrayAdapter, array $values): array - { - // make sure we don't cache null values - $values = array_filter($values, fn ($val) => null !== $val); - - return parent::warmUpPhpArrayAdapter($phpArrayAdapter, $values); - } - - private function readAllComponents(Reader $reader, string $class): void - { - $reflectionClass = new \ReflectionClass($class); - - try { - $reader->getClassAnnotations($reflectionClass); - } catch (AnnotationException) { - /* - * Ignore any AnnotationException to not break the cache warming process if an Annotation is badly - * configured or could not be found / read / etc. - * - * In particular cases, an Annotation in your code can be used and defined only for a specific - * environment but is always added to the annotations.map file by some Symfony default behaviors, - * and you always end up with a not found Annotation. - */ - } - - foreach ($reflectionClass->getMethods() as $reflectionMethod) { - try { - $reader->getMethodAnnotations($reflectionMethod); - } catch (AnnotationException) { - } - } - - foreach ($reflectionClass->getProperties() as $reflectionProperty) { - try { - $reader->getPropertyAnnotations($reflectionProperty); - } catch (AnnotationException) { - } - } - } -} diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index ae5daac8e..c21b7369d 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -23,9 +23,9 @@ class UnusedTagsPass implements CompilerPassInterface { private const KNOWN_TAGS = [ 'annotations.cached_reader', - 'assets.package', 'asset_mapper.compiler', 'asset_mapper.importmap.resolver', + 'assets.package', 'auto_alias', 'cache.pool', 'cache.pool.clearer', @@ -33,7 +33,6 @@ class UnusedTagsPass implements CompilerPassInterface 'chatter.transport_factory', 'config_cache.resource_checker', 'console.command', - 'container.do_not_inline', 'container.env_var_loader', 'container.env_var_processor', 'container.excluded', diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 8ca6a54c0..29048942b 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; -use Doctrine\Common\Annotations\Annotation; use Doctrine\DBAL\Connection; use Psr\Log\LogLevel; use Symfony\Bundle\FullStack; @@ -155,7 +154,7 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addAssetMapperSection($rootNode, $enableIfStandalone); $this->addTranslatorSection($rootNode, $enableIfStandalone); $this->addValidationSection($rootNode, $enableIfStandalone); - $this->addAnnotationsSection($rootNode, $willBeAvailable); + $this->addAnnotationsSection($rootNode); $this->addSerializerSection($rootNode, $enableIfStandalone); $this->addPropertyAccessSection($rootNode, $willBeAvailable); $this->addPropertyInfoSection($rootNode, $enableIfStandalone); @@ -1057,21 +1056,15 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e ; } - private function addAnnotationsSection(ArrayNodeDefinition $rootNode, callable $willBeAvailable): void + private function addAnnotationsSection(ArrayNodeDefinition $rootNode): void { $rootNode ->children() ->arrayNode('annotations') - ->info('annotation configuration') - ->{$willBeAvailable('doctrine/annotations', Annotation::class) ? 'canBeDisabled' : 'canBeEnabled'}() - ->children() - ->enumNode('cache') - ->values(['none', 'php_array', 'file']) - ->defaultValue('php_array') - ->end() - ->scalarNode('file_cache_dir')->defaultValue('%kernel.cache_dir%/annotations')->end() - ->booleanNode('debug')->defaultValue($this->debug)->end() - ->end() + ->canBeEnabled() + ->validate() + ->ifTrue(static fn (array $v) => $v['enabled']) + ->thenInvalid('Enabling the doctrine/annotations integration is not supported anymore.') ->end() ->end() ; diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index e266ab1f0..d4b16662a 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -12,7 +12,6 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; use Composer\InstalledVersions; -use Doctrine\Common\Annotations\Reader; use Http\Client\HttpAsyncClient; use Http\Client\HttpClient; use phpDocumentor\Reflection\DocBlockFactoryInterface; @@ -54,7 +53,6 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\DependencyInjection\Alias; -use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -255,7 +253,6 @@ public function load(array $configs, ContainerBuilder $container): void $config = $this->processConfiguration($configuration, $configs); // warmup config enabled - $this->readConfigEnabled('annotations', $container, $config['annotations']); $this->readConfigEnabled('translator', $container, $config['translator']); $this->readConfigEnabled('property_access', $container, $config['property_access']); $this->readConfigEnabled('profiler', $container, $config['profiler']); @@ -363,7 +360,6 @@ public function load(array $configs, ContainerBuilder $container): void $this->registerWorkflowConfiguration($config['workflows'], $container, $loader); $this->registerDebugConfiguration($config['php_errors'], $container, $loader); $this->registerRouterConfiguration($config['router'], $container, $loader, $config['enabled_locales']); - $this->registerAnnotationsConfiguration($config['annotations'], $container, $loader); $this->registerPropertyAccessConfiguration($config['property_access'], $container, $loader); $this->registerSecretsConfiguration($config['secrets'], $container, $loader); @@ -1600,10 +1596,7 @@ private function registerValidationConfiguration(array $config, ContainerBuilder $definition->replaceArgument(0, $config['email_validation_mode']); if (\array_key_exists('enable_annotations', $config) && $config['enable_annotations']) { - $validatorBuilder->addMethodCall('enableAnnotationMapping', [true]); - if ($this->isInitializedConfigEnabled('annotations')) { - $validatorBuilder->addMethodCall('setDoctrineAnnotationReader', [new Reference('annotation_reader')]); - } + $validatorBuilder->addMethodCall('enableAnnotationMapping'); } if (\array_key_exists('static_method', $config) && $config['static_method']) { @@ -1698,58 +1691,6 @@ private function registerMappingFilesFromConfig(ContainerBuilder $container, arr } } - private function registerAnnotationsConfiguration(array $config, ContainerBuilder $container, LoaderInterface $loader): void - { - if (!$this->isInitializedConfigEnabled('annotations')) { - return; - } - - if (!class_exists(\Doctrine\Common\Annotations\Annotation::class)) { - throw new LogicException('Annotations cannot be enabled as the Doctrine Annotation library is not installed. Try running "composer require doctrine/annotations".'); - } - - trigger_deprecation('symfony/framework-bundle', '6.4', 'Enabling the integration of Doctrine annotations is deprecated. Set the "framework.annotations.enabled" config option to false.'); - - $loader->load('annotations.php'); - - if ('none' === $config['cache']) { - $container->removeDefinition('annotations.cached_reader'); - - return; - } - - if ('php_array' === $config['cache']) { - $cacheService = 'annotations.cache_adapter'; - - // Enable warmer only if PHP array is used for cache - $definition = $container->findDefinition('annotations.cache_warmer'); - $definition->addTag('kernel.cache_warmer'); - } else { - $cacheService = 'annotations.filesystem_cache_adapter'; - $cacheDir = $container->getParameterBag()->resolveValue($config['file_cache_dir']); - - if (!is_dir($cacheDir) && false === @mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { - throw new \RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir)); - } - - $container - ->getDefinition('annotations.filesystem_cache_adapter') - ->replaceArgument(2, $cacheDir) - ; - } - - $container - ->getDefinition('annotations.cached_reader') - ->replaceArgument(2, $config['debug']) - // reference the cache provider without using it until AddAnnotationsCachedReaderPass runs - ->addArgument(new ServiceClosureArgument(new Reference($cacheService))) - ; - - $container->setAlias('annotation_reader', 'annotations.cached_reader'); - $container->setAlias(Reader::class, new Alias('annotations.cached_reader', false)); - $container->removeDefinition('annotations.psr_cached_reader'); - } - private function registerPropertyAccessConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void { if (!$this->readConfigEnabled('property_access', $container, $config)) { diff --git a/Resources/config/annotations.php b/Resources/config/annotations.php deleted file mode 100644 index ddf65175b..000000000 --- a/Resources/config/annotations.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Loader\Configurator; - -use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Annotations\PsrCachedReader; -use Doctrine\Common\Annotations\Reader; -use Symfony\Bundle\FrameworkBundle\CacheWarmer\AnnotationsCacheWarmer; -use Symfony\Component\Cache\Adapter\ArrayAdapter; -use Symfony\Component\Cache\Adapter\FilesystemAdapter; -use Symfony\Component\Cache\Adapter\PhpArrayAdapter; - -return static function (ContainerConfigurator $container) { - $container->services() - ->set('annotations.reader', AnnotationReader::class) - ->call('addGlobalIgnoredName', ['required']) - ->deprecate('symfony/framework-bundle', '6.4', 'The "%service_id%" service is deprecated without replacement.') - - ->set('annotations.cached_reader', PsrCachedReader::class) - ->args([ - service('annotations.reader'), - inline_service(ArrayAdapter::class), - abstract_arg('Debug-Flag'), - ]) - ->tag('annotations.cached_reader') - ->tag('container.do_not_inline') - ->deprecate('symfony/framework-bundle', '6.4', 'The "%service_id%" service is deprecated without replacement.') - - ->set('annotations.filesystem_cache_adapter', FilesystemAdapter::class) - ->args([ - '', - 0, - abstract_arg('Cache-Directory'), - ]) - ->deprecate('symfony/framework-bundle', '6.4', 'The "%service_id%" service is deprecated without replacement.') - - ->set('annotations.cache_warmer', AnnotationsCacheWarmer::class) - ->args([ - service('annotations.reader'), - param('kernel.cache_dir').'/annotations.php', - '#^Symfony\\\\(?:Component\\\\HttpKernel\\\\|Bundle\\\\FrameworkBundle\\\\Controller\\\\(?!.*Controller$))#', - param('kernel.debug'), - false, - ]) - ->deprecate('symfony/framework-bundle', '6.4', 'The "%service_id%" service is deprecated without replacement.') - - ->set('annotations.cache_adapter', PhpArrayAdapter::class) - ->factory([PhpArrayAdapter::class, 'create']) - ->args([ - param('kernel.cache_dir').'/annotations.php', - service('cache.annotations'), - ]) - ->tag('container.hot_path') - ->deprecate('symfony/framework-bundle', '6.4', 'The "%service_id%" service is deprecated without replacement.') - - ->alias('annotation_reader', 'annotations.reader') - ->deprecate('symfony/framework-bundle', '6.4', 'The "%alias_id%" service alias is deprecated without replacement.') - - ->alias(Reader::class, 'annotation_reader') - ->deprecate('symfony/framework-bundle', '6.4', 'The "%alias_id%" service alias is deprecated without replacement.') - ; -}; diff --git a/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php b/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php deleted file mode 100644 index 6ff003b47..000000000 --- a/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php +++ /dev/null @@ -1,191 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\CacheWarmer; - -use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Annotations\PsrCachedReader; -use Doctrine\Common\Annotations\Reader; -use PHPUnit\Framework\MockObject\MockObject; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; -use Symfony\Bundle\FrameworkBundle\CacheWarmer\AnnotationsCacheWarmer; -use Symfony\Bundle\FrameworkBundle\Tests\TestCase; -use Symfony\Component\Cache\Adapter\ArrayAdapter; -use Symfony\Component\Cache\Adapter\NullAdapter; -use Symfony\Component\Cache\Adapter\PhpArrayAdapter; -use Symfony\Component\Filesystem\Filesystem; - -/** - * @group legacy - */ -class AnnotationsCacheWarmerTest extends TestCase -{ - use ExpectDeprecationTrait; - - private string $cacheDir; - - protected function setUp(): void - { - $this->cacheDir = sys_get_temp_dir().'/'.uniqid(); - $fs = new Filesystem(); - $fs->mkdir($this->cacheDir); - parent::setUp(); - } - - protected function tearDown(): void - { - $fs = new Filesystem(); - $fs->remove($this->cacheDir); - parent::tearDown(); - } - - public function testAnnotationsCacheWarmerWithDebugDisabled() - { - file_put_contents($this->cacheDir.'/annotations.map', sprintf('cacheDir, __FUNCTION__); - $reader = new AnnotationReader(); - - $this->expectDeprecation('Since symfony/framework-bundle 6.4: The "Symfony\Bundle\FrameworkBundle\CacheWarmer\AnnotationsCacheWarmer" class is deprecated without replacement.'); - $warmer = new AnnotationsCacheWarmer($reader, $cacheFile); - - $warmer->warmUp($this->cacheDir); - $this->assertFileExists($cacheFile); - - // Assert cache is valid - $reader = new PsrCachedReader( - $this->getReadOnlyReader(), - new PhpArrayAdapter($cacheFile, new NullAdapter()) - ); - $refClass = new \ReflectionClass($this); - $reader->getClassAnnotations($refClass); - $reader->getMethodAnnotations($refClass->getMethod(__FUNCTION__)); - $reader->getPropertyAnnotations($refClass->getProperty('cacheDir')); - } - - public function testAnnotationsCacheWarmerWithDebugEnabled() - { - file_put_contents($this->cacheDir.'/annotations.map', sprintf('cacheDir, __FUNCTION__); - $reader = new AnnotationReader(); - - $this->expectDeprecation('Since symfony/framework-bundle 6.4: The "Symfony\Bundle\FrameworkBundle\CacheWarmer\AnnotationsCacheWarmer" class is deprecated without replacement.'); - $warmer = new AnnotationsCacheWarmer($reader, $cacheFile, null, true); - - $warmer->warmUp($this->cacheDir); - $this->assertFileExists($cacheFile); - - // Assert cache is valid - $phpArrayAdapter = new PhpArrayAdapter($cacheFile, new NullAdapter()); - $reader = new PsrCachedReader( - $this->getReadOnlyReader(), - $phpArrayAdapter, - true - ); - $refClass = new \ReflectionClass($this); - $reader->getClassAnnotations($refClass); - $reader->getMethodAnnotations($refClass->getMethod(__FUNCTION__)); - $reader->getPropertyAnnotations($refClass->getProperty('cacheDir')); - } - - /** - * Test that the cache warming process is not broken if a class loader - * throws an exception (on class / file not found for example). - */ - public function testClassAutoloadException() - { - $this->assertFalse(class_exists($annotatedClass = 'C\C\C', false)); - - file_put_contents($this->cacheDir.'/annotations.map', sprintf('expectDeprecation('Since symfony/framework-bundle 6.4: The "Symfony\Bundle\FrameworkBundle\CacheWarmer\AnnotationsCacheWarmer" class is deprecated without replacement.'); - $warmer = new AnnotationsCacheWarmer(new AnnotationReader(), tempnam($this->cacheDir, __FUNCTION__)); - - spl_autoload_register($classLoader = function ($class) use ($annotatedClass) { - if ($class === $annotatedClass) { - throw new \DomainException('This exception should be caught by the warmer.'); - } - }, true, true); - - $warmer->warmUp($this->cacheDir); - - spl_autoload_unregister($classLoader); - } - - /** - * Test that the cache warming process is broken if a class loader throws an - * exception but that is unrelated to the class load. - */ - public function testClassAutoloadExceptionWithUnrelatedException() - { - $this->expectException(\DomainException::class); - $this->expectExceptionMessage('This exception should not be caught by the warmer.'); - - $this->assertFalse(class_exists($annotatedClass = 'AClassThatDoesNotExist_FWB_CacheWarmer_AnnotationsCacheWarmerTest', false)); - - file_put_contents($this->cacheDir.'/annotations.map', sprintf('expectDeprecation('Since symfony/framework-bundle 6.4: The "Symfony\Bundle\FrameworkBundle\CacheWarmer\AnnotationsCacheWarmer" class is deprecated without replacement.'); - $warmer = new AnnotationsCacheWarmer(new AnnotationReader(), tempnam($this->cacheDir, __FUNCTION__)); - - spl_autoload_register($classLoader = function ($class) use ($annotatedClass) { - if ($class === $annotatedClass) { - eval('class '.$annotatedClass.'{}'); - throw new \DomainException('This exception should not be caught by the warmer.'); - } - }, true, true); - - $warmer->warmUp($this->cacheDir); - - spl_autoload_unregister($classLoader); - } - - public function testWarmupRemoveCacheMisses() - { - $cacheFile = tempnam($this->cacheDir, __FUNCTION__); - $this->expectDeprecation('Since symfony/framework-bundle 6.4: The "Symfony\Bundle\FrameworkBundle\CacheWarmer\AnnotationsCacheWarmer" class is deprecated without replacement.'); - $warmer = $this->getMockBuilder(AnnotationsCacheWarmer::class) - ->setConstructorArgs([new AnnotationReader(), $cacheFile]) - ->onlyMethods(['doWarmUp']) - ->getMock(); - - $warmer->method('doWarmUp')->willReturnCallback(function ($cacheDir, ArrayAdapter $arrayAdapter) { - $arrayAdapter->getItem('foo_miss'); - - $item = $arrayAdapter->getItem('bar_hit'); - $item->set('data'); - $arrayAdapter->save($item); - - $item = $arrayAdapter->getItem('baz_hit_null'); - $item->set(null); - $arrayAdapter->save($item); - - return true; - }); - - $warmer->warmUp($this->cacheDir); - $data = include $cacheFile; - - $this->assertCount(1, $data[0]); - $this->assertTrue(isset($data[0]['bar_hit'])); - } - - private function getReadOnlyReader(): MockObject&Reader - { - $readerMock = $this->createMock(Reader::class); - $readerMock->expects($this->exactly(0))->method('getClassAnnotations'); - $readerMock->expects($this->exactly(0))->method('getClassAnnotation'); - $readerMock->expects($this->exactly(0))->method('getMethodAnnotations'); - $readerMock->expects($this->exactly(0))->method('getMethodAnnotation'); - $readerMock->expects($this->exactly(0))->method('getPropertyAnnotations'); - $readerMock->expects($this->exactly(0))->method('getPropertyAnnotation'); - - return $readerMock; - } -} diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 2e291157a..bca02cbf2 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -557,10 +557,7 @@ protected static function getBundleDefaultConfig() ], ], 'annotations' => [ - 'cache' => 'php_array', - 'file_cache_dir' => '%kernel.cache_dir%/annotations', - 'debug' => true, - 'enabled' => true, + 'enabled' => false, ], 'serializer' => [ 'default_context' => [], diff --git a/Tests/DependencyInjection/Fixtures/php/legacy_annotations.php b/Tests/DependencyInjection/Fixtures/php/legacy_annotations.php deleted file mode 100644 index a939b34f1..000000000 --- a/Tests/DependencyInjection/Fixtures/php/legacy_annotations.php +++ /dev/null @@ -1,10 +0,0 @@ -loadFromExtension('framework', [ - 'annotations' => [ - 'cache' => 'file', - 'debug' => true, - 'file_cache_dir' => '%kernel.cache_dir%/annotations', - ], - 'http_method_override' => false, -]); diff --git a/Tests/DependencyInjection/Fixtures/php/validation_legacy_annotations.php b/Tests/DependencyInjection/Fixtures/php/validation_legacy_annotations.php deleted file mode 100644 index 48b281e65..000000000 --- a/Tests/DependencyInjection/Fixtures/php/validation_legacy_annotations.php +++ /dev/null @@ -1,15 +0,0 @@ -loadFromExtension('framework', [ - 'annotations' => [ - 'enabled' => true, - ], - 'http_method_override' => false, - 'secret' => 's3cr3t', - 'validation' => [ - 'enabled' => true, - 'enable_annotations' => true, - ], -]); - -$container->setAlias('validator.alias', 'validator')->setPublic(true); diff --git a/Tests/DependencyInjection/Fixtures/xml/legacy_annotations.xml b/Tests/DependencyInjection/Fixtures/xml/legacy_annotations.xml deleted file mode 100644 index 57229dad6..000000000 --- a/Tests/DependencyInjection/Fixtures/xml/legacy_annotations.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - diff --git a/Tests/DependencyInjection/Fixtures/xml/validation_legacy_annotations.xml b/Tests/DependencyInjection/Fixtures/xml/validation_legacy_annotations.xml deleted file mode 100644 index 150473a56..000000000 --- a/Tests/DependencyInjection/Fixtures/xml/validation_legacy_annotations.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - diff --git a/Tests/DependencyInjection/Fixtures/yml/legacy_annotations.yml b/Tests/DependencyInjection/Fixtures/yml/legacy_annotations.yml deleted file mode 100644 index 0d34e986c..000000000 --- a/Tests/DependencyInjection/Fixtures/yml/legacy_annotations.yml +++ /dev/null @@ -1,7 +0,0 @@ -framework: - annotations: - enabled: true - cache: file - debug: true - file_cache_dir: '%kernel.cache_dir%/annotations' - http_method_override: true diff --git a/Tests/DependencyInjection/Fixtures/yml/validation_legacy_annotations.yml b/Tests/DependencyInjection/Fixtures/yml/validation_legacy_annotations.yml deleted file mode 100644 index afee3fe15..000000000 --- a/Tests/DependencyInjection/Fixtures/yml/validation_legacy_annotations.yml +++ /dev/null @@ -1,13 +0,0 @@ -framework: - annotations: - enabled: true - http_method_override: false - secret: s3cr3t - validation: - enabled: true - enable_annotations: true - -services: - validator.alias: - alias: validator - public: true diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index da4468c2e..12ae8ccef 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -35,7 +35,6 @@ use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ChildDefinition; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -94,7 +93,7 @@ abstract class FrameworkExtensionTestCase extends TestCase { use ExpectDeprecationTrait; - private static $containerCache = []; + private static array $containerCache = []; abstract protected function loadFromFile(ContainerBuilder $container, $file); @@ -1223,19 +1222,16 @@ public function testValidationService() $this->assertInstanceOf(ValidatorInterface::class, $container->get('validator.alias')); } - /** - * @group legacy - */ public function testAnnotations() { - $this->expectDeprecation('Since symfony/framework-bundle 6.4: Enabling the integration of Doctrine annotations is deprecated. Set the "framework.annotations.enabled" config option to false.'); - - $container = $this->createContainerFromFile('legacy_annotations', [], true, false); - $container->addCompilerPass(new TestAnnotationsPass()); - $container->compile(); + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('Invalid configuration for path "framework.annotations": Enabling the doctrine/annotations integration is not supported anymore.'); - $this->assertEquals($container->getParameter('kernel.cache_dir').'/annotations', $container->getDefinition('annotations.filesystem_cache_adapter')->getArgument(2)); - $this->assertSame('annotations.filesystem_cache_adapter', (string) $container->getDefinition('annotation_reader')->getArgument(1)); + $this->createContainerFromClosure(function (ContainerBuilder $container) { + $container->loadFromExtension('framework', [ + 'annotations' => true, + ]); + }); } public function testFileLinkFormat() @@ -1264,28 +1260,6 @@ public function testValidationAnnotations() // no cache this time } - /** - * @group legacy - */ - public function testValidationLegacyAnnotations() - { - $this->expectDeprecation('Since symfony/framework-bundle 6.4: Enabling the integration of Doctrine annotations is deprecated. Set the "framework.annotations.enabled" config option to false.'); - - $container = $this->createContainerFromFile('validation_legacy_annotations'); - - $calls = $container->getDefinition('validator.builder')->getMethodCalls(); - - $this->assertCount(8, $calls); - $this->assertSame('enableAnnotationMapping', $calls[4][0]); - $this->assertSame('setDoctrineAnnotationReader', $calls[5][0]); - $this->assertEquals([new Reference('annotation_reader')], $calls[5][1]); - $this->assertSame('addMethodMapping', $calls[6][0]); - $this->assertSame(['loadValidatorMetadata'], $calls[6][1]); - $this->assertSame('setMappingCache', $calls[7][0]); - $this->assertEquals([new Reference('validator.mapping.cache.adapter')], $calls[7][1]); - // no cache this time - } - public function testValidationPaths() { require_once __DIR__.'/Fixtures/TestBundle/TestBundle.php'; @@ -2317,7 +2291,7 @@ protected function createContainer(array $data = []) ], $data))); } - protected function createContainerFromFile(string $file, array $data = [], bool $resetCompilerPasses = true, bool $compile = true, FrameworkExtension $extension = null) + protected function createContainerFromFile(string $file, array $data = [], bool $resetCompilerPasses = true, bool $compile = true, FrameworkExtension $extension = null): ContainerBuilder { $cacheKey = md5(static::class.$file.serialize($data)); if ($compile && isset(self::$containerCache[$cacheKey])) { @@ -2344,7 +2318,7 @@ protected function createContainerFromFile(string $file, array $data = [], bool return self::$containerCache[$cacheKey] = $container; } - protected function createContainerFromClosure($closure, $data = []) + protected function createContainerFromClosure($closure, $data = []): ContainerBuilder { $container = $this->createContainer($data); $container->registerExtension(new FrameworkExtension()); @@ -2416,15 +2390,3 @@ private function assertCachePoolServiceDefinitionIsCreated(ContainerBuilder $con }; } } - -/** - * Simulates ReplaceAliasByActualDefinitionPass. - */ -class TestAnnotationsPass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container): void - { - $container->setDefinition('annotation_reader', $container->getDefinition('annotations.cached_reader')); - $container->removeDefinition('annotations.cached_reader'); - } -} diff --git a/Tests/Functional/AutowiringTypesTest.php b/Tests/Functional/AutowiringTypesTest.php index 95f99ef2c..28ce69f93 100644 --- a/Tests/Functional/AutowiringTypesTest.php +++ b/Tests/Functional/AutowiringTypesTest.php @@ -11,9 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; -use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Annotations\PsrCachedReader; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher; @@ -21,34 +18,6 @@ class AutowiringTypesTest extends AbstractWebTestCase { - use ExpectDeprecationTrait; - - /** - * @group legacy - */ - public function testAnnotationReaderAutowiring() - { - $this->expectDeprecation('Since symfony/framework-bundle 6.4: Enabling the integration of Doctrine annotations is deprecated. Set the "framework.annotations.enabled" config option to false.'); - - static::bootKernel(['root_config' => 'no_annotations_cache.yml', 'environment' => 'no_annotations_cache']); - - $annotationReader = self::getContainer()->get('test.autowiring_types.autowired_services')->getAnnotationReader(); - $this->assertInstanceOf(AnnotationReader::class, $annotationReader); - } - - /** - * @group legacy - */ - public function testCachedAnnotationReaderAutowiring() - { - $this->expectDeprecation('Since symfony/framework-bundle 6.4: Enabling the integration of Doctrine annotations is deprecated. Set the "framework.annotations.enabled" config option to false.'); - - static::bootKernel(['root_config' => 'with_annotations.yml', 'environment' => 'with_annotations']); - - $annotationReader = self::getContainer()->get('test.autowiring_types.autowired_services')->getAnnotationReader(); - $this->assertInstanceOf(PsrCachedReader::class, $annotationReader); - } - public function testEventDispatcherAutowiring() { static::bootKernel(['debug' => false]); diff --git a/Tests/Functional/Bundle/TestBundle/AutowiringTypes/AutowiredServices.php b/Tests/Functional/Bundle/TestBundle/AutowiringTypes/AutowiredServices.php index 4743460e6..c5a5decce 100644 --- a/Tests/Functional/Bundle/TestBundle/AutowiringTypes/AutowiredServices.php +++ b/Tests/Functional/Bundle/TestBundle/AutowiringTypes/AutowiredServices.php @@ -11,26 +11,15 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\AutowiringTypes; -use Doctrine\Common\Annotations\Reader; use Psr\Cache\CacheItemPoolInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; class AutowiredServices { - private $annotationReader; - private $dispatcher; - private $cachePool; - - public function __construct(Reader $annotationReader = null, EventDispatcherInterface $dispatcher, CacheItemPoolInterface $cachePool) - { - $this->annotationReader = $annotationReader; - $this->dispatcher = $dispatcher; - $this->cachePool = $cachePool; - } - - public function getAnnotationReader() - { - return $this->annotationReader; + public function __construct( + private readonly EventDispatcherInterface $dispatcher, + private readonly CacheItemPoolInterface $cachePool, + ) { } public function getDispatcher() diff --git a/Tests/Functional/app/AutowiringTypes/no_annotations_cache.yml b/Tests/Functional/app/AutowiringTypes/no_annotations_cache.yml deleted file mode 100644 index fa78d7200..000000000 --- a/Tests/Functional/app/AutowiringTypes/no_annotations_cache.yml +++ /dev/null @@ -1,8 +0,0 @@ -imports: - - { resource: config.yml } - -framework: - http_method_override: false - annotations: - enabled: true - cache: none diff --git a/Tests/Functional/app/AutowiringTypes/with_annotations.yml b/Tests/Functional/app/AutowiringTypes/with_annotations.yml deleted file mode 100644 index cb87d7f4e..000000000 --- a/Tests/Functional/app/AutowiringTypes/with_annotations.yml +++ /dev/null @@ -1,6 +0,0 @@ -imports: - - { resource: config.yml } - -framework: - annotations: - enabled: true diff --git a/composer.json b/composer.json index 722a4c5f1..bf371b6e8 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,6 @@ "symfony/routing": "^6.4|^7.0" }, "require-dev": { - "doctrine/annotations": "^1.13.1|^2", "doctrine/persistence": "^1.3|^2|^3", "symfony/asset": "^6.4|^7.0", "symfony/asset-mapper": "^6.4|^7.0", @@ -73,7 +72,6 @@ "twig/twig": "^2.10|^3.0" }, "conflict": { - "doctrine/annotations": "<1.13.1", "doctrine/persistence": "<1.3", "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", From d615ca1a435e425bede38ab5fe28c07bd11696ca Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 24 Jul 2023 14:01:38 +0200 Subject: [PATCH 012/281] Fix merge --- DependencyInjection/FrameworkExtension.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index c948a1daf..6b49ac7a7 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -140,7 +140,6 @@ use Symfony\Component\RateLimiter\Storage\CacheStorage; use Symfony\Component\RemoteEvent\Attribute\AsRemoteEventConsumer; use Symfony\Component\RemoteEvent\RemoteEvent; -use Symfony\Component\Routing\Loader\AnnotationClassLoader; use Symfony\Component\Routing\Loader\Psr4DirectoryLoader; use Symfony\Component\Scheduler\Attribute\AsSchedule; use Symfony\Component\Scheduler\Messenger\SchedulerTransportFactory; @@ -1169,13 +1168,6 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co if (!class_exists(Psr4DirectoryLoader::class)) { $container->removeDefinition('routing.loader.psr4'); } - - if ($this->isInitializedConfigEnabled('annotations') && (new \ReflectionClass(AnnotationClassLoader::class))->hasProperty('reader')) { - $container->getDefinition('routing.loader.annotation')->setArguments([ - new Reference('annotation_reader'), - '%kernel.environment%', - ]); - } } private function registerSessionConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void From dbe315a278a2f16058c277202ed9dcc86afcbdb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Mon, 24 Jul 2023 17:21:55 +0200 Subject: [PATCH 013/281] [FrameworkBundle][Workflow] Remove dead code --- Command/WorkflowDumpCommand.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Command/WorkflowDumpCommand.php b/Command/WorkflowDumpCommand.php index f69a0e11e..d732305d4 100644 --- a/Command/WorkflowDumpCommand.php +++ b/Command/WorkflowDumpCommand.php @@ -81,10 +81,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $type = $workflow instanceof StateMachine ? 'state_machine' : 'workflow'; $definition = $workflow->getDefinition(); - if (null === $definition) { - throw new InvalidArgumentException(sprintf('No service found for "workflow.%1$s" nor "state_machine.%1$s".', $workflowName)); - } - switch ($input->getOption('dump-format')) { case 'puml': $transitionType = 'workflow' === $type ? PlantUmlDumper::WORKFLOW_TRANSITION : PlantUmlDumper::STATEMACHINE_TRANSITION; From 4137cb3fcf10cefafade6339cc34fe749d2563a1 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Fri, 21 Jul 2023 16:36:29 +0200 Subject: [PATCH 014/281] [Messenger] Remove deprecated `StopWorkerOnSignalsListener` --- DependencyInjection/FrameworkExtension.php | 7 ------- Resources/config/messenger.php | 10 ---------- 2 files changed, 17 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 2d3b0cf10..2c8cd891b 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -108,7 +108,6 @@ use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Messenger\Bridge as MessengerBridge; use Symfony\Component\Messenger\Command\StatsCommand; -use Symfony\Component\Messenger\EventListener\StopWorkerOnSignalsListener; use Symfony\Component\Messenger\Handler\BatchHandlerInterface; use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\MessageBusInterface; @@ -2035,12 +2034,6 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder $container->getDefinition('messenger.transport.beanstalkd.factory')->addTag('messenger.transport_factory'); } - if (!class_exists(StopWorkerOnSignalsListener::class)) { - $container->removeDefinition('messenger.listener.stop_worker_signals_listener'); - } elseif ($config['stop_worker_on_signals']) { - $container->getDefinition('messenger.listener.stop_worker_signals_listener')->replaceArgument(0, $config['stop_worker_on_signals']); - } - if (null === $config['default_bus'] && 1 === \count($config['buses'])) { $config['default_bus'] = key($config['buses']); } diff --git a/Resources/config/messenger.php b/Resources/config/messenger.php index 56b50d78d..556affd07 100644 --- a/Resources/config/messenger.php +++ b/Resources/config/messenger.php @@ -23,7 +23,6 @@ use Symfony\Component\Messenger\EventListener\SendFailedMessageToFailureTransportListener; use Symfony\Component\Messenger\EventListener\StopWorkerOnCustomStopExceptionListener; use Symfony\Component\Messenger\EventListener\StopWorkerOnRestartSignalListener; -use Symfony\Component\Messenger\EventListener\StopWorkerOnSignalsListener; use Symfony\Component\Messenger\Handler\RedispatchMessageHandler; use Symfony\Component\Messenger\Middleware\AddBusNameStampMiddleware; use Symfony\Component\Messenger\Middleware\DispatchAfterCurrentBusMiddleware; @@ -201,15 +200,6 @@ ->tag('kernel.event_subscriber') ->tag('monolog.logger', ['channel' => 'messenger']) - ->set('messenger.listener.stop_worker_signals_listener', StopWorkerOnSignalsListener::class) - ->deprecate('6.4', 'symfony/messenger', 'The "%service_id%" service is deprecated, use the "Symfony\Component\Console\Command\SignalableCommandInterface" instead.') - ->args([ - null, - service('logger')->ignoreOnInvalid(), - ]) - ->tag('kernel.event_subscriber') - ->tag('monolog.logger', ['channel' => 'messenger']) - ->set('messenger.listener.stop_worker_on_stop_exception_listener', StopWorkerOnCustomStopExceptionListener::class) ->tag('kernel.event_subscriber') From d27d89c588d63a097409abc9c089a9b4d719654d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 21 Jul 2023 15:36:26 +0200 Subject: [PATCH 015/281] Add types to public and protected properties --- Controller/AbstractController.php | 5 +---- HttpCache/HttpCache.php | 4 ++-- Secrets/AbstractVault.php | 2 +- Test/KernelTestCase.php | 11 +++-------- Translation/Translator.php | 7 +++---- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/Controller/AbstractController.php b/Controller/AbstractController.php index 3149f1ae8..6cdfb3d8a 100644 --- a/Controller/AbstractController.php +++ b/Controller/AbstractController.php @@ -55,10 +55,7 @@ */ abstract class AbstractController implements ServiceSubscriberInterface { - /** - * @var ContainerInterface - */ - protected $container; + protected ContainerInterface $container; #[Required] public function setContainer(ContainerInterface $container): ?ContainerInterface diff --git a/HttpCache/HttpCache.php b/HttpCache/HttpCache.php index f312a7afe..ab8cae6b7 100644 --- a/HttpCache/HttpCache.php +++ b/HttpCache/HttpCache.php @@ -27,8 +27,8 @@ */ class HttpCache extends BaseHttpCache { - protected $cacheDir; - protected $kernel; + protected ?string $cacheDir = null; + protected KernelInterface $kernel; private ?StoreInterface $store = null; private ?SurrogateInterface $surrogate; diff --git a/Secrets/AbstractVault.php b/Secrets/AbstractVault.php index ac2078f6d..374964a06 100644 --- a/Secrets/AbstractVault.php +++ b/Secrets/AbstractVault.php @@ -16,7 +16,7 @@ */ abstract class AbstractVault { - protected $lastMessage; + protected ?string $lastMessage = null; public function getLastMessage(): ?string { diff --git a/Test/KernelTestCase.php b/Test/KernelTestCase.php index e5ef9112c..d14c4dde8 100644 --- a/Test/KernelTestCase.php +++ b/Test/KernelTestCase.php @@ -27,14 +27,9 @@ abstract class KernelTestCase extends TestCase use MailerAssertionsTrait; use NotificationAssertionsTrait; - protected static $class; - - /** - * @var KernelInterface - */ - protected static $kernel; - - protected static $booted = false; + protected static ?string $class = null; + protected static ?KernelInterface $kernel = null; + protected static bool $booted = false; protected function tearDown(): void { diff --git a/Translation/Translator.php b/Translation/Translator.php index 3cce60ec1..de1f4f925 100644 --- a/Translation/Translator.php +++ b/Translation/Translator.php @@ -24,10 +24,9 @@ */ class Translator extends BaseTranslator implements WarmableInterface { - protected $container; - protected $loaderIds; - - protected $options = [ + protected ContainerInterface $container; + protected array $loaderIds; + protected array $options = [ 'cache_dir' => null, 'debug' => false, 'resource_files' => [], From f36e094719c045a8ad3cfa0ec47b4912a5c796fe Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 5 Aug 2023 16:46:07 +0200 Subject: [PATCH 016/281] [MonologBridge][FrameworkBundle] Remove recently deprecated symbols --- CHANGELOG.md | 2 + .../Compiler/AddDebugLogProcessorPass.php | 12 ----- .../Compiler/EnableLoggerDebugModePass.php | 44 ------------------- 3 files changed, 2 insertions(+), 56 deletions(-) delete mode 100644 DependencyInjection/Compiler/EnableLoggerDebugModePass.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a233efb4e..aaf735d49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ CHANGELOG `Symfony\Component\Serializer\Normalizer\NormalizerInterface` or implement `NormalizerAwareInterface` instead * Remove the `Http\Client\HttpClient` service, use `Psr\Http\Client\ClientInterface` instead * Remove the integration of Doctrine annotations, use native attributes instead + * Remove `EnableLoggerDebugModePass`, use argument `$debug` of HttpKernel's `Logger` instead + * Remove `AddDebugLogProcessorPass::configureLogger()`, use HttpKernel's `DebugLoggerConfigurator` instead 6.4 --- diff --git a/DependencyInjection/Compiler/AddDebugLogProcessorPass.php b/DependencyInjection/Compiler/AddDebugLogProcessorPass.php index 68a1e51b0..1efdcb87f 100644 --- a/DependencyInjection/Compiler/AddDebugLogProcessorPass.php +++ b/DependencyInjection/Compiler/AddDebugLogProcessorPass.php @@ -32,16 +32,4 @@ public function process(ContainerBuilder $container): void $container->getDefinition('monolog.logger_prototype') ->setConfigurator([new Reference('debug.debug_logger_configurator'), 'pushDebugLogger']); } - - /** - * @deprecated since Symfony 6.4, use HttpKernel's DebugLoggerConfigurator instead - */ - public static function configureLogger(mixed $logger): void - { - trigger_deprecation('symfony/framework-bundle', '6.4', 'The "%s()" method is deprecated, use HttpKernel\'s DebugLoggerConfigurator instead.', __METHOD__); - - if (\is_object($logger) && method_exists($logger, 'removeDebugLogger') && \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { - $logger->removeDebugLogger(); - } - } } diff --git a/DependencyInjection/Compiler/EnableLoggerDebugModePass.php b/DependencyInjection/Compiler/EnableLoggerDebugModePass.php deleted file mode 100644 index c1a5e444f..000000000 --- a/DependencyInjection/Compiler/EnableLoggerDebugModePass.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -trigger_deprecation('symfony/framework-bundle', '6.4', 'The "%s" class is deprecated, use argument $debug of HttpKernel\'s Logger instead.', EnableLoggerDebugModePass::class); - -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\HttpKernel\Log\Logger; - -/** - * @deprecated since Symfony 6.4, use argument $debug of HttpKernel's Logger instead - */ -final class EnableLoggerDebugModePass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container): void - { - if (!$container->hasDefinition('profiler') || !$container->hasDefinition('logger')) { - return; - } - - $loggerDefinition = $container->getDefinition('logger'); - - if (Logger::class === $loggerDefinition->getClass()) { - $loggerDefinition->setConfigurator([__CLASS__, 'configureLogger']); - } - } - - public static function configureLogger(Logger $logger): void - { - if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { - $logger->enableDebug(); - } - } -} From c855b63bf32c0f0c98ecead79d552832901ba560 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 18 Aug 2023 16:58:37 +0200 Subject: [PATCH 017/281] [FrameworkBundle] Remove config deprecations --- CHANGELOG.md | 8 ++ DependencyInjection/Configuration.php | 100 +++--------------- DependencyInjection/FrameworkExtension.php | 8 +- .../DependencyInjection/ConfigurationTest.php | 10 +- 4 files changed, 32 insertions(+), 94 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad847a244..03cf42722 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,14 @@ CHANGELOG * Remove the integration of Doctrine annotations, use native attributes instead * Remove `EnableLoggerDebugModePass`, use argument `$debug` of HttpKernel's `Logger` instead * Remove `AddDebugLogProcessorPass::configureLogger()`, use HttpKernel's `DebugLoggerConfigurator` instead + * Make the `framework.handle_all_throwables` config option default to `true` + * Make the `framework.php_errors.log` config option default to `true` + * Make the `framework.session.cookie_secure` config option default to `auto` + * Make the `framework.session.cookie_samesite` config option default to `lax` + * Make the `framework.session.handler_id` default to null if `save_path` is not set and to `session.handler.native_file` otherwise + * Make the `framework.uid.default_uuid_version` config option default to `7` + * Make the `framework.uid.time_based_uuid_version` config option default to `7` + * Make the `framework.validation.email_validation_mode` config option default to `html5` 6.4 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 4782cb70a..72460206b 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -82,15 +82,6 @@ public function getConfigTreeBuilder(): TreeBuilder return $v; }) ->end() - ->validate() - ->always(function ($v) { - if (!isset($v['handle_all_throwables'])) { - trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting the "framework.handle_all_throwables" config option is deprecated. It will default to "true" in 7.0.'); - } - - return $v; - }) - ->end() ->fixXmlConfig('enabled_locale') ->children() ->scalarNode('secret')->end() @@ -137,7 +128,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->scalarNode('error_controller') ->defaultValue('error_controller') ->end() - ->booleanNode('handle_all_throwables')->info('HttpKernel will handle all kinds of \Throwable')->end() + ->booleanNode('handle_all_throwables')->info('HttpKernel will handle all kinds of \Throwable')->defaultTrue()->end() ->end() ; @@ -649,38 +640,15 @@ private function addRouterSection(ArrayNodeDefinition $rootNode): void private function addSessionSection(ArrayNodeDefinition $rootNode): void { $rootNode - ->validate() - ->always(function (array $v): array { - if ($v['session']['enabled']) { - if (!\array_key_exists('cookie_secure', $v['session'])) { - trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting the "framework.session.cookie_secure" config option is deprecated. It will default to "auto" in 7.0.'); - } - - if (!\array_key_exists('cookie_samesite', $v['session'])) { - trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting the "framework.session.cookie_samesite" config option is deprecated. It will default to "lax" in 7.0.'); - } - - if (!\array_key_exists('handler_id', $v['session']) && !\array_key_exists('handler_id', $v['save_path'])) { - trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting either "framework.session.handler_id" or "save_path" config options is deprecated; "handler_id" will default to null in 7.0 if "save_path" is not set and to "session.handler.native_file" otherwise.'); - } - } - - $v['session'] += [ - 'cookie_samesite' => null, - 'handler_id' => 'session.handler.native_file', - 'save_path' => '%kernel.cache_dir%/sessions', - ]; - - return $v; - }) - ->end() ->children() ->arrayNode('session') ->info('session configuration') ->canBeEnabled() ->children() ->scalarNode('storage_factory_id')->defaultValue('session.storage.factory.native')->end() - ->scalarNode('handler_id')->end() + ->scalarNode('handler_id') + ->info('Defaults to using the native session handler, or to the native *file* session handler if "save_path" is not null.') + ->end() ->scalarNode('name') ->validate() ->ifTrue(function ($v) { @@ -694,14 +662,16 @@ private function addSessionSection(ArrayNodeDefinition $rootNode): void ->scalarNode('cookie_lifetime')->end() ->scalarNode('cookie_path')->end() ->scalarNode('cookie_domain')->end() - ->enumNode('cookie_secure')->values([true, false, 'auto'])->end() + ->enumNode('cookie_secure')->values([true, false, 'auto'])->defaultValue('auto')->end() ->booleanNode('cookie_httponly')->defaultTrue()->end() - ->enumNode('cookie_samesite')->values([null, Cookie::SAMESITE_LAX, Cookie::SAMESITE_STRICT, Cookie::SAMESITE_NONE])->end() + ->enumNode('cookie_samesite')->values([null, Cookie::SAMESITE_LAX, Cookie::SAMESITE_STRICT, Cookie::SAMESITE_NONE])->defaultValue('lax')->end() ->booleanNode('use_cookies')->end() ->scalarNode('gc_divisor')->end() ->scalarNode('gc_probability')->defaultValue(1)->end() ->scalarNode('gc_maxlifetime')->end() - ->scalarNode('save_path')->end() + ->scalarNode('save_path') + ->info('Defaults to "%kernel.cache_dir%/sessions" if the "handler_id" option is not null') + ->end() ->integerNode('metadata_update_threshold') ->defaultValue(0) ->info('seconds to wait between 2 session metadata updates') @@ -1013,25 +983,6 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e private function addValidationSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone): void { $rootNode - ->validate() - ->always(function ($v) { - if ($v['validation']['enabled'] && !\array_key_exists('email_validation_mode', $v['validation'])) { - trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting the "framework.validation.email_validation_mode" config option is deprecated. It will default to "html5" in 7.0.'); - } - - if (isset($v['enable_annotations'])) { - trigger_deprecation('symfony/framework-bundle', '6.4', 'Option "enable_annotations" at "framework.validation" is deprecated. Use the "enable_attributes" option instead.'); - - if (!isset($v['enable_attributes'])) { - $v['enable_attributes'] = $v['enable_annotations']; - } else { - throw new LogicException('The "enable_annotations" and "enable_attributes" options at path "framework.validation" must not be both set. Only the "enable_attributes" option must be used.'); - } - } - - return $v; - }) - ->end() ->children() ->arrayNode('validation') ->info('validation configuration') @@ -1047,7 +998,7 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e ->validate()->castToArray()->end() ->end() ->scalarNode('translation_domain')->defaultValue('validators')->end() - ->enumNode('email_validation_mode')->values(['html5', 'loose', 'strict'])->end() + ->enumNode('email_validation_mode')->values(['html5', 'loose', 'strict'])->defaultValue('html5')->end() ->arrayNode('mapping') ->addDefaultsIfNotSet() ->fixXmlConfig('path') @@ -1312,17 +1263,6 @@ private function addCacheSection(ArrayNodeDefinition $rootNode, callable $willBe private function addPhpErrorsSection(ArrayNodeDefinition $rootNode): void { $rootNode - ->validate() - ->always(function (array $v): array { - if (!\array_key_exists('log', $v['php_errors'])) { - trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting the "framework.php_errors.log" config option is deprecated. It will default to "true" in 7.0.'); - - $v['php_errors']['log'] = $this->debug; - } - - return $v; - }) - ->end() ->children() ->arrayNode('php_errors') ->info('PHP errors handling configuration') @@ -1332,6 +1272,7 @@ private function addPhpErrorsSection(ArrayNodeDefinition $rootNode): void ->info('Use the application logger instead of the PHP logger for logging PHP errors.') ->example('"true" to use the default configuration: log all errors. "false" to disable. An integer bit field of E_* constants, or an array mapping E_* constants to log levels.') ->treatNullLike($this->debug) + ->defaultTrue() ->beforeNormalization() ->ifArray() ->then(function (array $v): array { @@ -2344,23 +2285,6 @@ private function addRateLimiterSection(ArrayNodeDefinition $rootNode, callable $ private function addUidSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone): void { $rootNode - ->validate() - ->always(function ($v) { - if ($v['uid']['enabled']) { - if (!\array_key_exists('default_uuid_version', $v['uid'])) { - trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting the "framework.uid.default_uuid_version" config option is deprecated. It will default to "7" in 7.0.'); - } - - if (!\array_key_exists('time_based_uuid_version', $v['uid'])) { - trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting the "framework.uid.time_based_uuid_version" config option is deprecated. It will default to "7" in 7.0.'); - } - } - - $v['uid'] += ['default_uuid_version' => 6, 'time_based_uuid_version' => 6]; - - return $v; - }) - ->end() ->children() ->arrayNode('uid') ->info('Uid configuration') @@ -2369,6 +2293,7 @@ private function addUidSection(ArrayNodeDefinition $rootNode, callable $enableIf ->children() ->enumNode('default_uuid_version') ->values([7, 6, 4, 1]) + ->defaultValue(7) ->end() ->enumNode('name_based_uuid_version') ->defaultValue(5) @@ -2379,6 +2304,7 @@ private function addUidSection(ArrayNodeDefinition $rootNode, callable $enableIf ->end() ->enumNode('time_based_uuid_version') ->values([7, 6, 1]) + ->defaultValue(7) ->end() ->scalarNode('time_based_uuid_node') ->cannotBeEmpty() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 57cf7d4c6..e3101a664 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -181,7 +181,6 @@ use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader; use Symfony\Component\Validator\ObjectInitializerInterface; use Symfony\Component\Validator\Validation; -use Symfony\Component\Validator\ValidatorBuilder; use Symfony\Component\Webhook\Controller\WebhookController; use Symfony\Component\WebLink\HttpHeaderSerializer; use Symfony\Component\Workflow; @@ -1232,10 +1231,15 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c $container->setParameter('session.storage.options', $options); // session handler (the internal callback registered with PHP session management) - if (null === $config['handler_id']) { + if (null === ($config['handler_id'] ?? $config['save_path'] ?? null)) { $config['save_path'] = null; $container->setAlias('session.handler', 'session.handler.native'); } else { + $config['handler_id'] ??= 'session.handler.native_file'; + + if (!\array_key_exists('save_path', $config)) { + $config['save_path'] = '%kernel.cache_dir%/sessions'; + } $container->resolveEnvPlaceholders($config['handler_id'], null, $usedEnvs); if ($usedEnvs || preg_match('#^[a-z]++://#', $config['handler_id'])) { diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 2865a406b..c6c4b398a 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -602,6 +602,7 @@ protected static function getBundleDefaultConfig() 'enabled' => true, 'endpoint' => null, ], + 'email_validation_mode' => 'html5', ], 'annotations' => [ 'enabled' => false, @@ -635,11 +636,10 @@ protected static function getBundleDefaultConfig() 'session' => [ 'enabled' => false, 'storage_factory_id' => 'session.storage.factory.native', - 'handler_id' => 'session.handler.native_file', 'cookie_httponly' => true, - 'cookie_samesite' => null, + 'cookie_samesite' => 'lax', + 'cookie_secure' => 'auto', 'gc_probability' => 1, - 'save_path' => '%kernel.cache_dir%/sessions', 'metadata_update_threshold' => 0, ], 'request' => [ @@ -762,9 +762,9 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor ], 'uid' => [ 'enabled' => !class_exists(FullStack::class) && class_exists(UuidFactory::class), - 'default_uuid_version' => 6, + 'default_uuid_version' => 7, 'name_based_uuid_version' => 5, - 'time_based_uuid_version' => 6, + 'time_based_uuid_version' => 7, ], 'html_sanitizer' => [ 'enabled' => !class_exists(FullStack::class) && class_exists(HtmlSanitizer::class), From 1817356a523d93a1881a5f819525e5a92a23bbd7 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 22 Aug 2023 15:45:13 +0200 Subject: [PATCH 018/281] [FrameworkBundle][Validator] Remove remaining deprecations --- CHANGELOG.md | 2 ++ DependencyInjection/Configuration.php | 16 ---------------- Resources/config/schema/symfony-1.0.xsd | 2 -- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00d71e31e..8117ffe6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ CHANGELOG * Make the `framework.uid.default_uuid_version` config option default to `7` * Make the `framework.uid.time_based_uuid_version` config option default to `7` * Make the `framework.validation.email_validation_mode` config option default to `html5` + * Remove the `framework.validation.enable_annotations` config option, use `framework.validation.enable_attributes` instead + * Remove the `framework.serializer.enable_annotations` config option, use `framework.serializer.enable_attributes` instead 6.4 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 72460206b..a728efc2f 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -989,7 +989,6 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e ->{$enableIfStandalone('symfony/validator', Validation::class)}() ->children() ->scalarNode('cache')->end() - ->booleanNode('enable_annotations')->end() ->booleanNode('enable_attributes')->{!class_exists(FullStack::class) ? 'defaultTrue' : 'defaultFalse'}()->end() ->arrayNode('static_method') ->defaultValue(['loadValidatorMetadata']) @@ -1090,24 +1089,9 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $e $rootNode ->children() ->arrayNode('serializer') - ->validate() - ->always(function ($v) { - if (isset($v['enable_annotations'])) { - trigger_deprecation('symfony/framework-bundle', '6.4', 'Option "enable_annotations" at "framework.serializer" is deprecated. Use the "enable_attributes" option instead.'); - - if (!isset($v['enable_attributes'])) { - $v['enable_attributes'] = $v['enable_annotations']; - } else { - throw new LogicException('The "enable_annotations" and "enable_attributes" options at path "framework.serializer" must not be both set. Only the "enable_attributes" option must be used.'); - } - } - - return $v; - })->end() ->info('serializer configuration') ->{$enableIfStandalone('symfony/serializer', Serializer::class)}() ->children() - ->booleanNode('enable_annotations')->end() ->booleanNode('enable_attributes')->{!class_exists(FullStack::class) ? 'defaultTrue' : 'defaultFalse'}()->end() ->scalarNode('name_converter')->end() ->scalarNode('circular_reference_handler')->end() diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index 001b36d8e..c3f2f9670 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -266,7 +266,6 @@ - @@ -320,7 +319,6 @@ - From dbca7742f24bf0b0109c17f92eba9a87e880b1bf Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 24 Aug 2023 17:26:29 +0200 Subject: [PATCH 019/281] [FrameworkBundle] Remove compat code --- DependencyInjection/FrameworkExtension.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index e3101a664..9c9a8af29 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -1851,12 +1851,6 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->removeDefinition('serializer.normalizer.translatable'); } - // compat with Symfony < 6.3 - if (!is_subclass_of(ProblemNormalizer::class, SerializerAwareInterface::class)) { - $container->getDefinition('serializer.normalizer.problem') - ->setArguments(['%kernel.debug%']); - } - $serializerLoaders = []; if (isset($config['enable_attributes']) && $config['enable_attributes']) { if ($container->getParameter('kernel.debug')) { From 50d420b5cba7b4b9de2f1653213dc6456e0d4cba Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sun, 3 Sep 2023 16:53:03 +0200 Subject: [PATCH 020/281] [FrameworkBundle] Remove references to annotation_reader --- .../AddAnnotationsCachedReaderPass.php | 43 ------------------- .../Compiler/UnusedTagsPass.php | 1 - DependencyInjection/FrameworkExtension.php | 8 +--- FrameworkBundle.php | 2 - .../FrameworkExtensionTestCase.php | 4 -- .../AnnotationReaderPass.php | 24 ----------- .../Bundle/TestBundle/TestBundle.php | 1 - 7 files changed, 1 insertion(+), 82 deletions(-) delete mode 100644 DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php delete mode 100644 Tests/Functional/Bundle/TestBundle/DependencyInjection/AnnotationReaderPass.php diff --git a/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php b/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php deleted file mode 100644 index 2105a54df..000000000 --- a/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -/** - * @internal - */ -class AddAnnotationsCachedReaderPass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container): void - { - // "annotations.cached_reader" is wired late so that any passes using - // "annotation_reader" at build time don't get any cache - foreach ($container->findTaggedServiceIds('annotations.cached_reader') as $id => $tags) { - $reader = $container->getDefinition($id); - $properties = $reader->getProperties(); - - if (isset($properties['cacheProviderBackup'])) { - $provider = $properties['cacheProviderBackup']->getValues()[0]; - unset($properties['cacheProviderBackup']); - $reader->setProperties($properties); - $reader->replaceArgument(1, $provider); - } elseif (4 <= \count($arguments = $reader->getArguments()) && $arguments[3] instanceof ServiceClosureArgument) { - $arguments[1] = $arguments[3]->getValues()[0]; - unset($arguments[3]); - $reader->setArguments($arguments); - } - } - } -} diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index c21b7369d..b0b20e1a1 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -22,7 +22,6 @@ class UnusedTagsPass implements CompilerPassInterface { private const KNOWN_TAGS = [ - 'annotations.cached_reader', 'asset_mapper.compiler', 'asset_mapper.importmap.resolver', 'assets.package', diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index e7d2f7758..1995e1ba7 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -161,10 +161,8 @@ use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -use Symfony\Component\Serializer\Normalizer\ProblemNormalizer; use Symfony\Component\Serializer\Normalizer\UnwrappingDenormalizer; use Symfony\Component\Serializer\Serializer; -use Symfony\Component\Serializer\SerializerAwareInterface; use Symfony\Component\Stopwatch\Stopwatch; use Symfony\Component\String\LazyString; use Symfony\Component\String\Slugger\SluggerInterface; @@ -715,7 +713,6 @@ public function load(array $configs, ContainerBuilder $container): void ->addTag('routing.route_loader'); $container->setParameter('container.behavior_describing_tags', [ - 'annotations.cached_reader', 'container.do_not_inline', 'container.service_locator', 'container.service_subscriber', @@ -1857,10 +1854,7 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->removeDefinition('serializer.mapping.cache_class_metadata_factory'); } - $annotationLoader = new Definition( - AnnotationLoader::class, - [new Reference('annotation_reader', ContainerInterface::NULL_ON_INVALID_REFERENCE)] - ); + $annotationLoader = new Definition(AnnotationLoader::class); $serializerLoaders[] = $annotationLoader; } diff --git a/FrameworkBundle.php b/FrameworkBundle.php index cf195c522..25c1aca2a 100644 --- a/FrameworkBundle.php +++ b/FrameworkBundle.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddAnnotationsCachedReaderPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddDebugLogProcessorPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AssetsContextPass; @@ -142,7 +141,6 @@ public function build(ContainerBuilder $container): void // but as late as possible to get resolved parameters $container->addCompilerPass($registerListenersPass, PassConfig::TYPE_BEFORE_REMOVING); $this->addCompilerPassIfExists($container, AddConstraintValidatorsPass::class); - $container->addCompilerPass(new AddAnnotationsCachedReaderPass(), PassConfig::TYPE_AFTER_REMOVING, -255); $this->addCompilerPassIfExists($container, AddValidatorInitializersPass::class); $this->addCompilerPassIfExists($container, AddConsoleCommandPass::class, PassConfig::TYPE_BEFORE_REMOVING); // must be registered as late as possible to get access to all Twig paths registered in diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 0e08e29f6..665112998 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -14,7 +14,6 @@ use Psr\Cache\CacheItemPoolInterface; use Psr\Log\LoggerAwareInterface; use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddAnnotationsCachedReaderPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DummyMessage; @@ -81,7 +80,6 @@ use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass; use Symfony\Component\Validator\Validation; use Symfony\Component\Validator\Validator\ValidatorInterface; -use Symfony\Component\Validator\ValidatorBuilder; use Symfony\Component\Webhook\Client\RequestParser; use Symfony\Component\Webhook\Controller\WebhookController; use Symfony\Component\Workflow; @@ -2048,7 +2046,6 @@ public function testRegisterParameterCollectingBehaviorDescribingTags() $this->assertTrue($container->hasParameter('container.behavior_describing_tags')); $this->assertEquals([ - 'annotations.cached_reader', 'container.do_not_inline', 'container.service_locator', 'container.service_subscriber', @@ -2321,7 +2318,6 @@ protected function createContainerFromFile(string $file, array $data = [], bool } $container->getCompilerPassConfig()->setBeforeOptimizationPasses([new LoggerPass()]); $container->getCompilerPassConfig()->setBeforeRemovingPasses([new AddConstraintValidatorsPass(), new TranslatorPass()]); - $container->getCompilerPassConfig()->setAfterRemovingPasses([new AddAnnotationsCachedReaderPass()]); if (!$compile) { return $container; diff --git a/Tests/Functional/Bundle/TestBundle/DependencyInjection/AnnotationReaderPass.php b/Tests/Functional/Bundle/TestBundle/DependencyInjection/AnnotationReaderPass.php deleted file mode 100644 index 9e61c5ae7..000000000 --- a/Tests/Functional/Bundle/TestBundle/DependencyInjection/AnnotationReaderPass.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection; - -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class AnnotationReaderPass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container): void - { - // simulate using "annotation_reader" in a compiler pass - $container->get('test.annotation_reader'); - } -} diff --git a/Tests/Functional/Bundle/TestBundle/TestBundle.php b/Tests/Functional/Bundle/TestBundle/TestBundle.php index 7dbbb096b..d0c6588b0 100644 --- a/Tests/Functional/Bundle/TestBundle/TestBundle.php +++ b/Tests/Functional/Bundle/TestBundle/TestBundle.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle; -use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\AnnotationReaderPass; use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\Config\CustomConfig; use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass; use Symfony\Component\DependencyInjection\Compiler\PassConfig; From cc44af547af0c86fdf0983f10b1e9f67ae4899a2 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 11 Sep 2023 14:26:06 +0200 Subject: [PATCH 021/281] Drop support for Twig 2 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 75cd73795..818f4aa50 100644 --- a/composer.json +++ b/composer.json @@ -70,7 +70,7 @@ "symfony/uid": "^6.4|^7.0", "symfony/web-link": "^6.4|^7.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "twig/twig": "^2.10|^3.0" + "twig/twig": "^3.0.4" }, "conflict": { "doctrine/persistence": "<1.3", From 4633b156b60cac478f6e40db3128c98b25144fa4 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Thu, 5 Oct 2023 16:34:22 +0200 Subject: [PATCH 022/281] [FrameworkBundle] Remove ContainerAware leftover --- Tests/Fixtures/ContainerAwareController.php | 38 --------------------- 1 file changed, 38 deletions(-) delete mode 100644 Tests/Fixtures/ContainerAwareController.php diff --git a/Tests/Fixtures/ContainerAwareController.php b/Tests/Fixtures/ContainerAwareController.php deleted file mode 100644 index fa1be2069..000000000 --- a/Tests/Fixtures/ContainerAwareController.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures; - -use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; - -class ContainerAwareController implements ContainerAwareInterface -{ - private ?ContainerInterface $container = null; - - public function setContainer(?ContainerInterface $container): void - { - $this->container = $container; - } - - public function getContainer(): ?ContainerInterface - { - return $this->container; - } - - public function testAction() - { - } - - public function __invoke() - { - } -} From 6fd189fb38c7a8460bf7600aa5290bb97abd2590 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Sat, 14 Oct 2023 14:52:14 +0200 Subject: [PATCH 023/281] [FrameworkBundle] Remove deprecation layer for `KernelBrowser::loginUser()` --- KernelBrowser.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/KernelBrowser.php b/KernelBrowser.php index cf6df7009..0c1374633 100644 --- a/KernelBrowser.php +++ b/KernelBrowser.php @@ -100,10 +100,8 @@ public function enableReboot(): void * * @return $this */ - public function loginUser(object $user, string $firewallContext = 'main'/* , array $tokenAttributes = [] */): static + public function loginUser(object $user, string $firewallContext = 'main', array $tokenAttributes = []): static { - $tokenAttributes = 2 < \func_num_args() ? func_get_arg(2) : []; - if (!interface_exists(UserInterface::class)) { throw new \LogicException(sprintf('"%s" requires symfony/security-core to be installed. Try running "composer require symfony/security-core".', __METHOD__)); } From 4639f4f3f408218811d5fa7c801e5c7a9506a3f2 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 17 Oct 2023 16:30:56 +0200 Subject: [PATCH 024/281] [Serializer] Fix Serializer deprecations merge --- DependencyInjection/FrameworkExtension.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index cfb3b756b..507e26107 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -1858,9 +1858,9 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $serializerLoaders = []; if (isset($config['enable_attributes']) && $config['enable_attributes']) { - $annotationLoader = new Definition(AttributeLoader::class); + $attributeLoader = new Definition(AttributeLoader::class); - $serializerLoaders[] = $annotationLoader; + $serializerLoaders[] = $attributeLoader; } $fileRecorder = function ($extension, $path) use (&$serializerLoaders) { From 0590b338671130b345f63139390c83e405588eb2 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 18 Oct 2023 11:51:14 +0200 Subject: [PATCH 025/281] [FrameworkBundle][Routing] Remove remaining deprecations --- CHANGELOG.md | 4 ++++ Resources/config/routing.php | 9 --------- Routing/AttributeRouteControllerLoader.php | 8 +------- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c71b9e56e..997d79422 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,10 @@ CHANGELOG * Make the `framework.validation.email_validation_mode` config option default to `html5` * Remove the `framework.validation.enable_annotations` config option, use `framework.validation.enable_attributes` instead * Remove the `framework.serializer.enable_annotations` config option, use `framework.serializer.enable_attributes` instead + * Remove the `routing.loader.annotation` service, use the `routing.loader.attribute` service instead + * Remove the `routing.loader.annotation.directory` service, use the `routing.loader.attribute.directory` service instead + * Remove the `routing.loader.annotation.file` service, use the `routing.loader.attribute.file` service instead + * Remove `AnnotatedRouteControllerLoader`, use `AttributeRouteControllerLoader` instead 6.4 --- diff --git a/Resources/config/routing.php b/Resources/config/routing.php index 5fc0cbb3e..8cdbbf33a 100644 --- a/Resources/config/routing.php +++ b/Resources/config/routing.php @@ -98,9 +98,6 @@ ]) ->tag('routing.loader', ['priority' => -10]) - ->alias('routing.loader.annotation', 'routing.loader.attribute') - ->deprecate('symfony/routing', '6.4', 'The "%alias_id%" service is deprecated, use the "routing.loader.attribute" service instead.') - ->set('routing.loader.attribute.directory', AttributeDirectoryLoader::class) ->args([ service('file_locator'), @@ -108,9 +105,6 @@ ]) ->tag('routing.loader', ['priority' => -10]) - ->alias('routing.loader.annotation.directory', 'routing.loader.attribute.directory') - ->deprecate('symfony/routing', '6.4', 'The "%alias_id%" service is deprecated, use the "routing.loader.attribute.directory" service instead.') - ->set('routing.loader.attribute.file', AttributeFileLoader::class) ->args([ service('file_locator'), @@ -118,9 +112,6 @@ ]) ->tag('routing.loader', ['priority' => -10]) - ->alias('routing.loader.annotation.file', 'routing.loader.attribute.file') - ->deprecate('symfony/routing', '6.4', 'The "%alias_id%" service is deprecated, use the "routing.loader.attribute.file" service instead.') - ->set('routing.loader.psr4', Psr4DirectoryLoader::class) ->args([ service('file_locator'), diff --git a/Routing/AttributeRouteControllerLoader.php b/Routing/AttributeRouteControllerLoader.php index a629f4387..7e43e1af5 100644 --- a/Routing/AttributeRouteControllerLoader.php +++ b/Routing/AttributeRouteControllerLoader.php @@ -25,10 +25,8 @@ class AttributeRouteControllerLoader extends AttributeClassLoader { /** * Configures the _controller default parameter of a given Route instance. - * - * @return void */ - protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot) + protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot): void { if ('__invoke' === $method->getName()) { $route->setDefault('_controller', $class->getName()); @@ -51,7 +49,3 @@ protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMetho return str_replace('__', '_', $name); } } - -if (!class_exists(AnnotatedRouteControllerLoader::class, false)) { - class_alias(AttributeRouteControllerLoader::class, AnnotatedRouteControllerLoader::class); -} From 1edaef6271e26f799ab71d3eda0295ef21304f1f Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 18 Oct 2023 13:21:23 +0200 Subject: [PATCH 026/281] [FrameworkBundle] Fix workflow tests --- Tests/DependencyInjection/FrameworkExtensionTestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 6e94638e0..ec939b4b8 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -505,7 +505,7 @@ public function testWorkflowServicesCanBeEnabled() { $container = $this->createContainerFromFile('workflows_enabled'); - $this->assertTrue($container->hasDefinition('.workflow.registry')); + $this->assertTrue($container->hasDefinition('workflow.registry')); $this->assertTrue($container->hasDefinition('console.command.workflow_dump')); } From bfad70a13278b54286d997de1b9502d21f313937 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 18 Oct 2023 14:57:55 +0200 Subject: [PATCH 027/281] [7.0] Cleanup legacy code paths --- CacheWarmer/SerializerCacheWarmer.php | 3 --- CacheWarmer/ValidatorCacheWarmer.php | 3 --- DependencyInjection/Configuration.php | 3 --- DependencyInjection/FrameworkExtension.php | 8 -------- FrameworkBundle.php | 7 +------ Resources/config/cache.php | 5 ----- Resources/config/services.php | 3 --- Tests/DependencyInjection/FrameworkExtensionTestCase.php | 3 --- Tests/Test/WebTestCaseTest.php | 9 --------- 9 files changed, 1 insertion(+), 43 deletions(-) diff --git a/CacheWarmer/SerializerCacheWarmer.php b/CacheWarmer/SerializerCacheWarmer.php index 1fd257d27..b1300516b 100644 --- a/CacheWarmer/SerializerCacheWarmer.php +++ b/CacheWarmer/SerializerCacheWarmer.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; -use Doctrine\Common\Annotations\AnnotationException; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; @@ -51,8 +50,6 @@ protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter, string foreach ($loader->getMappedClasses() as $mappedClass) { try { $metadataFactory->getMetadataFor($mappedClass); - } catch (AnnotationException) { - // ignore failing annotations } catch (\Exception $e) { $this->ignoreAutoloadException($mappedClass, $e); } diff --git a/CacheWarmer/ValidatorCacheWarmer.php b/CacheWarmer/ValidatorCacheWarmer.php index e37b0196a..d88fd36c8 100644 --- a/CacheWarmer/ValidatorCacheWarmer.php +++ b/CacheWarmer/ValidatorCacheWarmer.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; -use Doctrine\Common\Annotations\AnnotationException; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\PhpArrayAdapter; use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; @@ -50,8 +49,6 @@ protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter, string if ($metadataFactory->hasMetadataFor($mappedClass)) { $metadataFactory->getMetadataFor($mappedClass); } - } catch (AnnotationException) { - // ignore failing annotations } catch (\Exception $e) { $this->ignoreAutoloadException($mappedClass, $e); } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index df58d7392..d5a09481c 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -899,9 +899,6 @@ private function addAssetMapperSection(ArrayNodeDefinition $rootNode, callable $ ->info('The directory to store JavaScript vendors.') ->defaultValue('%kernel.project_dir%/assets/vendor') ->end() - ->scalarNode('provider') - ->setDeprecated('symfony/framework-bundle', '6.4', 'Option "%node%" at "%path%" is deprecated and does nothing. Remove it.') - ->end() ->end() ->end() ->end() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 574f40c45..b126a2faf 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -564,14 +564,6 @@ public function load(array $configs, ContainerBuilder $container): void $this->registerHtmlSanitizerConfiguration($config['html_sanitizer'], $container, $loader); } - $this->addAnnotatedClassesToCompile([ - '**\\Controller\\', - '**\\Entity\\', - - // Added explicitly so that we don't rely on the class map being dumped to make it work - AbstractController::class, - ]); - if (ContainerBuilder::willBeAvailable('symfony/mime', MimeTypes::class, ['symfony/framework-bundle'])) { $loader->load('mime_type.php'); } diff --git a/FrameworkBundle.php b/FrameworkBundle.php index 3da66702c..701443723 100644 --- a/FrameworkBundle.php +++ b/FrameworkBundle.php @@ -95,12 +95,7 @@ public function boot(): void $handler = ErrorHandler::register(null, false); - // When upgrading an existing Symfony application from 6.2 to 6.3, and - // the cache is warmed up, the service is not available yet, so we need - // to check if it exists. - if ($this->container->has('debug.error_handler_configurator')) { - $this->container->get('debug.error_handler_configurator')->configure($handler); - } + $this->container->get('debug.error_handler_configurator')->configure($handler); if ($this->container->getParameter('kernel.http_method_override')) { Request::enableHttpMethodParameterOverride(); diff --git a/Resources/config/cache.php b/Resources/config/cache.php index 87207cf95..eceb9bdfd 100644 --- a/Resources/config/cache.php +++ b/Resources/config/cache.php @@ -56,11 +56,6 @@ ->private() ->tag('cache.pool') - ->set('cache.annotations') - ->parent('cache.system') - ->private() - ->tag('cache.pool') - ->set('cache.property_info') ->parent('cache.system') ->private() diff --git a/Resources/config/services.php b/Resources/config/services.php index 905e16f9b..c85ccf5d0 100644 --- a/Resources/config/services.php +++ b/Resources/config/services.php @@ -48,7 +48,6 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\KernelInterface; -use Symfony\Component\HttpKernel\UriSigner as HttpKernelUriSigner; use Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner; use Symfony\Component\Runtime\Runner\Symfony\ResponseRunner; use Symfony\Component\Runtime\SymfonyRuntime; @@ -158,8 +157,6 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : [] param('kernel.secret'), ]) ->alias(UriSigner::class, 'uri_signer') - ->alias(HttpKernelUriSigner::class, 'uri_signer') - ->deprecate('symfony/framework-bundle', '6.4', 'The "%alias_id%" alias is deprecated, use "'.UriSigner::class.'" instead.') ->set('config_cache_factory', ResourceCheckerConfigCacheFactory::class) ->args([ diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index ec939b4b8..575ed5666 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -13,7 +13,6 @@ use Psr\Cache\CacheItemPoolInterface; use Psr\Log\LoggerAwareInterface; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DummyMessage; @@ -91,8 +90,6 @@ abstract class FrameworkExtensionTestCase extends TestCase { - use ExpectDeprecationTrait; - private static array $containerCache = []; abstract protected function loadFromFile(ContainerBuilder $container, $file); diff --git a/Tests/Test/WebTestCaseTest.php b/Tests/Test/WebTestCaseTest.php index d9f597044..54a8044b0 100644 --- a/Tests/Test/WebTestCaseTest.php +++ b/Tests/Test/WebTestCaseTest.php @@ -23,7 +23,6 @@ use Symfony\Component\HttpFoundation\Cookie as HttpFoundationCookie; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\Test\Constraint\ResponseHeaderLocationSame; class WebTestCaseTest extends TestCase { @@ -62,10 +61,6 @@ public function testAssertResponseRedirectsWithLocation() public function testAssertResponseRedirectsWithLocationWithoutHost() { - if (!class_exists(ResponseHeaderLocationSame::class)) { - $this->markTestSkipped('Requires symfony/http-foundation 6.3 or higher.'); - } - $this->getResponseTester(new Response('', 301, ['Location' => 'https://example.com/']))->assertResponseRedirects('/'); $this->expectException(AssertionFailedError::class); $this->expectExceptionMessage('is redirected and has header "Location" matching "/".'); @@ -74,10 +69,6 @@ public function testAssertResponseRedirectsWithLocationWithoutHost() public function testAssertResponseRedirectsWithLocationWithoutScheme() { - if (!class_exists(ResponseHeaderLocationSame::class)) { - $this->markTestSkipped('Requires symfony/http-foundation 6.3 or higher.'); - } - $this->getResponseTester(new Response('', 301, ['Location' => 'https://example.com/']))->assertResponseRedirects('//example.com/'); $this->expectException(AssertionFailedError::class); $this->expectExceptionMessage('is redirected and has header "Location" matching "//example.com/".'); From 84b4769da2bac6ec14c44ee8fe98d7ea409fb1a6 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Thu, 19 Oct 2023 18:37:23 +0200 Subject: [PATCH 028/281] [FrameworkBundle] Remove deprecated compiler passes --- CHANGELOG.md | 4 + .../AddExpressionLanguageProvidersPass.php | 39 ------ .../Compiler/DataCollectorTranslatorPass.php | 40 ------ .../Compiler/LoggingTranslatorPass.php | 58 -------- .../Compiler/WorkflowGuardListenerPass.php | 49 ------- ...AddExpressionLanguageProvidersPassTest.php | 63 --------- .../DataCollectorTranslatorPassTest.php | 124 ------------------ .../Compiler/LoggingTranslatorPassTest.php | 85 ------------ .../WorkflowGuardListenerPassTest.php | 106 --------------- 9 files changed, 4 insertions(+), 564 deletions(-) delete mode 100644 DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php delete mode 100644 DependencyInjection/Compiler/DataCollectorTranslatorPass.php delete mode 100644 DependencyInjection/Compiler/LoggingTranslatorPass.php delete mode 100644 DependencyInjection/Compiler/WorkflowGuardListenerPass.php delete mode 100644 Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php delete mode 100644 Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php delete mode 100644 Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php delete mode 100644 Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a4ea10516..ed3b53d14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,10 @@ CHANGELOG * Remove the `routing.loader.annotation.directory` service, use the `routing.loader.attribute.directory` service instead * Remove the `routing.loader.annotation.file` service, use the `routing.loader.attribute.file` service instead * Remove `AnnotatedRouteControllerLoader`, use `AttributeRouteControllerLoader` instead + * Remove `AddExpressionLanguageProvidersPass`, use `Symfony\Component\Routing\DependencyInjection\AddExpressionLanguageProvidersPass` instead + * Remove `DataCollectorTranslatorPass`, use `Symfony\Component\Translation\DependencyInjection\DataCollectorTranslatorPass` instead + * Remove `LoggingTranslatorPass`, use `Symfony\Component\Translation\DependencyInjection\LoggingTranslatorPass` instead + * Remove `WorkflowGuardListenerPass`, use `Symfony\Component\Workflow\DependencyInjection\WorkflowGuardListenerPass` instead 6.4 --- diff --git a/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php b/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php deleted file mode 100644 index cc8871b6b..000000000 --- a/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; - -trigger_deprecation('symfony/framework-bundle', '6.4', 'The "%s" class is deprecated, use "%s" instead.', AddExpressionLanguageProvidersPass::class, \Symfony\Component\Routing\DependencyInjection\AddExpressionLanguageProvidersPass::class); - -/** - * Registers the expression language providers. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 6.4, use Symfony\Component\Routing\DependencyInjection\AddExpressionLanguageProvidersPass instead. - */ -class AddExpressionLanguageProvidersPass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container): void - { - // routing - if ($container->has('router.default')) { - $definition = $container->findDefinition('router.default'); - foreach ($container->findTaggedServiceIds('routing.expression_language_provider', true) as $id => $attributes) { - $definition->addMethodCall('addExpressionLanguageProvider', [new Reference($id)]); - } - } - } -} diff --git a/DependencyInjection/Compiler/DataCollectorTranslatorPass.php b/DependencyInjection/Compiler/DataCollectorTranslatorPass.php deleted file mode 100644 index f87dcc277..000000000 --- a/DependencyInjection/Compiler/DataCollectorTranslatorPass.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\Translation\TranslatorBagInterface; - -trigger_deprecation('symfony/framework-bundle', '6.4', 'The "%s" class is deprecated, use "%s" instead.', DataCollectorTranslatorPass::class, \Symfony\Component\Translation\DependencyInjection\DataCollectorTranslatorPass::class); - -/** - * @author Christian Flothmann - * - * @deprecated since Symfony 6.4, use Symfony\Component\Translation\DependencyInjection\DataCollectorTranslatorPass instead. - */ -class DataCollectorTranslatorPass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container): void - { - if (!$container->has('translator')) { - return; - } - - $translatorClass = $container->getParameterBag()->resolveValue($container->findDefinition('translator')->getClass()); - - if (!is_subclass_of($translatorClass, TranslatorBagInterface::class)) { - $container->removeDefinition('translator.data_collector'); - $container->removeDefinition('data_collector.translation'); - } - } -} diff --git a/DependencyInjection/Compiler/LoggingTranslatorPass.php b/DependencyInjection/Compiler/LoggingTranslatorPass.php deleted file mode 100644 index c88643132..000000000 --- a/DependencyInjection/Compiler/LoggingTranslatorPass.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; -use Symfony\Component\Translation\TranslatorBagInterface; -use Symfony\Contracts\Translation\TranslatorInterface; - -trigger_deprecation('symfony/framework-bundle', '6.4', 'The "%s" class is deprecated, use "%s" instead.', LoggingTranslatorPass::class, \Symfony\Component\Translation\DependencyInjection\LoggingTranslatorPass::class); - -/** - * @author Abdellatif Ait boudad - * - * @deprecated since Symfony 6.4, use Symfony\Component\Translation\DependencyInjection\LoggingTranslatorPass instead. - */ -class LoggingTranslatorPass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container): void - { - if (!$container->hasAlias('logger') || !$container->hasAlias('translator')) { - return; - } - - if ($container->hasParameter('translator.logging') && $container->getParameter('translator.logging')) { - $translatorAlias = $container->getAlias('translator'); - $definition = $container->getDefinition((string) $translatorAlias); - $class = $container->getParameterBag()->resolveValue($definition->getClass()); - - if (!$r = $container->getReflectionClass($class)) { - throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $translatorAlias)); - } - if ($r->isSubclassOf(TranslatorInterface::class) && $r->isSubclassOf(TranslatorBagInterface::class)) { - $container->getDefinition('translator.logging')->setDecoratedService('translator'); - $warmer = $container->getDefinition('translation.warmer'); - $subscriberAttributes = $warmer->getTag('container.service_subscriber'); - $warmer->clearTag('container.service_subscriber'); - - foreach ($subscriberAttributes as $k => $v) { - if ((!isset($v['id']) || 'translator' !== $v['id']) && (!isset($v['key']) || 'translator' !== $v['key'])) { - $warmer->addTag('container.service_subscriber', $v); - } - } - $warmer->addTag('container.service_subscriber', ['key' => 'translator', 'id' => 'translator.logging.inner']); - } - } - } -} diff --git a/DependencyInjection/Compiler/WorkflowGuardListenerPass.php b/DependencyInjection/Compiler/WorkflowGuardListenerPass.php deleted file mode 100644 index e66d9503d..000000000 --- a/DependencyInjection/Compiler/WorkflowGuardListenerPass.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Exception\LogicException; - -trigger_deprecation('symfony/framework-bundle', '6.4', 'The "%s" class is deprecated, use "%s" instead.', WorkflowGuardListenerPass::class, \Symfony\Component\Workflow\DependencyInjection\WorkflowGuardListenerPass::class); - -/** - * @author Christian Flothmann - * @author Grégoire Pineau - * - * @deprecated since Symfony 6.4, use Symfony\Component\Workflow\DependencyInjection\WorkflowGuardListenerPass instead. - */ -class WorkflowGuardListenerPass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container): void - { - if (!$container->hasParameter('workflow.has_guard_listeners')) { - return; - } - - $container->getParameterBag()->remove('workflow.has_guard_listeners'); - - $servicesNeeded = [ - 'security.token_storage', - 'security.authorization_checker', - 'security.authentication.trust_resolver', - 'security.role_hierarchy', - ]; - - foreach ($servicesNeeded as $service) { - if (!$container->has($service)) { - throw new LogicException(sprintf('The "%s" service is needed to be able to use the workflow guard listener.', $service)); - } - } - } -} diff --git a/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php b/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php deleted file mode 100644 index d159057c6..000000000 --- a/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -/** - * @group legacy - */ -class AddExpressionLanguageProvidersPassTest extends TestCase -{ - public function testProcessForRouter() - { - $container = new ContainerBuilder(); - $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); - - $definition = new Definition(\stdClass::class); - $definition->addTag('routing.expression_language_provider'); - $container->setDefinition('some_routing_provider', $definition->setPublic(true)); - - $container->register('router.default', \stdClass::class)->setPublic(true); - $container->compile(); - - $router = $container->getDefinition('router.default'); - $calls = $router->getMethodCalls(); - $this->assertCount(1, $calls); - $this->assertEquals('addExpressionLanguageProvider', $calls[0][0]); - $this->assertEquals(new Reference('some_routing_provider'), $calls[0][1][0]); - } - - public function testProcessForRouterAlias() - { - $container = new ContainerBuilder(); - $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); - - $definition = new Definition(\stdClass::class); - $definition->addTag('routing.expression_language_provider'); - $container->setDefinition('some_routing_provider', $definition->setPublic(true)); - - $container->register('my_router', \stdClass::class)->setPublic(true); - $container->setAlias('router.default', 'my_router'); - $container->compile(); - - $router = $container->getDefinition('my_router'); - $calls = $router->getMethodCalls(); - $this->assertCount(1, $calls); - $this->assertEquals('addExpressionLanguageProvider', $calls[0][0]); - $this->assertEquals(new Reference('some_routing_provider'), $calls[0][1][0]); - } -} diff --git a/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php b/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php deleted file mode 100644 index 805f2ca84..000000000 --- a/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php +++ /dev/null @@ -1,124 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\DataCollectorTranslatorPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\Translation\DataCollector\TranslationDataCollector; -use Symfony\Component\Translation\DataCollectorTranslator; -use Symfony\Component\Translation\Translator; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @group legacy - */ -class DataCollectorTranslatorPassTest extends TestCase -{ - private ContainerBuilder $container; - private DataCollectorTranslatorPass $dataCollectorTranslatorPass; - - protected function setUp(): void - { - $this->container = new ContainerBuilder(); - $this->dataCollectorTranslatorPass = new DataCollectorTranslatorPass(); - - $this->container->setParameter('translator_implementing_bag', Translator::class); - $this->container->setParameter('translator_not_implementing_bag', 'Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\TranslatorWithTranslatorBag'); - - $this->container->register('translator.data_collector', DataCollectorTranslator::class) - ->setDecoratedService('translator') - ->setArguments([new Reference('translator.data_collector.inner')]) - ; - - $this->container->register('data_collector.translation', TranslationDataCollector::class) - ->setArguments([new Reference('translator.data_collector')]) - ; - } - - /** - * @dataProvider getImplementingTranslatorBagInterfaceTranslatorClassNames - */ - public function testProcessKeepsDataCollectorTranslatorIfItImplementsTranslatorBagInterface($class) - { - $this->container->register('translator', $class); - - $this->dataCollectorTranslatorPass->process($this->container); - - $this->assertTrue($this->container->hasDefinition('translator.data_collector')); - } - - /** - * @dataProvider getImplementingTranslatorBagInterfaceTranslatorClassNames - */ - public function testProcessKeepsDataCollectorIfTranslatorImplementsTranslatorBagInterface($class) - { - $this->container->register('translator', $class); - - $this->dataCollectorTranslatorPass->process($this->container); - - $this->assertTrue($this->container->hasDefinition('data_collector.translation')); - } - - public static function getImplementingTranslatorBagInterfaceTranslatorClassNames() - { - return [ - [Translator::class], - ['%translator_implementing_bag%'], - ]; - } - - /** - * @dataProvider getNotImplementingTranslatorBagInterfaceTranslatorClassNames - */ - public function testProcessRemovesDataCollectorTranslatorIfItDoesNotImplementTranslatorBagInterface($class) - { - $this->container->register('translator', $class); - - $this->dataCollectorTranslatorPass->process($this->container); - - $this->assertFalse($this->container->hasDefinition('translator.data_collector')); - } - - /** - * @dataProvider getNotImplementingTranslatorBagInterfaceTranslatorClassNames - */ - public function testProcessRemovesDataCollectorIfTranslatorDoesNotImplementTranslatorBagInterface($class) - { - $this->container->register('translator', $class); - - $this->dataCollectorTranslatorPass->process($this->container); - - $this->assertFalse($this->container->hasDefinition('data_collector.translation')); - } - - public static function getNotImplementingTranslatorBagInterfaceTranslatorClassNames() - { - return [ - ['Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\TranslatorWithTranslatorBag'], - ['%translator_not_implementing_bag%'], - ]; - } -} - -class TranslatorWithTranslatorBag implements TranslatorInterface -{ - public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string - { - } - - public function getLocale(): string - { - return 'en'; - } -} diff --git a/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php b/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php deleted file mode 100644 index e15c62207..000000000 --- a/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggingTranslatorPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\Translation\Translator; - -/** - * @group legacy - */ -class LoggingTranslatorPassTest extends TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - $container->setParameter('translator.logging', true); - $container->setParameter('translator.class', Translator::class); - $container->register('monolog.logger'); - $container->setAlias('logger', 'monolog.logger'); - $container->register('translator.default', '%translator.class%'); - $container->register('translator.logging', '%translator.class%'); - $container->setAlias('translator', 'translator.default'); - $translationWarmerDefinition = $container->register('translation.warmer') - ->addArgument(new Reference('translator')) - ->addTag('container.service_subscriber', ['id' => 'translator']) - ->addTag('container.service_subscriber', ['id' => 'foo']); - - $pass = new LoggingTranslatorPass(); - $pass->process($container); - - $this->assertEquals( - ['container.service_subscriber' => [ - ['id' => 'foo'], - ['key' => 'translator', 'id' => 'translator.logging.inner'], - ]], - $translationWarmerDefinition->getTags() - ); - } - - public function testThatCompilerPassIsIgnoredIfThereIsNotLoggerDefinition() - { - $container = new ContainerBuilder(); - $container->register('identity_translator'); - $container->setAlias('translator', 'identity_translator'); - - $definitionsBefore = \count($container->getDefinitions()); - $aliasesBefore = \count($container->getAliases()); - - $pass = new LoggingTranslatorPass(); - $pass->process($container); - - // the container is untouched (i.e. no new definitions or aliases) - $this->assertCount($definitionsBefore, $container->getDefinitions()); - $this->assertCount($aliasesBefore, $container->getAliases()); - } - - public function testThatCompilerPassIsIgnoredIfThereIsNotTranslatorDefinition() - { - $container = new ContainerBuilder(); - $container->register('monolog.logger'); - $container->setAlias('logger', 'monolog.logger'); - - $definitionsBefore = \count($container->getDefinitions()); - $aliasesBefore = \count($container->getAliases()); - - $pass = new LoggingTranslatorPass(); - $pass->process($container); - - // the container is untouched (i.e. no new definitions or aliases) - $this->assertCount($definitionsBefore, $container->getDefinitions()); - $this->assertCount($aliasesBefore, $container->getAliases()); - } -} diff --git a/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php b/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php deleted file mode 100644 index 4c3327847..000000000 --- a/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php +++ /dev/null @@ -1,106 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; - -use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\WorkflowGuardListenerPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Exception\LogicException; -use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; -use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; -use Symfony\Component\Security\Core\Role\RoleHierarchy; -use Symfony\Component\Validator\Validator\ValidatorInterface; - -/** - * @group legacy - */ -class WorkflowGuardListenerPassTest extends TestCase -{ - private ContainerBuilder $container; - private WorkflowGuardListenerPass $compilerPass; - - protected function setUp(): void - { - $this->container = new ContainerBuilder(); - $this->compilerPass = new WorkflowGuardListenerPass(); - } - - public function testNoExeptionIfParameterIsNotSet() - { - $this->compilerPass->process($this->container); - - $this->assertFalse($this->container->hasParameter('workflow.has_guard_listeners')); - } - - public function testNoExeptionIfAllDependenciesArePresent() - { - $this->container->setParameter('workflow.has_guard_listeners', true); - $this->container->register('security.token_storage', TokenStorageInterface::class); - $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); - $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); - $this->container->register('security.role_hierarchy', RoleHierarchy::class); - $this->container->register('validator', ValidatorInterface::class); - - $this->compilerPass->process($this->container); - - $this->assertFalse($this->container->hasParameter('workflow.has_guard_listeners')); - } - - public function testExceptionIfTheTokenStorageServiceIsNotPresent() - { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The "security.token_storage" service is needed to be able to use the workflow guard listener.'); - $this->container->setParameter('workflow.has_guard_listeners', true); - $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); - $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); - $this->container->register('security.role_hierarchy', RoleHierarchy::class); - - $this->compilerPass->process($this->container); - } - - public function testExceptionIfTheAuthorizationCheckerServiceIsNotPresent() - { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The "security.authorization_checker" service is needed to be able to use the workflow guard listener.'); - $this->container->setParameter('workflow.has_guard_listeners', true); - $this->container->register('security.token_storage', TokenStorageInterface::class); - $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); - $this->container->register('security.role_hierarchy', RoleHierarchy::class); - - $this->compilerPass->process($this->container); - } - - public function testExceptionIfTheAuthenticationTrustResolverServiceIsNotPresent() - { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The "security.authentication.trust_resolver" service is needed to be able to use the workflow guard listener.'); - $this->container->setParameter('workflow.has_guard_listeners', true); - $this->container->register('security.token_storage', TokenStorageInterface::class); - $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); - $this->container->register('security.role_hierarchy', RoleHierarchy::class); - - $this->compilerPass->process($this->container); - } - - public function testExceptionIfTheRoleHierarchyServiceIsNotPresent() - { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('The "security.role_hierarchy" service is needed to be able to use the workflow guard listener.'); - $this->container->setParameter('workflow.has_guard_listeners', true); - $this->container->register('security.token_storage', TokenStorageInterface::class); - $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); - $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); - - $this->compilerPass->process($this->container); - } -} From 760202fc4712a069c7cddf03250e00f0d5cdeb95 Mon Sep 17 00:00:00 2001 From: Rafael Villa Verde Date: Fri, 1 Dec 2023 01:03:58 -0300 Subject: [PATCH 029/281] [Mailer] Add Azure bridge --- DependencyInjection/FrameworkExtension.php | 1 + 1 file changed, 1 insertion(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 95c8d8fa1..8272d24c7 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2559,6 +2559,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co } $classToServices = [ + MailerBridge\Azure\Transport\AzureTransportFactory::class => 'mailer.transport_factory.azure', MailerBridge\Brevo\Transport\BrevoTransportFactory::class => 'mailer.transport_factory.brevo', MailerBridge\Google\Transport\GmailTransportFactory::class => 'mailer.transport_factory.gmail', MailerBridge\Infobip\Transport\InfobipTransportFactory::class => 'mailer.transport_factory.infobip', From ec947a0de683f4df726e0924569ced70ce8d1144 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 10 Dec 2023 13:04:21 +0100 Subject: [PATCH 030/281] fix registering the Azure transport factory service --- Resources/config/mailer_transports.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Resources/config/mailer_transports.php b/Resources/config/mailer_transports.php index 06c9632d8..b8f822738 100644 --- a/Resources/config/mailer_transports.php +++ b/Resources/config/mailer_transports.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesTransportFactory; +use Symfony\Component\Mailer\Bridge\Azure\Transport\AzureTransportFactory; use Symfony\Component\Mailer\Bridge\Brevo\Transport\BrevoTransportFactory; use Symfony\Component\Mailer\Bridge\Google\Transport\GmailTransportFactory; use Symfony\Component\Mailer\Bridge\Infobip\Transport\InfobipTransportFactory; @@ -44,6 +45,10 @@ ->parent('mailer.transport_factory.abstract') ->tag('mailer.transport_factory') + ->set('mailer.transport_factory.azure', AzureTransportFactory::class) + ->parent('mailer.transport_factory.abstract') + ->tag('mailer.transport_factory') + ->set('mailer.transport_factory.brevo', BrevoTransportFactory::class) ->parent('mailer.transport_factory.abstract') ->tag('mailer.transport_factory') From f224c6198d229e1750c4eed336aee0bee51b08ab Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 11 Dec 2023 08:59:31 +0100 Subject: [PATCH 031/281] allow Twig 4 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8e879fd21..e847cb1ef 100644 --- a/composer.json +++ b/composer.json @@ -71,7 +71,7 @@ "symfony/uid": "^6.4|^7.0", "symfony/web-link": "^6.4|^7.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "twig/twig": "^3.0.4" + "twig/twig": "^3.0.4|^4.0" }, "conflict": { "doctrine/persistence": "<1.3", From 47b1f997d2d7aa3ff39dc0dc8d689fed277e37ec Mon Sep 17 00:00:00 2001 From: Quentin Devos <4972091+Okhoshi@users.noreply.github.com> Date: Sat, 9 Dec 2023 11:58:51 +0100 Subject: [PATCH 032/281] [FrameworkBundle] Move Router cache directory to `kernel.build_dir` --- CHANGELOG.md | 6 +++ CacheWarmer/RouterCacheWarmer.php | 4 ++ DependencyInjection/Configuration.php | 5 ++- Routing/Router.php | 8 +++- Tests/CacheWarmer/RouterCacheWarmerTest.php | 43 +++++++++++++++---- .../DependencyInjection/ConfigurationTest.php | 2 +- 6 files changed, 55 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed3b53d14..4bde9c2f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +7.1 +--- + + * Move the Router `cache_dir` to `kernel.build_dir` + * Deprecate the `router.cache_dir` config option + 7.0 --- diff --git a/CacheWarmer/RouterCacheWarmer.php b/CacheWarmer/RouterCacheWarmer.php index 2af9a2fe8..9dfa71c2c 100644 --- a/CacheWarmer/RouterCacheWarmer.php +++ b/CacheWarmer/RouterCacheWarmer.php @@ -36,6 +36,10 @@ public function __construct(ContainerInterface $container) public function warmUp(string $cacheDir, string $buildDir = null): array { + if (!$buildDir) { + return []; + } + $router = $this->container->get('router'); if ($router instanceof WarmableInterface) { diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index a247418a9..493835892 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -613,7 +613,10 @@ private function addRouterSection(ArrayNodeDefinition $rootNode): void ->children() ->scalarNode('resource')->isRequired()->end() ->scalarNode('type')->end() - ->scalarNode('cache_dir')->defaultValue('%kernel.cache_dir%')->end() + ->scalarNode('cache_dir') + ->defaultValue('%kernel.build_dir%') + ->setDeprecated('symfony/framework-bundle', '7.1', 'Setting the "%path%.%node%" configuration option is deprecated. It will be removed in version 8.0.') + ->end() ->scalarNode('default_uri') ->info('The default URI used to generate URLs in a non-HTTP context') ->defaultNull() diff --git a/Routing/Router.php b/Routing/Router.php index d6b1d57dc..b5b7567de 100644 --- a/Routing/Router.php +++ b/Routing/Router.php @@ -82,10 +82,14 @@ public function getRouteCollection(): RouteCollection public function warmUp(string $cacheDir, string $buildDir = null): array { + if (!$buildDir) { + return []; + } + $currentDir = $this->getOption('cache_dir'); - // force cache generation - $this->setOption('cache_dir', $cacheDir); + // force cache generation in build_dir + $this->setOption('cache_dir', $buildDir); $this->getMatcher(); $this->getGenerator(); diff --git a/Tests/CacheWarmer/RouterCacheWarmerTest.php b/Tests/CacheWarmer/RouterCacheWarmerTest.php index 727b566e1..7686b139f 100644 --- a/Tests/CacheWarmer/RouterCacheWarmerTest.php +++ b/Tests/CacheWarmer/RouterCacheWarmerTest.php @@ -19,36 +19,61 @@ class RouterCacheWarmerTest extends TestCase { - public function testWarmUpWithWarmebleInterface() + public function testWarmUpWithWarmableInterfaceWithBuildDir() { $containerMock = $this->getMockBuilder(ContainerInterface::class)->onlyMethods(['get', 'has'])->getMock(); - $routerMock = $this->getMockBuilder(testRouterInterfaceWithWarmebleInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection', 'warmUp'])->getMock(); + $routerMock = $this->getMockBuilder(testRouterInterfaceWithWarmableInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection', 'warmUp'])->getMock(); $containerMock->expects($this->any())->method('get')->with('router')->willReturn($routerMock); $routerCacheWarmer = new RouterCacheWarmer($containerMock); - $routerCacheWarmer->warmUp('/tmp'); - $routerMock->expects($this->any())->method('warmUp')->with('/tmp')->willReturn([]); + $routerCacheWarmer->warmUp('/tmp/cache', '/tmp/build'); + $routerMock->expects($this->any())->method('warmUp')->with('/tmp/cache', '/tmp/build')->willReturn([]); $this->addToAssertionCount(1); } - public function testWarmUpWithoutWarmebleInterface() + public function testWarmUpWithoutWarmableInterfaceWithBuildDir() { $containerMock = $this->getMockBuilder(ContainerInterface::class)->onlyMethods(['get', 'has'])->getMock(); - $routerMock = $this->getMockBuilder(testRouterInterfaceWithoutWarmebleInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection'])->getMock(); + $routerMock = $this->getMockBuilder(testRouterInterfaceWithoutWarmableInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection'])->getMock(); $containerMock->expects($this->any())->method('get')->with('router')->willReturn($routerMock); $routerCacheWarmer = new RouterCacheWarmer($containerMock); $this->expectException(\LogicException::class); $this->expectExceptionMessage('cannot be warmed up because it does not implement "Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface"'); - $routerCacheWarmer->warmUp('/tmp'); + $routerCacheWarmer->warmUp('/tmp/cache', '/tmp/build'); + } + + public function testWarmUpWithWarmableInterfaceWithoutBuildDir() + { + $containerMock = $this->getMockBuilder(ContainerInterface::class)->onlyMethods(['get', 'has'])->getMock(); + + $routerMock = $this->getMockBuilder(testRouterInterfaceWithWarmableInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection', 'warmUp'])->getMock(); + $containerMock->expects($this->any())->method('get')->with('router')->willReturn($routerMock); + $routerCacheWarmer = new RouterCacheWarmer($containerMock); + + $preload = $routerCacheWarmer->warmUp('/tmp'); + $routerMock->expects($this->never())->method('warmUp'); + self::assertSame([], $preload); + $this->addToAssertionCount(1); + } + + public function testWarmUpWithoutWarmableInterfaceWithoutBuildDir() + { + $containerMock = $this->getMockBuilder(ContainerInterface::class)->onlyMethods(['get', 'has'])->getMock(); + + $routerMock = $this->getMockBuilder(testRouterInterfaceWithoutWarmableInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection'])->getMock(); + $containerMock->expects($this->any())->method('get')->with('router')->willReturn($routerMock); + $routerCacheWarmer = new RouterCacheWarmer($containerMock); + $preload = $routerCacheWarmer->warmUp('/tmp'); + self::assertSame([], $preload); } } -interface testRouterInterfaceWithWarmebleInterface extends RouterInterface, WarmableInterface +interface testRouterInterfaceWithWarmableInterface extends RouterInterface, WarmableInterface { } -interface testRouterInterfaceWithoutWarmebleInterface extends RouterInterface +interface testRouterInterfaceWithoutWarmableInterface extends RouterInterface { } diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 82d9b354f..d56cfa90d 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -634,7 +634,7 @@ protected static function getBundleDefaultConfig() 'https_port' => 443, 'strict_requirements' => true, 'utf8' => true, - 'cache_dir' => '%kernel.cache_dir%', + 'cache_dir' => '%kernel.build_dir%', ], 'session' => [ 'enabled' => false, From 6fd1ada231f9836b0bf711f40cf585df80818ce1 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 14 Dec 2023 11:03:37 +0100 Subject: [PATCH 033/281] Set `strict` parameter of `in_array` to true where possible --- Command/TranslationDebugCommand.php | 4 ++-- DependencyInjection/Compiler/UnusedTagsPass.php | 2 +- DependencyInjection/Configuration.php | 2 +- DependencyInjection/FrameworkExtension.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Command/TranslationDebugCommand.php b/Command/TranslationDebugCommand.php index 79a67847a..15544a90c 100644 --- a/Command/TranslationDebugCommand.php +++ b/Command/TranslationDebugCommand.php @@ -223,8 +223,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int } } - if (!\in_array(self::MESSAGE_UNUSED, $states) && $input->getOption('only-unused') - || !\in_array(self::MESSAGE_MISSING, $states) && $input->getOption('only-missing') + if (!\in_array(self::MESSAGE_UNUSED, $states, true) && $input->getOption('only-unused') + || !\in_array(self::MESSAGE_MISSING, $states, true) && $input->getOption('only-missing') ) { continue; } diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index 1d21c6b66..6f53e57f0 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -110,7 +110,7 @@ public function process(ContainerBuilder $container): void foreach ($container->findUnusedTags() as $tag) { // skip known tags - if (\in_array($tag, self::KNOWN_TAGS)) { + if (\in_array($tag, self::KNOWN_TAGS, true)) { continue; } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 493835892..31dbc6f8d 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -435,7 +435,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void if (!\is_string($value)) { return true; } - if (class_exists(WorkflowEvents::class) && !\in_array($value, WorkflowEvents::ALIASES)) { + if (class_exists(WorkflowEvents::class) && !\in_array($value, WorkflowEvents::ALIASES, true)) { return true; } } diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index d160942f6..5ec5929be 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2179,7 +2179,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder ->setArguments([$transport['dsn'], $transport['options'] + ['transport_name' => $name], new Reference($serializerId)]) ->addTag('messenger.receiver', [ 'alias' => $name, - 'is_failure_transport' => \in_array($name, $failureTransports), + 'is_failure_transport' => \in_array($name, $failureTransports, true), ] ) ; From fea90681521723d59d1b4d6d13fdfcedcec7d87d Mon Sep 17 00:00:00 2001 From: Nyholm Date: Thu, 14 Dec 2023 19:51:27 +0100 Subject: [PATCH 034/281] [Notifier] Add Bluesky notifier bridge --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_transports.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index d160942f6..c25327f3d 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2695,6 +2695,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\AllMySms\AllMySmsTransportFactory::class => 'notifier.transport_factory.all-my-sms', NotifierBridge\AmazonSns\AmazonSnsTransportFactory::class => 'notifier.transport_factory.amazon-sns', NotifierBridge\Bandwidth\BandwidthTransportFactory::class => 'notifier.transport_factory.bandwidth', + NotifierBridge\Bluesky\BlueskyTransportFactory::class => 'notifier.transport_factory.bluesky', NotifierBridge\Brevo\BrevoTransportFactory::class => 'notifier.transport_factory.brevo', NotifierBridge\Chatwork\ChatworkTransportFactory::class => 'notifier.transport_factory.chatwork', NotifierBridge\Clickatell\ClickatellTransportFactory::class => 'notifier.transport_factory.clickatell', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index 3feb1c080..f678e0588 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -22,6 +22,10 @@ ->abstract() ->args([service('event_dispatcher'), service('http_client')->ignoreOnInvalid()]) + ->set('notifier.transport_factory.bluesky', Bridge\Bluesky\BlueskyTransportFactory::class) + ->parent('notifier.transport_factory.abstract') + ->tag('chatter.transport_factory') + ->set('notifier.transport_factory.brevo', Bridge\Brevo\BrevoTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') From 59d2946cc46f0ccd1d0177491b2970cf7292a36e Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Fri, 15 Dec 2023 13:46:43 -0500 Subject: [PATCH 035/281] [RateLimiter][FrameworkBundle] add `rate_limiter` tag to rate limiter services --- CHANGELOG.md | 1 + DependencyInjection/FrameworkExtension.php | 3 ++- .../PhpFrameworkExtensionTest.php | 20 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bde9c2f4..ed156fda3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Move the Router `cache_dir` to `kernel.build_dir` * Deprecate the `router.cache_dir` config option + * Add `rate_limiter` tags to rate limiter services 7.0 --- diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 8776bcb9c..d8e061ad1 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2860,7 +2860,8 @@ private function registerRateLimiterConfiguration(array $config, ContainerBuilde // default configuration (when used by other DI extensions) $limiterConfig += ['lock_factory' => 'lock.factory', 'cache_pool' => 'cache.rate_limiter']; - $limiter = $container->setDefinition($limiterId = 'limiter.'.$name, new ChildDefinition('limiter')); + $limiter = $container->setDefinition($limiterId = 'limiter.'.$name, new ChildDefinition('limiter')) + ->addTag('rate_limiter', ['name' => $name]); if (null !== $limiterConfig['lock_factory']) { if (!interface_exists(LockInterface::class)) { diff --git a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index 53268ffd2..deac159b6 100644 --- a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -245,4 +245,24 @@ public function testRateLimiterLockFactory() $container->getDefinition('limiter.without_lock')->getArgument(2); } + + public function testRateLimiterIsTagged() + { + $container = $this->createContainerFromClosure(function (ContainerBuilder $container) { + $container->loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'lock' => true, + 'rate_limiter' => [ + 'first' => ['policy' => 'fixed_window', 'limit' => 10, 'interval' => '1 hour'], + 'second' => ['policy' => 'fixed_window', 'limit' => 10, 'interval' => '1 hour'], + ], + ]); + }); + + $this->assertSame('first', $container->getDefinition('limiter.first')->getTag('rate_limiter')[0]['name']); + $this->assertSame('second', $container->getDefinition('limiter.second')->getTag('rate_limiter')[0]['name']); + } } From 8e8e7f0dc022371a712022f56c0009b033bad8dd Mon Sep 17 00:00:00 2001 From: Farhad Safarov Date: Sat, 16 Dec 2023 00:21:18 +0300 Subject: [PATCH 036/281] [Notifier] Add Unifonic notifier bridge --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_transports.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 8776bcb9c..3b611c38f 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2760,6 +2760,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\TurboSms\TurboSmsTransport::class => 'notifier.transport_factory.turbo-sms', NotifierBridge\Twilio\TwilioTransportFactory::class => 'notifier.transport_factory.twilio', NotifierBridge\Twitter\TwitterTransportFactory::class => 'notifier.transport_factory.twitter', + NotifierBridge\Unifonic\UnifonicTransportFactory::class => 'notifier.transport_factory.unifonic', NotifierBridge\Vonage\VonageTransportFactory::class => 'notifier.transport_factory.vonage', NotifierBridge\Yunpian\YunpianTransportFactory::class => 'notifier.transport_factory.yunpian', NotifierBridge\Zendesk\ZendeskTransportFactory::class => 'notifier.transport_factory.zendesk', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index f678e0588..fd2952c50 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -66,6 +66,10 @@ ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') + ->set('notifier.transport_factory.unifonic', Bridge\Unifonic\UnifonicTransportFactory::class) + ->parent('notifier.transport_factory.abstract') + ->tag('texter.transport_factory') + ->set('notifier.transport_factory.all-my-sms', Bridge\AllMySms\AllMySmsTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') From d73a168baf6f9c088a16c1d646300e025bbecf1e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 8 Dec 2023 16:21:59 +0100 Subject: [PATCH 037/281] Use faster hashing algorithms when possible --- EventListener/ConsoleProfilerListener.php | 2 +- Tests/DependencyInjection/FrameworkExtensionTestCase.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/EventListener/ConsoleProfilerListener.php b/EventListener/ConsoleProfilerListener.php index d3fc38106..19f0794d4 100644 --- a/EventListener/ConsoleProfilerListener.php +++ b/EventListener/ConsoleProfilerListener.php @@ -72,7 +72,7 @@ public function initialize(ConsoleCommandEvent $event): void return; } - $request->attributes->set('_stopwatch_token', substr(hash('sha256', uniqid(mt_rand(), true)), 0, 6)); + $request->attributes->set('_stopwatch_token', substr(hash('xxh128', uniqid(mt_rand(), true)), 0, 6)); $this->stopwatch->openSection(); } diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 460c899e3..4fa2ab34c 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -1693,7 +1693,7 @@ public function testCachePoolServices() ->replaceArgument(0, $expectedSeed) ->replaceArgument(1, 12), (new ChildDefinition('cache.adapter.redis')) - ->replaceArgument(0, new Reference('.cache_connection.kYdiLgf')) + ->replaceArgument(0, new Reference('.cache_connection.U5HliuY')) ->replaceArgument(1, $expectedSeed) ->replaceArgument(2, 12), ], From c4ea5cf72130cf02e19a803fa9511ac3c9bc2ee1 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 18 Dec 2023 15:39:48 +0100 Subject: [PATCH 038/281] fix merge --- Tests/DependencyInjection/FrameworkExtensionTestCase.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 4fa2ab34c..4bd3c481f 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -40,6 +40,7 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -1693,7 +1694,7 @@ public function testCachePoolServices() ->replaceArgument(0, $expectedSeed) ->replaceArgument(1, 12), (new ChildDefinition('cache.adapter.redis')) - ->replaceArgument(0, new Reference('.cache_connection.U5HliuY')) + ->replaceArgument(0, new Reference('.cache_connection.'.(\count((new \ReflectionMethod(ContainerConfigurator::class, 'extension'))->getParameters()) > 2 ? 'U5HliuY' : 'kYdiLgf'))) ->replaceArgument(1, $expectedSeed) ->replaceArgument(2, 12), ], From 6e6f3cecf335ff50b26a5fbc0d22c09a5edf6591 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 18 Dec 2023 08:46:12 +0100 Subject: [PATCH 039/281] Code updates --- Console/Descriptor/JsonDescriptor.php | 2 +- Console/Descriptor/XmlDescriptor.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Console/Descriptor/JsonDescriptor.php b/Console/Descriptor/JsonDescriptor.php index 35c86c584..f3c70310e 100644 --- a/Console/Descriptor/JsonDescriptor.php +++ b/Console/Descriptor/JsonDescriptor.php @@ -323,7 +323,7 @@ private function getContainerAliasData(Alias $alias): array private function getEventDispatcherListenersData(EventDispatcherInterface $eventDispatcher, array $options): array { $data = []; - $event = \array_key_exists('event', $options) ? $options['event'] : null; + $event = $options['event'] ?? null; if (null !== $event) { foreach ($eventDispatcher->getListeners($event) as $listener) { diff --git a/Console/Descriptor/XmlDescriptor.php b/Console/Descriptor/XmlDescriptor.php index 069dcf09f..d530936d7 100644 --- a/Console/Descriptor/XmlDescriptor.php +++ b/Console/Descriptor/XmlDescriptor.php @@ -501,7 +501,7 @@ private function getContainerParameterDocument(mixed $parameter, ?array $depreca private function getEventDispatcherListenersDocument(EventDispatcherInterface $eventDispatcher, array $options): \DOMDocument { - $event = \array_key_exists('event', $options) ? $options['event'] : null; + $event = $options['event'] ?? null; $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($eventDispatcherXML = $dom->createElement('event-dispatcher')); From 3191f9834cd4923c9c1ec077a4a2d48e24e9daf2 Mon Sep 17 00:00:00 2001 From: Alan ZARLI Date: Mon, 18 Dec 2023 14:48:00 +0100 Subject: [PATCH 040/281] [Notifier] Add Smsbox notifier bridge --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_transports.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 17330540d..f94430eeb 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2750,6 +2750,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\Sms77\Sms77TransportFactory::class => 'notifier.transport_factory.sms77', NotifierBridge\Smsapi\SmsapiTransportFactory::class => 'notifier.transport_factory.smsapi', NotifierBridge\SmsBiuras\SmsBiurasTransportFactory::class => 'notifier.transport_factory.sms-biuras', + NotifierBridge\Smsbox\SmsboxTransportFactory::class => 'notifier.transport_factory.smsbox', NotifierBridge\Smsc\SmscTransportFactory::class => 'notifier.transport_factory.smsc', NotifierBridge\SmsFactor\SmsFactorTransportFactory::class => 'notifier.transport_factory.sms-factor', NotifierBridge\Smsmode\SmsmodeTransportFactory::class => 'notifier.transport_factory.smsmode', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index fd2952c50..a4a2574a2 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -188,6 +188,10 @@ ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') + ->set('notifier.transport_factory.smsbox', Bridge\Smsbox\SmsboxTransportFactory::class) + ->parent('notifier.transport_factory.abstract') + ->tag('texter.transport_factory') + ->set('notifier.transport_factory.smsc', Bridge\Smsc\SmscTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') From 089f2db00004e4b04b61ce15feee6260f8eb7b9d Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 26 Dec 2023 22:44:28 +0100 Subject: [PATCH 041/281] [Mailer][Notifier] Simplify transport service registration + sorting --- Resources/config/mailer_transports.php | 97 ++---- Resources/config/notifier_transports.php | 380 ++++++----------------- 2 files changed, 124 insertions(+), 353 deletions(-) diff --git a/Resources/config/mailer_transports.php b/Resources/config/mailer_transports.php index b8f822738..f95fc6d64 100644 --- a/Resources/config/mailer_transports.php +++ b/Resources/config/mailer_transports.php @@ -39,73 +39,32 @@ service('http_client')->ignoreOnInvalid(), service('logger')->ignoreOnInvalid(), ]) - ->tag('monolog.logger', ['channel' => 'mailer']) - - ->set('mailer.transport_factory.amazon', SesTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.azure', AzureTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.brevo', BrevoTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.gmail', GmailTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.infobip', InfobipTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.mailersend', MailerSendTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.mailchimp', MandrillTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.mailjet', MailjetTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.mailgun', MailgunTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.mailpace', MailPaceTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.postmark', PostmarkTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.sendgrid', SendgridTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.null', NullTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.scaleway', ScalewayTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.sendmail', SendmailTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory') - - ->set('mailer.transport_factory.smtp', EsmtpTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory', ['priority' => -100]) - - ->set('mailer.transport_factory.native', NativeTransportFactory::class) - ->parent('mailer.transport_factory.abstract') - ->tag('mailer.transport_factory'); + ->tag('monolog.logger', ['channel' => 'mailer']); + + $factories = [ + 'amazon' => SesTransportFactory::class, + 'azure' => AzureTransportFactory::class, + 'brevo' => BrevoTransportFactory::class, + 'gmail' => GmailTransportFactory::class, + 'infobip' => InfobipTransportFactory::class, + 'mailchimp' => MandrillTransportFactory::class, + 'mailersend' => MailerSendTransportFactory::class, + 'mailgun' => MailgunTransportFactory::class, + 'mailjet' => MailjetTransportFactory::class, + 'mailpace' => MailPaceTransportFactory::class, + 'native' => NativeTransportFactory::class, + 'null' => NullTransportFactory::class, + 'postmark' => PostmarkTransportFactory::class, + 'scaleway' => ScalewayTransportFactory::class, + 'sendgrid' => SendgridTransportFactory::class, + 'sendmail' => SendmailTransportFactory::class, + 'smtp' => EsmtpTransportFactory::class, + ]; + + foreach ($factories as $name => $class) { + $container->services() + ->set('mailer.transport_factory.'.$name, $class) + ->parent('mailer.transport_factory.abstract') + ->tag('mailer.transport_factory'); + } }; diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index a4a2574a2..fbe52abc2 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -20,156 +20,103 @@ ->set('notifier.transport_factory.abstract', AbstractTransportFactory::class) ->abstract() - ->args([service('event_dispatcher'), service('http_client')->ignoreOnInvalid()]) - - ->set('notifier.transport_factory.bluesky', Bridge\Bluesky\BlueskyTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.brevo', Bridge\Brevo\BrevoTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.slack', Bridge\Slack\SlackTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.linked-in', Bridge\LinkedIn\LinkedInTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.telegram', Bridge\Telegram\TelegramTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.mattermost', Bridge\Mattermost\MattermostTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.vonage', Bridge\Vonage\VonageTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.rocket-chat', Bridge\RocketChat\RocketChatTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.google-chat', Bridge\GoogleChat\GoogleChatTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.twilio', Bridge\Twilio\TwilioTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.twitter', Bridge\Twitter\TwitterTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.unifonic', Bridge\Unifonic\UnifonicTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.all-my-sms', Bridge\AllMySms\AllMySmsTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.firebase', Bridge\Firebase\FirebaseTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.forty-six-elks', Bridge\FortySixElks\FortySixElksTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.free-mobile', Bridge\FreeMobile\FreeMobileTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.spot-hit', Bridge\SpotHit\SpotHitTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.fake-chat', Bridge\FakeChat\FakeChatTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.fake-sms', Bridge\FakeSms\FakeSmsTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.ovh-cloud', Bridge\OvhCloud\OvhCloudTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.sinch', Bridge\Sinch\SinchTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.zulip', Bridge\Zulip\ZulipTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.infobip', Bridge\Infobip\InfobipTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.isendpro', Bridge\Isendpro\IsendproTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.mobyt', Bridge\Mobyt\MobytTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.smsapi', Bridge\Smsapi\SmsapiTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.esendex', Bridge\Esendex\EsendexTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.sendberry', Bridge\Sendberry\SendberryTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.iqsms', Bridge\Iqsms\IqsmsTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.octopush', Bridge\Octopush\OctopushTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.discord', Bridge\Discord\DiscordTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.microsoft-teams', Bridge\MicrosoftTeams\MicrosoftTeamsTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.gateway-api', Bridge\GatewayApi\GatewayApiTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.mercure', Bridge\Mercure\MercureTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.gitter', Bridge\Gitter\GitterTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.clickatell', Bridge\Clickatell\ClickatellTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.contact-everyone', Bridge\ContactEveryone\ContactEveryoneTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') + ->args([ + service('event_dispatcher'), + service('http_client')->ignoreOnInvalid(), + ]); + + $chatterFactories = [ + 'google-chat' => Bridge\GoogleChat\GoogleChatTransportFactory::class, + 'telegram' => Bridge\Telegram\TelegramTransportFactory::class, + 'bluesky' => Bridge\Bluesky\BlueskyTransportFactory::class, + 'fake-chat' => Bridge\FakeChat\FakeChatTransportFactory::class, + 'firebase' => Bridge\Firebase\FirebaseTransportFactory::class, + 'gitter' => Bridge\Gitter\GitterTransportFactory::class, + 'line-notify' => Bridge\LineNotify\LineNotifyTransportFactory::class, + 'linked-in' => Bridge\LinkedIn\LinkedInTransportFactory::class, + 'mastodon' => Bridge\Mastodon\MastodonTransportFactory::class, + 'mercure' => Bridge\Mercure\MercureTransportFactory::class, + 'microsoft-teams' => Bridge\MicrosoftTeams\MicrosoftTeamsTransportFactory::class, + 'pager-duty' => Bridge\PagerDuty\PagerDutyTransportFactory::class, + 'rocket-chat' => Bridge\RocketChat\RocketChatTransportFactory::class, + 'twitter' => Bridge\Twitter\TwitterTransportFactory::class, + 'zulip' => Bridge\Zulip\ZulipTransportFactory::class, + 'brevo' => Bridge\Brevo\BrevoTransportFactory::class, + 'chatwork' => Bridge\Chatwork\ChatworkTransportFactory::class, + 'discord' => Bridge\Discord\DiscordTransportFactory::class, + 'mattermost' => Bridge\Mattermost\MattermostTransportFactory::class, + 'slack' => Bridge\Slack\SlackTransportFactory::class, + 'zendesk' => Bridge\Zendesk\ZendeskTransportFactory::class, + ]; + + foreach ($chatterFactories as $name => $class) { + $container->services() + ->set('notifier.transport_factory.'.$name, $class) + ->parent('notifier.transport_factory.abstract') + ->tag('chatter.transport_factory'); + } + + $texterFactories = [ + 'all-my-sms' => Bridge\AllMySms\AllMySmsTransportFactory::class, + 'bandwidth' => Bridge\Bandwidth\BandwidthTransportFactory::class, + 'click-send' => Bridge\ClickSend\ClickSendTransportFactory::class, + 'clickatell' => Bridge\Clickatell\ClickatellTransportFactory::class, + 'contact-everyone' => Bridge\ContactEveryone\ContactEveryoneTransportFactory::class, + 'engagespot' => Bridge\Engagespot\EngagespotTransportFactory::class, + 'esendex' => Bridge\Esendex\EsendexTransportFactory::class, + 'expo' => Bridge\Expo\ExpoTransportFactory::class, + 'fake-sms' => Bridge\FakeSms\FakeSmsTransportFactory::class, + 'forty-six-elks' => Bridge\FortySixElks\FortySixElksTransportFactory::class, + 'free-mobile' => Bridge\FreeMobile\FreeMobileTransportFactory::class, + 'gateway-api' => Bridge\GatewayApi\GatewayApiTransportFactory::class, + 'go-ip' => Bridge\GoIp\GoIpTransportFactory::class, + 'infobip' => Bridge\Infobip\InfobipTransportFactory::class, + 'iqsms' => Bridge\Iqsms\IqsmsTransportFactory::class, + 'isendpro' => Bridge\Isendpro\IsendproTransportFactory::class, + 'kaz-info-teh' => Bridge\KazInfoTeh\KazInfoTehTransportFactory::class, + 'mailjet' => Bridge\Mailjet\MailjetTransportFactory::class, + 'message-bird' => Bridge\MessageBird\MessageBirdTransportFactory::class, + 'message-media' => Bridge\MessageMedia\MessageMediaTransportFactory::class, + 'mobyt' => Bridge\Mobyt\MobytTransportFactory::class, + 'novu' => Bridge\Novu\NovuTransportFactory::class, + 'ntfy' => Bridge\Ntfy\NtfyTransportFactory::class, + 'octopush' => Bridge\Octopush\OctopushTransportFactory::class, + 'one-signal' => Bridge\OneSignal\OneSignalTransportFactory::class, + 'orange-sms' => Bridge\OrangeSms\OrangeSmsTransportFactory::class, + 'ovh-cloud' => Bridge\OvhCloud\OvhCloudTransportFactory::class, + 'plivo' => Bridge\Plivo\PlivoTransportFactory::class, + 'pushover' => Bridge\Pushover\PushoverTransportFactory::class, + 'redlink' => Bridge\Redlink\RedlinkTransportFactory::class, + 'ring-central' => Bridge\RingCentral\RingCentralTransportFactory::class, + 'sendberry' => Bridge\Sendberry\SendberryTransportFactory::class, + 'simple-textin' => Bridge\SimpleTextin\SimpleTextinTransportFactory::class, + 'sinch' => Bridge\Sinch\SinchTransportFactory::class, + 'sms-factor' => Bridge\SmsFactor\SmsFactorTransportFactory::class, + 'sms77' => Bridge\Sms77\Sms77TransportFactory::class, + 'smsapi' => Bridge\Smsapi\SmsapiTransportFactory::class, + 'smsc' => Bridge\Smsc\SmscTransportFactory::class, + 'smsmode' => Bridge\Smsmode\SmsmodeTransportFactory::class, + 'spot-hit' => Bridge\SpotHit\SpotHitTransportFactory::class, + 'telnyx' => Bridge\Telnyx\TelnyxTransportFactory::class, + 'termii' => Bridge\Termii\TermiiTransportFactory::class, + 'turbo-sms' => Bridge\TurboSms\TurboSmsTransportFactory::class, + 'twilio' => Bridge\Twilio\TwilioTransportFactory::class, + 'unifonic' => Bridge\Unifonic\UnifonicTransportFactory::class, + 'vonage' => Bridge\Vonage\VonageTransportFactory::class, + 'yunpian' => Bridge\Yunpian\YunpianTransportFactory::class, + 'light-sms' => Bridge\LightSms\LightSmsTransportFactory::class, + 'sms-biuras' => Bridge\SmsBiuras\SmsBiurasTransportFactory::class, + 'smsbox' => Bridge\Smsbox\SmsboxTransportFactory::class, + ]; + + foreach ($texterFactories as $name => $class) { + $container->services() + ->set('notifier.transport_factory.'.$name, $class) + ->parent('notifier.transport_factory.abstract') + ->tag('texter.transport_factory'); + } + $container->services() ->set('notifier.transport_factory.amazon-sns', Bridge\AmazonSns\AmazonSnsTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') @@ -179,140 +126,5 @@ ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.light-sms', Bridge\LightSms\LightSmsTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.sms-biuras', Bridge\SmsBiuras\SmsBiurasTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.smsbox', Bridge\Smsbox\SmsboxTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.smsc', Bridge\Smsc\SmscTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.sms-factor', Bridge\SmsFactor\SmsFactorTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.message-bird', Bridge\MessageBird\MessageBirdTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.message-media', Bridge\MessageMedia\MessageMediaTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.telnyx', Bridge\Telnyx\TelnyxTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.mailjet', Bridge\Mailjet\MailjetTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.yunpian', Bridge\Yunpian\YunpianTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.turbo-sms', Bridge\TurboSms\TurboSmsTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.sms77', Bridge\Sms77\Sms77TransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.one-signal', Bridge\OneSignal\OneSignalTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.orange-sms', Bridge\OrangeSms\OrangeSmsTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.expo', Bridge\Expo\ExpoTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.kaz-info-teh', Bridge\KazInfoTeh\KazInfoTehTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.engagespot', Bridge\Engagespot\EngagespotTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.zendesk', Bridge\Zendesk\ZendeskTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.chatwork', Bridge\Chatwork\ChatworkTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.termii', Bridge\Termii\TermiiTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.ring-central', Bridge\RingCentral\RingCentralTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.plivo', Bridge\Plivo\PlivoTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.bandwidth', Bridge\Bandwidth\BandwidthTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.line-notify', Bridge\LineNotify\LineNotifyTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.mastodon', Bridge\Mastodon\MastodonTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.pager-duty', Bridge\PagerDuty\PagerDutyTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('chatter.transport_factory') - - ->set('notifier.transport_factory.pushover', Bridge\Pushover\PushoverTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.simple-textin', Bridge\SimpleTextin\SimpleTextinTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.click-send', Bridge\ClickSend\ClickSendTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.smsmode', Bridge\Smsmode\SmsmodeTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.novu', Bridge\Novu\NovuTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.ntfy', Bridge\Ntfy\NtfyTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - - ->set('notifier.transport_factory.redlink', Bridge\Redlink\RedlinkTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') - ->set('notifier.transport_factory.go-ip', Bridge\GoIp\GoIpTransportFactory::class) - ->parent('notifier.transport_factory.abstract') - ->tag('texter.transport_factory') ; }; From 67fc0dd6052228a4a0128335adc701f18ef25963 Mon Sep 17 00:00:00 2001 From: Dennis Fridrich Date: Sat, 9 Dec 2023 17:41:28 +0100 Subject: [PATCH 042/281] Add sms-sluzba.cz Notifier Bridge Fix #52975 --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_transports.php | 1 + 2 files changed, 2 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index f94430eeb..d1e6aa073 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2754,6 +2754,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\Smsc\SmscTransportFactory::class => 'notifier.transport_factory.smsc', NotifierBridge\SmsFactor\SmsFactorTransportFactory::class => 'notifier.transport_factory.sms-factor', NotifierBridge\Smsmode\SmsmodeTransportFactory::class => 'notifier.transport_factory.smsmode', + NotifierBridge\SmsSluzba\SmsSluzbaTransportFactory::class => 'notifier.transport_factory.sms-sluzba', NotifierBridge\SpotHit\SpotHitTransportFactory::class => 'notifier.transport_factory.spot-hit', NotifierBridge\Telegram\TelegramTransportFactory::class => 'notifier.transport_factory.telegram', NotifierBridge\Telnyx\TelnyxTransportFactory::class => 'notifier.transport_factory.telnyx', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index fbe52abc2..94962e8f1 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -107,6 +107,7 @@ 'light-sms' => Bridge\LightSms\LightSmsTransportFactory::class, 'sms-biuras' => Bridge\SmsBiuras\SmsBiurasTransportFactory::class, 'smsbox' => Bridge\Smsbox\SmsboxTransportFactory::class, + 'sms-sluzba' => Bridge\SmsSluzba\SmsSluzbaTransportFactory::class, ]; foreach ($texterFactories as $name => $class) { From e7b31132415b7eb37e6a21cb2f801462b268b43a Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Thu, 28 Dec 2023 22:37:50 +0100 Subject: [PATCH 043/281] [Notifier] Sort factories --- Resources/config/notifier_transports.php | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index 94962e8f1..39a44e278 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -26,27 +26,27 @@ ]); $chatterFactories = [ - 'google-chat' => Bridge\GoogleChat\GoogleChatTransportFactory::class, - 'telegram' => Bridge\Telegram\TelegramTransportFactory::class, 'bluesky' => Bridge\Bluesky\BlueskyTransportFactory::class, + 'brevo' => Bridge\Brevo\BrevoTransportFactory::class, + 'chatwork' => Bridge\Chatwork\ChatworkTransportFactory::class, + 'discord' => Bridge\Discord\DiscordTransportFactory::class, 'fake-chat' => Bridge\FakeChat\FakeChatTransportFactory::class, 'firebase' => Bridge\Firebase\FirebaseTransportFactory::class, 'gitter' => Bridge\Gitter\GitterTransportFactory::class, + 'google-chat' => Bridge\GoogleChat\GoogleChatTransportFactory::class, 'line-notify' => Bridge\LineNotify\LineNotifyTransportFactory::class, 'linked-in' => Bridge\LinkedIn\LinkedInTransportFactory::class, 'mastodon' => Bridge\Mastodon\MastodonTransportFactory::class, + 'mattermost' => Bridge\Mattermost\MattermostTransportFactory::class, 'mercure' => Bridge\Mercure\MercureTransportFactory::class, 'microsoft-teams' => Bridge\MicrosoftTeams\MicrosoftTeamsTransportFactory::class, 'pager-duty' => Bridge\PagerDuty\PagerDutyTransportFactory::class, 'rocket-chat' => Bridge\RocketChat\RocketChatTransportFactory::class, - 'twitter' => Bridge\Twitter\TwitterTransportFactory::class, - 'zulip' => Bridge\Zulip\ZulipTransportFactory::class, - 'brevo' => Bridge\Brevo\BrevoTransportFactory::class, - 'chatwork' => Bridge\Chatwork\ChatworkTransportFactory::class, - 'discord' => Bridge\Discord\DiscordTransportFactory::class, - 'mattermost' => Bridge\Mattermost\MattermostTransportFactory::class, 'slack' => Bridge\Slack\SlackTransportFactory::class, + 'telegram' => Bridge\Telegram\TelegramTransportFactory::class, + 'twitter' => Bridge\Twitter\TwitterTransportFactory::class, 'zendesk' => Bridge\Zendesk\ZendeskTransportFactory::class, + 'zulip' => Bridge\Zulip\ZulipTransportFactory::class, ]; foreach ($chatterFactories as $name => $class) { @@ -74,6 +74,7 @@ 'iqsms' => Bridge\Iqsms\IqsmsTransportFactory::class, 'isendpro' => Bridge\Isendpro\IsendproTransportFactory::class, 'kaz-info-teh' => Bridge\KazInfoTeh\KazInfoTehTransportFactory::class, + 'light-sms' => Bridge\LightSms\LightSmsTransportFactory::class, 'mailjet' => Bridge\Mailjet\MailjetTransportFactory::class, 'message-bird' => Bridge\MessageBird\MessageBirdTransportFactory::class, 'message-media' => Bridge\MessageMedia\MessageMediaTransportFactory::class, @@ -91,9 +92,12 @@ 'sendberry' => Bridge\Sendberry\SendberryTransportFactory::class, 'simple-textin' => Bridge\SimpleTextin\SimpleTextinTransportFactory::class, 'sinch' => Bridge\Sinch\SinchTransportFactory::class, + 'sms-biuras' => Bridge\SmsBiuras\SmsBiurasTransportFactory::class, 'sms-factor' => Bridge\SmsFactor\SmsFactorTransportFactory::class, + 'sms-sluzba' => Bridge\SmsSluzba\SmsSluzbaTransportFactory::class, 'sms77' => Bridge\Sms77\Sms77TransportFactory::class, 'smsapi' => Bridge\Smsapi\SmsapiTransportFactory::class, + 'smsbox' => Bridge\Smsbox\SmsboxTransportFactory::class, 'smsc' => Bridge\Smsc\SmscTransportFactory::class, 'smsmode' => Bridge\Smsmode\SmsmodeTransportFactory::class, 'spot-hit' => Bridge\SpotHit\SpotHitTransportFactory::class, @@ -104,10 +108,6 @@ 'unifonic' => Bridge\Unifonic\UnifonicTransportFactory::class, 'vonage' => Bridge\Vonage\VonageTransportFactory::class, 'yunpian' => Bridge\Yunpian\YunpianTransportFactory::class, - 'light-sms' => Bridge\LightSms\LightSmsTransportFactory::class, - 'sms-biuras' => Bridge\SmsBiuras\SmsBiurasTransportFactory::class, - 'smsbox' => Bridge\Smsbox\SmsboxTransportFactory::class, - 'sms-sluzba' => Bridge\SmsSluzba\SmsSluzbaTransportFactory::class, ]; foreach ($texterFactories as $name => $class) { From 595d82e7b043781af74030f2aa743c1720a53dff Mon Sep 17 00:00:00 2001 From: Frank Naegler Date: Fri, 8 Dec 2023 00:00:57 +0100 Subject: [PATCH 044/281] [Notifier] Add Seven Notifier Bridge Seven.io is the new name of SMS77, they changed also the URL to the gateway. To reflect that change, this patch introduces the new Seven Notifier Bridge. The current SMS77 Bridge should be deprecated in another patch --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_transports.php | 1 + 2 files changed, 2 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index d1e6aa073..4750729ef 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2745,6 +2745,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\RocketChat\RocketChatTransportFactory::class => 'notifier.transport_factory.rocket-chat', NotifierBridge\Sendberry\SendberryTransportFactory::class => 'notifier.transport_factory.sendberry', NotifierBridge\SimpleTextin\SimpleTextinTransportFactory::class => 'notifier.transport_factory.simple-textin', + NotifierBridge\Sevenio\SevenIoTransportFactory::class => 'notifier.transport_factory.sevenio', NotifierBridge\Sinch\SinchTransportFactory::class => 'notifier.transport_factory.sinch', NotifierBridge\Slack\SlackTransportFactory::class => 'notifier.transport_factory.slack', NotifierBridge\Sms77\Sms77TransportFactory::class => 'notifier.transport_factory.sms77', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index 39a44e278..c92d9b4b6 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -90,6 +90,7 @@ 'redlink' => Bridge\Redlink\RedlinkTransportFactory::class, 'ring-central' => Bridge\RingCentral\RingCentralTransportFactory::class, 'sendberry' => Bridge\Sendberry\SendberryTransportFactory::class, + 'sevenio' => Bridge\Sevenio\SevenIoTransportFactory::class, 'simple-textin' => Bridge\SimpleTextin\SimpleTextinTransportFactory::class, 'sinch' => Bridge\Sinch\SinchTransportFactory::class, 'sms-biuras' => Bridge\SmsBiuras\SmsBiurasTransportFactory::class, From dbd3cebcc9feb4e0518fa0b24f210a56b6b8fcc7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 30 Dec 2023 20:21:21 +0100 Subject: [PATCH 045/281] Leverage ReflectionFunction::isAnonymous() --- Console/Descriptor/JsonDescriptor.php | 2 +- Console/Descriptor/MarkdownDescriptor.php | 2 +- Console/Descriptor/TextDescriptor.php | 2 +- Console/Descriptor/XmlDescriptor.php | 2 +- Kernel/MicroKernelTrait.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Console/Descriptor/JsonDescriptor.php b/Console/Descriptor/JsonDescriptor.php index f3c70310e..9562dc500 100644 --- a/Console/Descriptor/JsonDescriptor.php +++ b/Console/Descriptor/JsonDescriptor.php @@ -393,7 +393,7 @@ private function getCallableData(mixed $callable): array $data['type'] = 'closure'; $r = new \ReflectionFunction($callable); - if (str_contains($r->name, '{closure}')) { + if ($r->isAnonymous()) { return $data; } $data['name'] = $r->name; diff --git a/Console/Descriptor/MarkdownDescriptor.php b/Console/Descriptor/MarkdownDescriptor.php index 3b66c79cf..924951b5d 100644 --- a/Console/Descriptor/MarkdownDescriptor.php +++ b/Console/Descriptor/MarkdownDescriptor.php @@ -403,7 +403,7 @@ protected function describeCallable(mixed $callable, array $options = []): void $string .= "\n- Type: `closure`"; $r = new \ReflectionFunction($callable); - if (str_contains($r->name, '{closure}')) { + if ($r->isAnonymous()) { $this->write($string."\n"); return; diff --git a/Console/Descriptor/TextDescriptor.php b/Console/Descriptor/TextDescriptor.php index d13d3e13c..4e243c45e 100644 --- a/Console/Descriptor/TextDescriptor.php +++ b/Console/Descriptor/TextDescriptor.php @@ -649,7 +649,7 @@ private function formatCallable(mixed $callable): string if ($callable instanceof \Closure) { $r = new \ReflectionFunction($callable); - if (str_contains($r->name, '{closure}')) { + if ($r->isAnonymous()) { return 'Closure()'; } if ($class = $r->getClosureCalledClass()) { diff --git a/Console/Descriptor/XmlDescriptor.php b/Console/Descriptor/XmlDescriptor.php index d530936d7..14435f9e0 100644 --- a/Console/Descriptor/XmlDescriptor.php +++ b/Console/Descriptor/XmlDescriptor.php @@ -581,7 +581,7 @@ private function getCallableDocument(mixed $callable): \DOMDocument $callableXML->setAttribute('type', 'closure'); $r = new \ReflectionFunction($callable); - if (str_contains($r->name, '{closure}')) { + if ($r->isAnonymous()) { return $dom; } $callableXML->setAttribute('name', $r->name); diff --git a/Kernel/MicroKernelTrait.php b/Kernel/MicroKernelTrait.php index 73a7f6fe7..b3f49c059 100644 --- a/Kernel/MicroKernelTrait.php +++ b/Kernel/MicroKernelTrait.php @@ -214,7 +214,7 @@ public function loadRoutes(LoaderInterface $loader): RouteCollection if (\is_array($controller) && [0, 1] === array_keys($controller) && $this === $controller[0]) { $route->setDefault('_controller', ['kernel', $controller[1]]); - } elseif ($controller instanceof \Closure && $this === ($r = new \ReflectionFunction($controller))->getClosureThis() && !str_contains($r->name, '{closure}')) { + } elseif ($controller instanceof \Closure && $this === ($r = new \ReflectionFunction($controller))->getClosureThis() && !$r->isAnonymous()) { $route->setDefault('_controller', ['kernel', $r->name]); } } From 832108167271ee0c82413c14549f3a6a119ab6cc Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 20 Dec 2023 08:12:55 +0100 Subject: [PATCH 046/281] mark classes implementing the WarmableInterface as final --- CHANGELOG.md | 1 + CacheWarmer/ConfigBuilderCacheWarmer.php | 2 ++ CacheWarmer/SerializerCacheWarmer.php | 2 ++ CacheWarmer/TranslationsCacheWarmer.php | 2 ++ CacheWarmer/ValidatorCacheWarmer.php | 2 ++ Routing/Router.php | 2 ++ Tests/Translation/TranslatorTest.php | 18 ------------------ Translation/Translator.php | 2 ++ 8 files changed, 13 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed156fda3..804b949dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.1 --- + * Mark classes `ConfigBuilderCacheWarmer`, `Router`, `SerializerCacheWarmer`, `TranslationsCacheWarmer`, `Translator` and `ValidatorCacheWarmer` as `final` * Move the Router `cache_dir` to `kernel.build_dir` * Deprecate the `router.cache_dir` config option * Add `rate_limiter` tags to rate limiter services diff --git a/CacheWarmer/ConfigBuilderCacheWarmer.php b/CacheWarmer/ConfigBuilderCacheWarmer.php index 04ba80f6a..6c93797c7 100644 --- a/CacheWarmer/ConfigBuilderCacheWarmer.php +++ b/CacheWarmer/ConfigBuilderCacheWarmer.php @@ -25,6 +25,8 @@ * Generate all config builders. * * @author Tobias Nyholm + * + * @final since Symfony 7.1 */ class ConfigBuilderCacheWarmer implements CacheWarmerInterface { diff --git a/CacheWarmer/SerializerCacheWarmer.php b/CacheWarmer/SerializerCacheWarmer.php index b1300516b..662c2136d 100644 --- a/CacheWarmer/SerializerCacheWarmer.php +++ b/CacheWarmer/SerializerCacheWarmer.php @@ -23,6 +23,8 @@ * Warms up XML and YAML serializer metadata. * * @author Titouan Galopin + * + * @final since Symfony 7.1 */ class SerializerCacheWarmer extends AbstractPhpFileCacheWarmer { diff --git a/CacheWarmer/TranslationsCacheWarmer.php b/CacheWarmer/TranslationsCacheWarmer.php index 59a7865a1..37fa98aa7 100644 --- a/CacheWarmer/TranslationsCacheWarmer.php +++ b/CacheWarmer/TranslationsCacheWarmer.php @@ -21,6 +21,8 @@ * Generates the catalogues for translations. * * @author Xavier Leune + * + * @final since Symfony 7.1 */ class TranslationsCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface { diff --git a/CacheWarmer/ValidatorCacheWarmer.php b/CacheWarmer/ValidatorCacheWarmer.php index d88fd36c8..3d9349a5b 100644 --- a/CacheWarmer/ValidatorCacheWarmer.php +++ b/CacheWarmer/ValidatorCacheWarmer.php @@ -24,6 +24,8 @@ * Warms up XML and YAML validator metadata. * * @author Titouan Galopin + * + * @final since Symfony 7.1 */ class ValidatorCacheWarmer extends AbstractPhpFileCacheWarmer { diff --git a/Routing/Router.php b/Routing/Router.php index b5b7567de..6712817eb 100644 --- a/Routing/Router.php +++ b/Routing/Router.php @@ -30,6 +30,8 @@ * This Router creates the Loader only when the cache is empty. * * @author Fabien Potencier + * + * @final since Symfony 7.1 */ class Router extends BaseRouter implements WarmableInterface, ServiceSubscriberInterface { diff --git a/Tests/Translation/TranslatorTest.php b/Tests/Translation/TranslatorTest.php index 2ef550ef0..1c8efd9b1 100644 --- a/Tests/Translation/TranslatorTest.php +++ b/Tests/Translation/TranslatorTest.php @@ -100,16 +100,6 @@ public function testTransWithCaching() $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); } - public function testTransWithCachingWithInvalidLocale() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid "invalid locale" locale.'); - $loader = $this->createMock(LoaderInterface::class); - $translator = $this->getTranslator($loader, ['cache_dir' => $this->tmpDir], 'loader', TranslatorWithInvalidLocale::class); - - $translator->trans('foo'); - } - public function testLoadResourcesWithoutCaching() { $loader = new YamlFileLoader(); @@ -418,11 +408,3 @@ private function createTranslator($loader, $options, $translatorClass = Translat ); } } - -class TranslatorWithInvalidLocale extends Translator -{ - public function getLocale(): string - { - return 'invalid locale'; - } -} diff --git a/Translation/Translator.php b/Translation/Translator.php index 644b0caac..91a070923 100644 --- a/Translation/Translator.php +++ b/Translation/Translator.php @@ -21,6 +21,8 @@ /** * @author Fabien Potencier + * + * @final since Symfony 7.1 */ class Translator extends BaseTranslator implements WarmableInterface { From fa06aec731adeefe9d4bd253b179fb27ad569b78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 19 Dec 2023 13:47:41 +0100 Subject: [PATCH 047/281] [FrameworkBundle] Prevent silenced warning by checking if file exists --- Command/CacheClearCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Command/CacheClearCommand.php b/Command/CacheClearCommand.php index 878157e87..d8f23f7de 100644 --- a/Command/CacheClearCommand.php +++ b/Command/CacheClearCommand.php @@ -200,7 +200,7 @@ private function isNfs(string $dir): bool if (null === $mounts) { $mounts = []; - if ('/' === \DIRECTORY_SEPARATOR && $files = @file('/proc/mounts')) { + if ('/' === \DIRECTORY_SEPARATOR && is_readable('/proc/mounts') && $files = @file('/proc/mounts')) { foreach ($files as $mount) { $mount = \array_slice(explode(' ', $mount), 1, -3); if (!\in_array(array_pop($mount), ['vboxsf', 'nfs'])) { From ab37429c57a88caf8f1b9846934b439fb26d782d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 31 Dec 2023 11:32:07 +0100 Subject: [PATCH 048/281] add a private_ranges shortcut for trusted_proxies --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 8 +++++++- .../Fixtures/php/trusted_proxies_private_ranges.php | 5 +++++ .../Fixtures/xml/trusted_proxies_private_ranges.xml | 9 +++++++++ .../Fixtures/yml/trusted_proxies_private_ranges.yml | 2 ++ Tests/DependencyInjection/FrameworkExtensionTestCase.php | 8 ++++++++ 6 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 Tests/DependencyInjection/Fixtures/php/trusted_proxies_private_ranges.php create mode 100644 Tests/DependencyInjection/Fixtures/xml/trusted_proxies_private_ranges.xml create mode 100644 Tests/DependencyInjection/Fixtures/yml/trusted_proxies_private_ranges.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 804b949dc..43d7f6295 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.1 --- + * Add `private_ranges` as a shortcut for private IP address ranges to the `trusted_proxies` option * Mark classes `ConfigBuilderCacheWarmer`, `Router`, `SerializerCacheWarmer`, `TranslationsCacheWarmer`, `Translator` and `ValidatorCacheWarmer` as `final` * Move the Router `cache_dir` to `kernel.build_dir` * Deprecate the `router.cache_dir` config option diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 31dbc6f8d..7247cbe5d 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -29,6 +29,7 @@ use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface; use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\IpUtils; use Symfony\Component\Lock\Lock; use Symfony\Component\Lock\Store\SemaphoreStore; use Symfony\Component\Mailer\Mailer; @@ -111,7 +112,12 @@ public function getConfigTreeBuilder(): TreeBuilder ->beforeNormalization()->ifString()->then(fn ($v) => [$v])->end() ->prototype('scalar')->end() ->end() - ->scalarNode('trusted_proxies')->end() + ->scalarNode('trusted_proxies') + ->beforeNormalization() + ->ifTrue(fn ($v) => 'private_ranges' === $v) + ->then(fn ($v) => implode(',', IpUtils::PRIVATE_SUBNETS)) + ->end() + ->end() ->arrayNode('trusted_headers') ->fixXmlConfig('trusted_header') ->performNoDeepMerging() diff --git a/Tests/DependencyInjection/Fixtures/php/trusted_proxies_private_ranges.php b/Tests/DependencyInjection/Fixtures/php/trusted_proxies_private_ranges.php new file mode 100644 index 000000000..e3a5dc4a5 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/trusted_proxies_private_ranges.php @@ -0,0 +1,5 @@ +loadFromExtension('framework', [ + 'trusted_proxies' => 'private_ranges', +]); diff --git a/Tests/DependencyInjection/Fixtures/xml/trusted_proxies_private_ranges.xml b/Tests/DependencyInjection/Fixtures/xml/trusted_proxies_private_ranges.xml new file mode 100644 index 000000000..700f84959 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/trusted_proxies_private_ranges.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/Tests/DependencyInjection/Fixtures/yml/trusted_proxies_private_ranges.yml b/Tests/DependencyInjection/Fixtures/yml/trusted_proxies_private_ranges.yml new file mode 100644 index 000000000..b98bb2f78 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/trusted_proxies_private_ranges.yml @@ -0,0 +1,2 @@ +framework: + trusted_proxies: private_ranges diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 4bd3c481f..0d97dcf62 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -52,6 +52,7 @@ use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\RetryableHttpClient; use Symfony\Component\HttpClient\ScopingHttpClient; +use Symfony\Component\HttpFoundation\IpUtils; use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass; use Symfony\Component\HttpKernel\Fragment\FragmentUriGeneratorInterface; use Symfony\Component\Messenger\Bridge\AmazonSqs\Transport\AmazonSqsTransportFactory; @@ -2265,6 +2266,13 @@ public function testNotifierWithSpecificMessageBus() $this->assertEquals(new Reference('app.another_bus'), $container->getDefinition('notifier.channel.sms')->getArgument(1)); } + public function testTrustedProxiesWithPrivateRanges() + { + $container = $this->createContainerFromFile('trusted_proxies_private_ranges'); + + $this->assertSame(IpUtils::PRIVATE_SUBNETS, array_map('trim', explode(',', $container->getParameter('kernel.trusted_proxies')))); + } + public function testWebhook() { if (!class_exists(WebhookController::class)) { From d4819b6ae802baca579f16283dfc387ab8068212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rokas=20Mikalk=C4=97nas?= Date: Sun, 31 Dec 2023 07:14:05 +0200 Subject: [PATCH 049/281] [Messenger] Add jitter parameter to MultiplierRetryStrategy --- DependencyInjection/Configuration.php | 1 + DependencyInjection/FrameworkExtension.php | 3 ++- Resources/config/messenger.php | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 7247cbe5d..702b4f610 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1592,6 +1592,7 @@ function ($a) { ->integerNode('delay')->defaultValue(1000)->min(0)->info('Time in ms to delay (or the initial value when multiplier is used)')->end() ->floatNode('multiplier')->defaultValue(2)->min(1)->info('If greater than 1, delay will grow exponentially for each retry: this delay = (delay * (multiple ^ retries))')->end() ->integerNode('max_delay')->defaultValue(0)->min(0)->info('Max time in ms that a retry should ever be delayed (0 = infinite)')->end() + ->floatNode('jitter')->defaultValue(0.1)->min(0)->max(1)->info('Randomness to apply to the delay (between 0 and 1)')->end() ->end() ->end() ->scalarNode('rate_limiter') diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 4750729ef..2b2412bae 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2195,7 +2195,8 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder ->replaceArgument(0, $transport['retry_strategy']['max_retries']) ->replaceArgument(1, $transport['retry_strategy']['delay']) ->replaceArgument(2, $transport['retry_strategy']['multiplier']) - ->replaceArgument(3, $transport['retry_strategy']['max_delay']); + ->replaceArgument(3, $transport['retry_strategy']['max_delay']) + ->replaceArgument(4, $transport['retry_strategy']['jitter']); $container->setDefinition($retryServiceId, $retryDefinition); $transportRetryReferences[$name] = new Reference($retryServiceId); diff --git a/Resources/config/messenger.php b/Resources/config/messenger.php index 556affd07..f71fcf344 100644 --- a/Resources/config/messenger.php +++ b/Resources/config/messenger.php @@ -160,6 +160,7 @@ abstract_arg('delay ms'), abstract_arg('multiplier'), abstract_arg('max delay ms'), + abstract_arg('jitter'), ]) // rate limiter From 958ec0b7cee12cd49a9168b853a6fe7e3811c194 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 6 Jan 2024 17:00:23 +0100 Subject: [PATCH 050/281] add the new jitter option to the XML schema definition --- Resources/config/schema/symfony-1.0.xsd | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index 57cc92334..9279eaf68 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -609,6 +609,7 @@ + From c866a3f1550525adf3271122865cf5d76d2d988f Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 11 Oct 2023 17:07:16 +0200 Subject: [PATCH 051/281] [FrameworkBundle][RemoteEvent][Routing][Scheduler] Add PHPDoc to attributes properties --- Routing/Attribute/AsRoutingConditionService.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Routing/Attribute/AsRoutingConditionService.php b/Routing/Attribute/AsRoutingConditionService.php index d1f1a5f34..2bc407978 100644 --- a/Routing/Attribute/AsRoutingConditionService.php +++ b/Routing/Attribute/AsRoutingConditionService.php @@ -41,6 +41,10 @@ #[\Attribute(\Attribute::TARGET_CLASS)] class AsRoutingConditionService extends AutoconfigureTag { + /** + * @param string|null $alias The alias of the service to use it in routing condition expressions + * @param int $priority Defines a priority that allows the routing condition service to override a service with the same alias + */ public function __construct( string $alias = null, int $priority = 0, From a4fbe7b7c15d48d409e2cd522e223dc989a76f35 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 10 Jan 2024 12:28:47 +0100 Subject: [PATCH 052/281] do not mock the RequestStack class --- Tests/Console/ApplicationTest.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Tests/Console/ApplicationTest.php b/Tests/Console/ApplicationTest.php index 4411d59ba..42691c75b 100644 --- a/Tests/Console/ApplicationTest.php +++ b/Tests/Console/ApplicationTest.php @@ -243,11 +243,6 @@ private function getKernel(array $bundles, $useDispatcher = false) { $container = $this->createMock(ContainerInterface::class); - $requestStack = $this->createMock(RequestStack::class); - $requestStack->expects($this->any()) - ->method('push') - ; - if ($useDispatcher) { $dispatcher = $this->createMock(EventDispatcherInterface::class); $dispatcher @@ -258,7 +253,7 @@ private function getKernel(array $bundles, $useDispatcher = false) $container->expects($this->atLeastOnce()) ->method('get') ->willReturnMap([ - ['.virtual_request_stack', 2, $requestStack], + ['.virtual_request_stack', 2, new RequestStack()], ['event_dispatcher', 1, $dispatcher], ]) ; From e95e5b8b13f85998f550a1131c2989358a272f6a Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Wed, 4 Oct 2023 08:57:42 +0200 Subject: [PATCH 053/281] [TypeInfo] Introduce component Co-authored-by: Baptiste Leduc --- DependencyInjection/Configuration.php | 14 ++++++ DependencyInjection/FrameworkExtension.php | 26 ++++++++++ Resources/config/schema/symfony-1.0.xsd | 5 ++ Resources/config/type_info.php | 50 +++++++++++++++++++ .../DependencyInjection/ConfigurationTest.php | 4 ++ .../DependencyInjection/Fixtures/php/full.php | 1 + .../Fixtures/php/type_info.php | 11 ++++ .../DependencyInjection/Fixtures/xml/full.xml | 1 + .../Fixtures/xml/type_info.xml | 13 +++++ .../DependencyInjection/Fixtures/yml/full.yml | 1 + .../Fixtures/yml/type_info.yml | 8 +++ .../FrameworkExtensionTestCase.php | 6 +++ Tests/Functional/TypeInfoTest.php | 32 ++++++++++++ Tests/Functional/app/TypeInfo/Dummy.php | 21 ++++++++ Tests/Functional/app/TypeInfo/bundles.php | 18 +++++++ Tests/Functional/app/TypeInfo/config.yml | 11 ++++ composer.json | 1 + 17 files changed, 223 insertions(+) create mode 100644 Resources/config/type_info.php create mode 100644 Tests/DependencyInjection/Fixtures/php/type_info.php create mode 100644 Tests/DependencyInjection/Fixtures/xml/type_info.xml create mode 100644 Tests/DependencyInjection/Fixtures/yml/type_info.yml create mode 100644 Tests/Functional/TypeInfoTest.php create mode 100644 Tests/Functional/app/TypeInfo/Dummy.php create mode 100644 Tests/Functional/app/TypeInfo/bundles.php create mode 100644 Tests/Functional/app/TypeInfo/config.yml diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 702b4f610..81ee3e06e 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -44,6 +44,7 @@ use Symfony\Component\Serializer\Encoder\JsonDecode; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Translation\Translator; +use Symfony\Component\TypeInfo\Type; use Symfony\Component\Uid\Factory\UuidFactory; use Symfony\Component\Validator\Validation; use Symfony\Component\Webhook\Controller\WebhookController; @@ -164,6 +165,7 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addAnnotationsSection($rootNode); $this->addSerializerSection($rootNode, $enableIfStandalone); $this->addPropertyAccessSection($rootNode, $willBeAvailable); + $this->addTypeInfoSection($rootNode, $enableIfStandalone); $this->addPropertyInfoSection($rootNode, $enableIfStandalone); $this->addCacheSection($rootNode, $willBeAvailable); $this->addPhpErrorsSection($rootNode); @@ -1162,6 +1164,18 @@ private function addPropertyInfoSection(ArrayNodeDefinition $rootNode, callable ; } + private function addTypeInfoSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone): void + { + $rootNode + ->children() + ->arrayNode('type_info') + ->info('Type info configuration') + ->{$enableIfStandalone('symfony/type-info', Type::class)}() + ->end() + ->end() + ; + } + private function addCacheSection(ArrayNodeDefinition $rootNode, callable $willBeAvailable): void { $rootNode diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 9c9446fb0..933c141e1 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -53,6 +53,7 @@ use Symfony\Component\Console\Debug\CliRequest; use Symfony\Component\Console\Messenger\RunCommandMessageHandler; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -167,6 +168,8 @@ use Symfony\Component\Translation\LocaleSwitcher; use Symfony\Component\Translation\PseudoLocalizationTranslator; use Symfony\Component\Translation\Translator; +use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver; use Symfony\Component\Uid\Factory\UuidFactory; use Symfony\Component\Uid\UuidV4; use Symfony\Component\Validator\Constraints\ExpressionLanguageProvider; @@ -388,6 +391,10 @@ public function load(array $configs, ContainerBuilder $container): void $container->removeDefinition('console.command.serializer_debug'); } + if ($this->readConfigEnabled('type_info', $container, $config['type_info'])) { + $this->registerTypeInfoConfiguration($container, $loader); + } + if ($propertyInfoEnabled) { $this->registerPropertyInfoConfiguration($container, $loader); } @@ -1953,6 +1960,25 @@ private function registerPropertyInfoConfiguration(ContainerBuilder $container, } } + private function registerTypeInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void + { + if (!class_exists(Type::class)) { + throw new LogicException('TypeInfo support cannot be enabled as the TypeInfo component is not installed. Try running "composer require symfony/type-info".'); + } + + $loader->load('type_info.php'); + + if (ContainerBuilder::willBeAvailable('phpstan/phpdoc-parser', PhpDocParser::class, ['symfony/framework-bundle', 'symfony/type-info'])) { + $container->register('type_info.resolver.string', StringTypeResolver::class); + + /** @var ServiceLocatorArgument $resolversLocator */ + $resolversLocator = $container->getDefinition('type_info.resolver')->getArgument(0); + $resolversLocator->setValues($resolversLocator->getValues() + [ + 'string' => new Reference('type_info.resolver.string'), + ]); + } + } + private function registerLockConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void { $loader->load('lock.php'); diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index 9279eaf68..6ebcfedbf 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -27,6 +27,7 @@ + @@ -327,6 +328,10 @@ + + + + diff --git a/Resources/config/type_info.php b/Resources/config/type_info.php new file mode 100644 index 000000000..71e3646a1 --- /dev/null +++ b/Resources/config/type_info.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\TypeInfo\TypeContext\TypeContextFactory; +use Symfony\Component\TypeInfo\TypeResolver\ReflectionParameterTypeResolver; +use Symfony\Component\TypeInfo\TypeResolver\ReflectionPropertyTypeResolver; +use Symfony\Component\TypeInfo\TypeResolver\ReflectionReturnTypeResolver; +use Symfony\Component\TypeInfo\TypeResolver\ReflectionTypeResolver; +use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; +use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface; + +return static function (ContainerConfigurator $container) { + $container->services() + // type context + ->set('type_info.type_context_factory', TypeContextFactory::class) + ->args([service('type_info.resolver.string')->nullOnInvalid()]) + + // type resolvers + ->set('type_info.resolver', TypeResolver::class) + ->args([service_locator([ + \ReflectionType::class => service('type_info.resolver.reflection_type'), + \ReflectionParameter::class => service('type_info.resolver.reflection_parameter'), + \ReflectionProperty::class => service('type_info.resolver.reflection_property'), + \ReflectionFunctionAbstract::class => service('type_info.resolver.reflection_return'), + ])]) + ->alias(TypeResolverInterface::class, 'type_info.resolver') + + ->set('type_info.resolver.reflection_type', ReflectionTypeResolver::class) + ->args([service('type_info.type_context_factory')]) + + ->set('type_info.resolver.reflection_parameter', ReflectionParameterTypeResolver::class) + ->args([service('type_info.resolver.reflection_type'), service('type_info.type_context_factory')]) + + ->set('type_info.resolver.reflection_property', ReflectionPropertyTypeResolver::class) + ->args([service('type_info.resolver.reflection_type'), service('type_info.type_context_factory')]) + + ->set('type_info.resolver.reflection_return', ReflectionReturnTypeResolver::class) + ->args([service('type_info.resolver.reflection_type'), service('type_info.type_context_factory')]) + ; +}; diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index d56cfa90d..5a4005bbf 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -28,6 +28,7 @@ use Symfony\Component\RateLimiter\Policy\TokenBucketLimiter; use Symfony\Component\Scheduler\Messenger\SchedulerTransportFactory; use Symfony\Component\Serializer\Encoder\JsonDecode; +use Symfony\Component\TypeInfo\Type; use Symfony\Component\Uid\Factory\UuidFactory; class ConfigurationTest extends TestCase @@ -624,6 +625,9 @@ protected static function getBundleDefaultConfig() 'throw_exception_on_invalid_index' => false, 'throw_exception_on_invalid_property_path' => true, ], + 'type_info' => [ + 'enabled' => !class_exists(FullStack::class) && class_exists(Type::class), + ], 'property_info' => [ 'enabled' => !class_exists(FullStack::class), ], diff --git a/Tests/DependencyInjection/Fixtures/php/full.php b/Tests/DependencyInjection/Fixtures/php/full.php index b5d8061e4..4fbf72a9f 100644 --- a/Tests/DependencyInjection/Fixtures/php/full.php +++ b/Tests/DependencyInjection/Fixtures/php/full.php @@ -70,6 +70,7 @@ 'default_context' => ['enable_max_depth' => true], ], 'property_info' => true, + 'type_info' => true, 'ide' => 'file%%link%%format', 'request' => [ 'formats' => [ diff --git a/Tests/DependencyInjection/Fixtures/php/type_info.php b/Tests/DependencyInjection/Fixtures/php/type_info.php new file mode 100644 index 000000000..0e7dcbae0 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/type_info.php @@ -0,0 +1,11 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'type_info' => [ + 'enabled' => true, + ], +]); diff --git a/Tests/DependencyInjection/Fixtures/xml/full.xml b/Tests/DependencyInjection/Fixtures/xml/full.xml index 92e4405a0..fd5d52e1c 100644 --- a/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -40,5 +40,6 @@ + diff --git a/Tests/DependencyInjection/Fixtures/xml/type_info.xml b/Tests/DependencyInjection/Fixtures/xml/type_info.xml new file mode 100644 index 000000000..0fe4d525d --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/type_info.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/Tests/DependencyInjection/Fixtures/yml/full.yml b/Tests/DependencyInjection/Fixtures/yml/full.yml index 883e9d6c2..96001f1d2 100644 --- a/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -59,6 +59,7 @@ framework: max_depth_handler: my.max.depth.handler default_context: enable_max_depth: true + type_info: ~ property_info: ~ ide: file%%link%%format request: diff --git a/Tests/DependencyInjection/Fixtures/yml/type_info.yml b/Tests/DependencyInjection/Fixtures/yml/type_info.yml new file mode 100644 index 000000000..4d6b405b2 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/type_info.yml @@ -0,0 +1,8 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + type_info: + enabled: true diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 0d97dcf62..8c4711c64 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -1616,6 +1616,12 @@ public function testSerializerServiceIsNotRegisteredWhenDisabled() $this->assertFalse($container->hasDefinition('serializer')); } + public function testTypeInfoEnabled() + { + $container = $this->createContainerFromFile('type_info'); + $this->assertTrue($container->has('type_info.resolver')); + } + public function testPropertyInfoEnabled() { $container = $this->createContainerFromFile('property_info'); diff --git a/Tests/Functional/TypeInfoTest.php b/Tests/Functional/TypeInfoTest.php new file mode 100644 index 000000000..6acdb9c81 --- /dev/null +++ b/Tests/Functional/TypeInfoTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use PHPStan\PhpDocParser\Parser\PhpDocParser; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\TypeInfo\Dummy; +use Symfony\Component\TypeInfo\Type; + +class TypeInfoTest extends AbstractWebTestCase +{ + public function testComponent() + { + static::bootKernel(['test_case' => 'TypeInfo']); + + $this->assertEquals(Type::string(), static::getContainer()->get('type_info.resolver')->resolve(new \ReflectionProperty(Dummy::class, 'name'))); + + if (!class_exists(PhpDocParser::class)) { + $this->markTestSkipped('"phpstan/phpdoc-parser" dependency is required.'); + } + + $this->assertEquals(Type::int(), static::getContainer()->get('type_info.resolver')->resolve('int')); + } +} diff --git a/Tests/Functional/app/TypeInfo/Dummy.php b/Tests/Functional/app/TypeInfo/Dummy.php new file mode 100644 index 000000000..0f517df51 --- /dev/null +++ b/Tests/Functional/app/TypeInfo/Dummy.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\TypeInfo; + +/** + * @author Mathias Arlaud + * @author Baptiste Leduc + */ +class Dummy +{ + public string $name; +} diff --git a/Tests/Functional/app/TypeInfo/bundles.php b/Tests/Functional/app/TypeInfo/bundles.php new file mode 100644 index 000000000..15ff182c6 --- /dev/null +++ b/Tests/Functional/app/TypeInfo/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return [ + new FrameworkBundle(), + new TestBundle(), +]; diff --git a/Tests/Functional/app/TypeInfo/config.yml b/Tests/Functional/app/TypeInfo/config.yml new file mode 100644 index 000000000..35c7bb4c4 --- /dev/null +++ b/Tests/Functional/app/TypeInfo/config.yml @@ -0,0 +1,11 @@ +imports: + - { resource: ../config/default.yml } + +framework: + http_method_override: false + type_info: true + +services: + type_info.resolver.alias: + alias: type_info.resolver + public: true diff --git a/composer.json b/composer.json index 9a50605a2..766bc8064 100644 --- a/composer.json +++ b/composer.json @@ -64,6 +64,7 @@ "symfony/string": "^6.4|^7.0", "symfony/translation": "^6.4|^7.0", "symfony/twig-bundle": "^6.4|^7.0", + "symfony/type-info": "^7.1", "symfony/validator": "^6.4|^7.0", "symfony/workflow": "^6.4|^7.0", "symfony/yaml": "^6.4|^7.0", From f26cb41a1eaffbe43105157f172fe92c54212f2a Mon Sep 17 00:00:00 2001 From: Mathieu Santostefano Date: Tue, 16 Jan 2024 15:06:57 +0100 Subject: [PATCH 054/281] Add Resend Mailer bridge --- DependencyInjection/FrameworkExtension.php | 2 ++ Resources/config/mailer_transports.php | 2 ++ Resources/config/mailer_webhook.php | 7 +++++++ 3 files changed, 11 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 9c9446fb0..cf8bb8dfc 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2572,6 +2572,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co MailerBridge\MailPace\Transport\MailPaceTransportFactory::class => 'mailer.transport_factory.mailpace', MailerBridge\Mailchimp\Transport\MandrillTransportFactory::class => 'mailer.transport_factory.mailchimp', MailerBridge\Postmark\Transport\PostmarkTransportFactory::class => 'mailer.transport_factory.postmark', + MailerBridge\Resend\Transport\ResendTransportFactory::class => 'mailer.transport_factory.resend', MailerBridge\Scaleway\Transport\ScalewayTransportFactory::class => 'mailer.transport_factory.scaleway', MailerBridge\Sendgrid\Transport\SendgridTransportFactory::class => 'mailer.transport_factory.sendgrid', MailerBridge\Amazon\Transport\SesTransportFactory::class => 'mailer.transport_factory.amazon', @@ -2591,6 +2592,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co MailerBridge\Mailgun\Webhook\MailgunRequestParser::class => 'mailer.webhook.request_parser.mailgun', MailerBridge\Mailjet\Webhook\MailjetRequestParser::class => 'mailer.webhook.request_parser.mailjet', MailerBridge\Postmark\Webhook\PostmarkRequestParser::class => 'mailer.webhook.request_parser.postmark', + MailerBridge\Resend\Webhook\ResendRequestParser::class => 'mailer.webhook.request_parser.resend', MailerBridge\Sendgrid\Webhook\SendgridRequestParser::class => 'mailer.webhook.request_parser.sendgrid', ]; diff --git a/Resources/config/mailer_transports.php b/Resources/config/mailer_transports.php index f95fc6d64..5434b4c56 100644 --- a/Resources/config/mailer_transports.php +++ b/Resources/config/mailer_transports.php @@ -22,6 +22,7 @@ use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetTransportFactory; use Symfony\Component\Mailer\Bridge\MailPace\Transport\MailPaceTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; +use Symfony\Component\Mailer\Bridge\Resend\Transport\ResendTransportFactory; use Symfony\Component\Mailer\Bridge\Scaleway\Transport\ScalewayTransportFactory; use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory; use Symfony\Component\Mailer\Transport\AbstractTransportFactory; @@ -55,6 +56,7 @@ 'native' => NativeTransportFactory::class, 'null' => NullTransportFactory::class, 'postmark' => PostmarkTransportFactory::class, + 'resend' => ResendTransportFactory::class, 'scaleway' => ScalewayTransportFactory::class, 'sendgrid' => SendgridTransportFactory::class, 'sendmail' => SendmailTransportFactory::class, diff --git a/Resources/config/mailer_webhook.php b/Resources/config/mailer_webhook.php index bb487b36c..5c1014713 100644 --- a/Resources/config/mailer_webhook.php +++ b/Resources/config/mailer_webhook.php @@ -19,6 +19,8 @@ use Symfony\Component\Mailer\Bridge\Mailjet\Webhook\MailjetRequestParser; use Symfony\Component\Mailer\Bridge\Postmark\RemoteEvent\PostmarkPayloadConverter; use Symfony\Component\Mailer\Bridge\Postmark\Webhook\PostmarkRequestParser; +use Symfony\Component\Mailer\Bridge\Resend\RemoteEvent\ResendPayloadConverter; +use Symfony\Component\Mailer\Bridge\Resend\Webhook\ResendRequestParser; use Symfony\Component\Mailer\Bridge\Sendgrid\RemoteEvent\SendgridPayloadConverter; use Symfony\Component\Mailer\Bridge\Sendgrid\Webhook\SendgridRequestParser; @@ -44,6 +46,11 @@ ->args([service('mailer.payload_converter.postmark')]) ->alias(PostmarkRequestParser::class, 'mailer.webhook.request_parser.postmark') + ->set('mailer.payload_converter.resend', ResendPayloadConverter::class) + ->set('mailer.webhook.request_parser.resend', ResendRequestParser::class) + ->args([service('mailer.payload_converter.resend')]) + ->alias(ResendRequestParser::class, 'mailer.webhook.request_parser.resend') + ->set('mailer.payload_converter.sendgrid', SendgridPayloadConverter::class) ->set('mailer.webhook.request_parser.sendgrid', SendgridRequestParser::class) ->args([service('mailer.payload_converter.sendgrid')]) From 2ce6f817c4fc7cba537cbd80d8a3992558c13b2d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 9 Jan 2024 15:08:59 +0100 Subject: [PATCH 055/281] do not mock the ContainerInterface --- Tests/CacheWarmer/RouterCacheWarmerTest.php | 26 +++++------ Tests/Command/CachePoolClearCommandTest.php | 6 +-- Tests/Command/CachePoolDeleteCommandTest.php | 6 +-- Tests/Command/CachePruneCommandTest.php | 6 +-- Tests/Command/RouterMatchCommandTest.php | 17 +------ Tests/Console/ApplicationTest.php | 47 +++---------------- Tests/Controller/ControllerResolverTest.php | 8 +--- Tests/Routing/RouterTest.php | 48 +++++++------------- Tests/Translation/TranslatorTest.php | 17 +++---- 9 files changed, 51 insertions(+), 130 deletions(-) diff --git a/Tests/CacheWarmer/RouterCacheWarmerTest.php b/Tests/CacheWarmer/RouterCacheWarmerTest.php index 7686b139f..615010a47 100644 --- a/Tests/CacheWarmer/RouterCacheWarmerTest.php +++ b/Tests/CacheWarmer/RouterCacheWarmerTest.php @@ -12,8 +12,8 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\CacheWarmer; use PHPUnit\Framework\TestCase; -use Psr\Container\ContainerInterface; use Symfony\Bundle\FrameworkBundle\CacheWarmer\RouterCacheWarmer; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use Symfony\Component\Routing\RouterInterface; @@ -21,11 +21,11 @@ class RouterCacheWarmerTest extends TestCase { public function testWarmUpWithWarmableInterfaceWithBuildDir() { - $containerMock = $this->getMockBuilder(ContainerInterface::class)->onlyMethods(['get', 'has'])->getMock(); + $container = new Container(); $routerMock = $this->getMockBuilder(testRouterInterfaceWithWarmableInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection', 'warmUp'])->getMock(); - $containerMock->expects($this->any())->method('get')->with('router')->willReturn($routerMock); - $routerCacheWarmer = new RouterCacheWarmer($containerMock); + $container->set('router', $routerMock); + $routerCacheWarmer = new RouterCacheWarmer($container); $routerCacheWarmer->warmUp('/tmp/cache', '/tmp/build'); $routerMock->expects($this->any())->method('warmUp')->with('/tmp/cache', '/tmp/build')->willReturn([]); @@ -34,11 +34,11 @@ public function testWarmUpWithWarmableInterfaceWithBuildDir() public function testWarmUpWithoutWarmableInterfaceWithBuildDir() { - $containerMock = $this->getMockBuilder(ContainerInterface::class)->onlyMethods(['get', 'has'])->getMock(); + $container = new Container(); $routerMock = $this->getMockBuilder(testRouterInterfaceWithoutWarmableInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection'])->getMock(); - $containerMock->expects($this->any())->method('get')->with('router')->willReturn($routerMock); - $routerCacheWarmer = new RouterCacheWarmer($containerMock); + $container->set('router', $routerMock); + $routerCacheWarmer = new RouterCacheWarmer($container); $this->expectException(\LogicException::class); $this->expectExceptionMessage('cannot be warmed up because it does not implement "Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface"'); $routerCacheWarmer->warmUp('/tmp/cache', '/tmp/build'); @@ -46,11 +46,11 @@ public function testWarmUpWithoutWarmableInterfaceWithBuildDir() public function testWarmUpWithWarmableInterfaceWithoutBuildDir() { - $containerMock = $this->getMockBuilder(ContainerInterface::class)->onlyMethods(['get', 'has'])->getMock(); + $container = new Container(); $routerMock = $this->getMockBuilder(testRouterInterfaceWithWarmableInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection', 'warmUp'])->getMock(); - $containerMock->expects($this->any())->method('get')->with('router')->willReturn($routerMock); - $routerCacheWarmer = new RouterCacheWarmer($containerMock); + $container->set('router', $routerMock); + $routerCacheWarmer = new RouterCacheWarmer($container); $preload = $routerCacheWarmer->warmUp('/tmp'); $routerMock->expects($this->never())->method('warmUp'); @@ -60,11 +60,11 @@ public function testWarmUpWithWarmableInterfaceWithoutBuildDir() public function testWarmUpWithoutWarmableInterfaceWithoutBuildDir() { - $containerMock = $this->getMockBuilder(ContainerInterface::class)->onlyMethods(['get', 'has'])->getMock(); + $container = new Container(); $routerMock = $this->getMockBuilder(testRouterInterfaceWithoutWarmableInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection'])->getMock(); - $containerMock->expects($this->any())->method('get')->with('router')->willReturn($routerMock); - $routerCacheWarmer = new RouterCacheWarmer($containerMock); + $container->set('router', $routerMock); + $routerCacheWarmer = new RouterCacheWarmer($container); $preload = $routerCacheWarmer->warmUp('/tmp'); self::assertSame([], $preload); } diff --git a/Tests/Command/CachePoolClearCommandTest.php b/Tests/Command/CachePoolClearCommandTest.php index fb7358831..3a927f217 100644 --- a/Tests/Command/CachePoolClearCommandTest.php +++ b/Tests/Command/CachePoolClearCommandTest.php @@ -17,7 +17,7 @@ use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Component\Console\Tester\CommandCompletionTester; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer; use Symfony\Component\HttpKernel\KernelInterface; @@ -54,13 +54,11 @@ public static function provideCompletionSuggestions(): iterable private function getKernel(): MockObject&KernelInterface { - $container = $this->createMock(ContainerInterface::class); - $kernel = $this->createMock(KernelInterface::class); $kernel ->expects($this->any()) ->method('getContainer') - ->willReturn($container); + ->willReturn(new Container()); $kernel ->expects($this->once()) diff --git a/Tests/Command/CachePoolDeleteCommandTest.php b/Tests/Command/CachePoolDeleteCommandTest.php index caa7eb550..3db39e121 100644 --- a/Tests/Command/CachePoolDeleteCommandTest.php +++ b/Tests/Command/CachePoolDeleteCommandTest.php @@ -18,7 +18,7 @@ use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Component\Console\Tester\CommandCompletionTester; use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer; use Symfony\Component\HttpKernel\KernelInterface; @@ -108,13 +108,11 @@ public static function provideCompletionSuggestions(): iterable private function getKernel(): MockObject&KernelInterface { - $container = $this->createMock(ContainerInterface::class); - $kernel = $this->createMock(KernelInterface::class); $kernel ->expects($this->any()) ->method('getContainer') - ->willReturn($container); + ->willReturn(new Container()); $kernel ->expects($this->once()) diff --git a/Tests/Command/CachePruneCommandTest.php b/Tests/Command/CachePruneCommandTest.php index 54467f1ef..a2d0ad7fe 100644 --- a/Tests/Command/CachePruneCommandTest.php +++ b/Tests/Command/CachePruneCommandTest.php @@ -18,7 +18,7 @@ use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpKernel\KernelInterface; class CachePruneCommandTest extends TestCase @@ -50,13 +50,11 @@ private function getEmptyRewindableGenerator(): RewindableGenerator private function getKernel(): MockObject&KernelInterface { - $container = $this->createMock(ContainerInterface::class); - $kernel = $this->createMock(KernelInterface::class); $kernel ->expects($this->any()) ->method('getContainer') - ->willReturn($container); + ->willReturn(new Container()); $kernel ->expects($this->once()) diff --git a/Tests/Command/RouterMatchCommandTest.php b/Tests/Command/RouterMatchCommandTest.php index 7dab41991..b6b6771f9 100644 --- a/Tests/Command/RouterMatchCommandTest.php +++ b/Tests/Command/RouterMatchCommandTest.php @@ -16,7 +16,7 @@ use Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Route; @@ -72,24 +72,11 @@ private function getRouter() private function getKernel() { - $container = $this->createMock(ContainerInterface::class); - $container - ->expects($this->atLeastOnce()) - ->method('has') - ->willReturnCallback(fn ($id) => 'console.command_loader' !== $id) - ; - $container - ->expects($this->any()) - ->method('get') - ->with('router') - ->willReturn($this->getRouter()) - ; - $kernel = $this->createMock(KernelInterface::class); $kernel ->expects($this->any()) ->method('getContainer') - ->willReturn($container) + ->willReturn(new Container()) ; $kernel ->expects($this->once()) diff --git a/Tests/Console/ApplicationTest.php b/Tests/Console/ApplicationTest.php index 42691c75b..9afb5a2fd 100644 --- a/Tests/Console/ApplicationTest.php +++ b/Tests/Console/ApplicationTest.php @@ -22,11 +22,11 @@ use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Tester\ApplicationTester; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\HttpKernel\Bundle\BundleInterface; use Symfony\Component\HttpKernel\KernelInterface; @@ -241,7 +241,10 @@ private function createEventForSuggestingPackages(string $command, array $altern private function getKernel(array $bundles, $useDispatcher = false) { - $container = $this->createMock(ContainerInterface::class); + $container = new Container(new ParameterBag([ + 'console.command.ids' => [], + 'console.lazy_command.ids' => [], + ])); if ($useDispatcher) { $dispatcher = $this->createMock(EventDispatcherInterface::class); @@ -250,45 +253,9 @@ private function getKernel(array $bundles, $useDispatcher = false) ->method('dispatch') ; - $container->expects($this->atLeastOnce()) - ->method('get') - ->willReturnMap([ - ['.virtual_request_stack', 2, new RequestStack()], - ['event_dispatcher', 1, $dispatcher], - ]) - ; + $container->set('event_dispatcher', $dispatcher); } - $container - ->expects($this->exactly(2)) - ->method('hasParameter') - ->willReturnCallback(function (...$args) { - static $series = [ - ['console.command.ids'], - ['console.lazy_command.ids'], - ]; - - $this->assertSame(array_shift($series), $args); - - return true; - }) - ; - - $container - ->expects($this->exactly(2)) - ->method('getParameter') - ->willReturnCallback(function (...$args) { - static $series = [ - ['console.lazy_command.ids'], - ['console.command.ids'], - ]; - - $this->assertSame(array_shift($series), $args); - - return []; - }) - ; - $kernel = $this->createMock(KernelInterface::class); $kernel->expects($this->once())->method('boot'); $kernel diff --git a/Tests/Controller/ControllerResolverTest.php b/Tests/Controller/ControllerResolverTest.php index 0c51924a4..0c03dc460 100644 --- a/Tests/Controller/ControllerResolverTest.php +++ b/Tests/Controller/ControllerResolverTest.php @@ -16,7 +16,6 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver; use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Tests\Controller\ContainerControllerResolverTest; @@ -107,7 +106,7 @@ class_exists(AbstractControllerTest::class); protected function createControllerResolver(LoggerInterface $logger = null, Psr11ContainerInterface $container = null) { if (!$container) { - $container = $this->createMockContainer(); + $container = new Container(); } return new ControllerResolver($container, $logger); @@ -117,11 +116,6 @@ protected function createMockParser() { return $this->createMock(ControllerNameParser::class); } - - protected function createMockContainer() - { - return $this->createMock(ContainerInterface::class); - } } class DummyController extends AbstractController diff --git a/Tests/Routing/RouterTest.php b/Tests/Routing/RouterTest.php index 3e185b54c..11c0dc7e6 100644 --- a/Tests/Routing/RouterTest.php +++ b/Tests/Routing/RouterTest.php @@ -23,6 +23,8 @@ use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\ParameterBag\ContainerBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\Routing\Loader\YamlFileLoader; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; @@ -406,7 +408,8 @@ public function testExceptionOnNonStringParameter() $routes->add('foo', new Route('/%object%')); $sc = $this->getPsr11ServiceContainer($routes); - $parameters = $this->getParameterBag(['object' => new \stdClass()]); + $parameters = new Container(); + $parameters->set('object', new \stdClass()); $router = new Router($sc, 'foo', [], null, $parameters); @@ -424,19 +427,15 @@ public function testExceptionOnNonStringParameterWithSfContainer() $sc = $this->getServiceContainer($routes); - $pc = $this->createMock(ContainerInterface::class); - $pc - ->expects($this->once()) - ->method('get') - ->willReturn(new \stdClass()) - ; + $pc = new Container(); + $pc->set('object', new \stdClass()); $router = new Router($sc, 'foo', [], null, $pc); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The container parameter "object", used in the route configuration value "/%object%", must be a string or numeric, but it is of type "stdClass".'); - $router->getRouteCollection()->get('foo'); + $router->getRouteCollection(); } /** @@ -483,7 +482,9 @@ public function testGetRouteCollectionAddsContainerParametersResource() $router = new Router($sc, 'foo', [], null, $parameters); - $router->getRouteCollection(); + $routeCollection = $router->getRouteCollection(); + + $this->assertEquals([new ContainerParametersResource(['locale' => 'en'])], $routeCollection->getResources()); } public function testGetRouteCollectionAddsContainerParametersResourceWithSfContainer() @@ -617,13 +618,8 @@ private function getServiceContainer(RouteCollection $routes): Container ->willReturn($routes) ; - $sc = $this->getMockBuilder(Container::class)->onlyMethods(['get'])->getMock(); - - $sc - ->expects($this->once()) - ->method('get') - ->willReturn($loader) - ; + $sc = new Container(); + $sc->set('routing.loader', $loader); return $sc; } @@ -638,26 +634,14 @@ private function getPsr11ServiceContainer(RouteCollection $routes): ContainerInt ->willReturn($routes) ; - $sc = $this->createMock(ContainerInterface::class); - - $sc - ->expects($this->once()) - ->method('get') - ->willReturn($loader) - ; + $container = new Container(); + $container->set('routing.loader', $loader); - return $sc; + return $container; } private function getParameterBag(array $params = []): ContainerInterface { - $bag = $this->createMock(ContainerInterface::class); - $bag - ->expects($this->any()) - ->method('get') - ->willReturnCallback(fn ($key) => $params[$key] ?? null) - ; - - return $bag; + return new ContainerBag(new Container(new ParameterBag($params))); } } diff --git a/Tests/Translation/TranslatorTest.php b/Tests/Translation/TranslatorTest.php index 1c8efd9b1..ff15a6d1d 100644 --- a/Tests/Translation/TranslatorTest.php +++ b/Tests/Translation/TranslatorTest.php @@ -15,7 +15,7 @@ use Symfony\Bundle\FrameworkBundle\Translation\Translator; use Symfony\Component\Config\Resource\DirectoryResource; use Symfony\Component\Config\Resource\FileExistenceResource; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Formatter\MessageFormatter; @@ -117,8 +117,7 @@ public function testLoadResourcesWithoutCaching() public function testGetDefaultLocale() { - $container = $this->createMock(\Psr\Container\ContainerInterface::class); - $translator = new Translator($container, new MessageFormatter(), 'en'); + $translator = new Translator(new Container(), new MessageFormatter(), 'en'); $this->assertSame('en', $translator->getLocale()); } @@ -127,9 +126,8 @@ public function testInvalidOptions() { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('The Translator does not support the following options: \'foo\''); - $container = $this->createMock(ContainerInterface::class); - new Translator($container, new MessageFormatter(), 'en', [], ['foo' => 'bar']); + new Translator(new Container(), new MessageFormatter(), 'en', [], ['foo' => 'bar']); } /** @dataProvider getDebugModeAndCacheDirCombinations */ @@ -294,12 +292,9 @@ protected function getLoader() protected function getContainer($loader) { - $container = $this->createMock(ContainerInterface::class); - $container - ->expects($this->any()) - ->method('get') - ->willReturn($loader) - ; + $container = new Container(); + $container->set('loader', $loader); + $container->set('yml', $loader); return $container; } From 14b251e9110b838c9798e2757f1e473a8f478ad6 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Tue, 16 Jan 2024 01:52:38 +0100 Subject: [PATCH 056/281] [FrameworkBundle] Remove redundant check from test --- Tests/DependencyInjection/FrameworkExtensionTestCase.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 0d97dcf62..8247d3fa7 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -38,7 +38,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; @@ -1930,9 +1929,6 @@ public function testHttpClientOverrideDefaultOptions() public function testHttpClientRetry() { - if (!class_exists(RetryableHttpClient::class)) { - $this->expectException(LogicException::class); - } $container = $this->createContainerFromFile('http_client_retry'); $this->assertSame([429, 500 => ['GET', 'HEAD']], $container->getDefinition('http_client.retry_strategy')->getArgument(0)); From a6b88545b54ab2cc0b09f40aeacfcf408449806b Mon Sep 17 00:00:00 2001 From: HypeMC Date: Mon, 29 Jan 2024 10:12:01 +0100 Subject: [PATCH 057/281] [FrameworkBundle] Remove redundant `name` attribute from `default_context` --- DependencyInjection/Configuration.php | 1 - 1 file changed, 1 deletion(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 7547b192f..1a9916ed7 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1116,7 +1116,6 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $e ->end() ->arrayNode('default_context') ->normalizeKeys(false) - ->useAttributeAsKey('name') ->beforeNormalization() ->ifTrue(fn () => $this->debug && class_exists(JsonParser::class)) ->then(fn (array $v) => $v + [JsonDecode::DETAILED_ERROR_MESSAGES => true]) From 8f2a6982e8e0b205413ff97205b9d25adc20029b Mon Sep 17 00:00:00 2001 From: Ramunas Pabreza Date: Fri, 19 Jan 2024 12:11:02 +0200 Subject: [PATCH 058/281] Mailersend webhook remote event --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/mailer_webhook.php | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index cf8bb8dfc..095d782aa 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2589,6 +2589,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co if ($webhookEnabled) { $webhookRequestParsers = [ MailerBridge\Brevo\Webhook\BrevoRequestParser::class => 'mailer.webhook.request_parser.brevo', + MailerBridge\MailerSend\Webhook\MailerSendRequestParser::class => 'mailer.webhook.request_parser.mailersend', MailerBridge\Mailgun\Webhook\MailgunRequestParser::class => 'mailer.webhook.request_parser.mailgun', MailerBridge\Mailjet\Webhook\MailjetRequestParser::class => 'mailer.webhook.request_parser.mailjet', MailerBridge\Postmark\Webhook\PostmarkRequestParser::class => 'mailer.webhook.request_parser.postmark', diff --git a/Resources/config/mailer_webhook.php b/Resources/config/mailer_webhook.php index 5c1014713..f9d2b9686 100644 --- a/Resources/config/mailer_webhook.php +++ b/Resources/config/mailer_webhook.php @@ -13,6 +13,8 @@ use Symfony\Component\Mailer\Bridge\Brevo\RemoteEvent\BrevoPayloadConverter; use Symfony\Component\Mailer\Bridge\Brevo\Webhook\BrevoRequestParser; +use Symfony\Component\Mailer\Bridge\MailerSend\RemoteEvent\MailerSendPayloadConverter; +use Symfony\Component\Mailer\Bridge\MailerSend\Webhook\MailerSendRequestParser; use Symfony\Component\Mailer\Bridge\Mailgun\RemoteEvent\MailgunPayloadConverter; use Symfony\Component\Mailer\Bridge\Mailgun\Webhook\MailgunRequestParser; use Symfony\Component\Mailer\Bridge\Mailjet\RemoteEvent\MailjetPayloadConverter; @@ -31,6 +33,11 @@ ->args([service('mailer.payload_converter.brevo')]) ->alias(BrevoRequestParser::class, 'mailer.webhook.request_parser.brevo') + ->set('mailer.payload_converter.mailersend', MailerSendPayloadConverter::class) + ->set('mailer.webhook.request_parser.mailersend', MailerSendRequestParser::class) + ->args([service('mailer.payload_converter.mailersend')]) + ->alias(MailerSendRequestParser::class, 'mailer.webhook.request_parser.mailersend') + ->set('mailer.payload_converter.mailgun', MailgunPayloadConverter::class) ->set('mailer.webhook.request_parser.mailgun', MailgunRequestParser::class) ->args([service('mailer.payload_converter.mailgun')]) From f96384a74ad810fa702b896010d38c78622744d3 Mon Sep 17 00:00:00 2001 From: Daniel Burger <48986191+danielburger1337@users.noreply.github.com> Date: Tue, 9 Jan 2024 08:02:56 +0100 Subject: [PATCH 059/281] Add `SecretsRevealCommand` --- CHANGELOG.md | 1 + Command/SecretsRevealCommand.php | 72 ++++++++++++++++++ DependencyInjection/FrameworkExtension.php | 1 + Resources/config/console.php | 8 ++ Tests/Command/SecretsRevealCommandTest.php | 86 ++++++++++++++++++++++ 5 files changed, 168 insertions(+) create mode 100644 Command/SecretsRevealCommand.php create mode 100644 Tests/Command/SecretsRevealCommandTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 43d7f6295..cc2151d6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ CHANGELOG * Move the Router `cache_dir` to `kernel.build_dir` * Deprecate the `router.cache_dir` config option * Add `rate_limiter` tags to rate limiter services + * Add `secrets:reveal` command 7.0 --- diff --git a/Command/SecretsRevealCommand.php b/Command/SecretsRevealCommand.php new file mode 100644 index 000000000..bcbdea11f --- /dev/null +++ b/Command/SecretsRevealCommand.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * @internal + */ +#[AsCommand(name: 'secrets:reveal', description: 'Reveal the value of a secret')] +final class SecretsRevealCommand extends Command +{ + public function __construct( + private readonly AbstractVault $vault, + private readonly ?AbstractVault $localVault = null, + ) { + parent::__construct(); + } + + protected function configure(): void + { + $this + ->addArgument('name', InputArgument::REQUIRED, 'The name of the secret to reveal', null, fn () => array_keys($this->vault->list())) + ->setHelp(<<<'EOF' +The %command.name% command reveals a stored secret. + + %command.full_name% +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output); + + $secrets = $this->vault->list(true); + $localSecrets = $this->localVault?->list(true); + + $name = (string) $input->getArgument('name'); + + if (null !== $localSecrets && \array_key_exists($name, $localSecrets)) { + $io->writeln($localSecrets[$name]); + } else { + if (!\array_key_exists($name, $secrets)) { + $io->error(sprintf('The secret "%s" does not exist.', $name)); + + return self::INVALID; + } + + $io->writeln($secrets[$name]); + } + + return self::SUCCESS; + } +} diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 095d782aa..38de7421d 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -1773,6 +1773,7 @@ private function registerSecretsConfiguration(array $config, ContainerBuilder $c if (!$this->readConfigEnabled('secrets', $container, $config)) { $container->removeDefinition('console.command.secrets_set'); $container->removeDefinition('console.command.secrets_list'); + $container->removeDefinition('console.command.secrets_reveal'); $container->removeDefinition('console.command.secrets_remove'); $container->removeDefinition('console.command.secrets_generate_key'); $container->removeDefinition('console.command.secrets_decrypt_to_local'); diff --git a/Resources/config/console.php b/Resources/config/console.php index 334d20426..b4f7dfcf3 100644 --- a/Resources/config/console.php +++ b/Resources/config/console.php @@ -33,6 +33,7 @@ use Symfony\Bundle\FrameworkBundle\Command\SecretsGenerateKeysCommand; use Symfony\Bundle\FrameworkBundle\Command\SecretsListCommand; use Symfony\Bundle\FrameworkBundle\Command\SecretsRemoveCommand; +use Symfony\Bundle\FrameworkBundle\Command\SecretsRevealCommand; use Symfony\Bundle\FrameworkBundle\Command\SecretsSetCommand; use Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand; use Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand; @@ -355,6 +356,13 @@ ]) ->tag('console.command') + ->set('console.command.secrets_reveal', SecretsRevealCommand::class) + ->args([ + service('secrets.vault'), + service('secrets.local_vault')->ignoreOnInvalid(), + ]) + ->tag('console.command') + ->set('console.command.secrets_decrypt_to_local', SecretsDecryptToLocalCommand::class) ->args([ service('secrets.vault'), diff --git a/Tests/Command/SecretsRevealCommandTest.php b/Tests/Command/SecretsRevealCommandTest.php new file mode 100644 index 000000000..94643db2c --- /dev/null +++ b/Tests/Command/SecretsRevealCommandTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Command\SecretsRevealCommand; +use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault; +use Symfony\Bundle\FrameworkBundle\Secrets\DotenvVault; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Tester\CommandTester; + +class SecretsRevealCommandTest extends TestCase +{ + public function testExecute() + { + $vault = $this->createMock(AbstractVault::class); + $vault->method('list')->willReturn(['secretKey' => 'secretValue']); + + $command = new SecretsRevealCommand($vault); + + $tester = new CommandTester($command); + $this->assertSame(Command::SUCCESS, $tester->execute(['name' => 'secretKey'])); + + $this->assertEquals('secretValue', trim($tester->getDisplay(true))); + } + + public function testInvalidName() + { + $vault = $this->createMock(AbstractVault::class); + $vault->method('list')->willReturn(['secretKey' => 'secretValue']); + + $command = new SecretsRevealCommand($vault); + + $tester = new CommandTester($command); + $this->assertSame(Command::INVALID, $tester->execute(['name' => 'undefinedKey'])); + + $this->assertStringContainsString('The secret "undefinedKey" does not exist.', trim($tester->getDisplay(true))); + } + + /** + * @backupGlobals enabled + */ + public function testLocalVaultOverride() + { + $vault = $this->createMock(AbstractVault::class); + $vault->method('list')->willReturn(['secretKey' => 'secretValue']); + + $_ENV = ['secretKey' => 'newSecretValue']; + $localVault = new DotenvVault('/not/a/path'); + + $command = new SecretsRevealCommand($vault, $localVault); + + $tester = new CommandTester($command); + $this->assertSame(Command::SUCCESS, $tester->execute(['name' => 'secretKey'])); + + $this->assertEquals('newSecretValue', trim($tester->getDisplay(true))); + } + + /** + * @backupGlobals enabled + */ + public function testOnlyLocalVaultContainsName() + { + $vault = $this->createMock(AbstractVault::class); + $vault->method('list')->willReturn(['otherKey' => 'secretValue']); + + $_ENV = ['secretKey' => 'secretValue']; + $localVault = new DotenvVault('/not/a/path'); + + $command = new SecretsRevealCommand($vault, $localVault); + + $tester = new CommandTester($command); + $this->assertSame(Command::SUCCESS, $tester->execute(['name' => 'secretKey'])); + + $this->assertEquals('secretValue', trim($tester->getDisplay(true))); + } +} From ffcf3feb54e335efec2acb3f0a9dea0e57015124 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Tue, 16 Jan 2024 13:02:04 +0100 Subject: [PATCH 060/281] [FrameworkBundle][HttpClient] Add `ThrottlingHttpClient` to limit requests within a timeframe --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 35 +++++++++++++--- DependencyInjection/FrameworkExtension.php | 33 +++++++++++++++ Resources/config/schema/symfony-1.0.xsd | 2 + .../DependencyInjection/ConfigurationTest.php | 40 +++++++++++++++++++ .../Fixtures/php/http_client_rate_limiter.php | 27 +++++++++++++ .../Fixtures/xml/http_client_rate_limiter.xml | 21 ++++++++++ .../Fixtures/yml/http_client_rate_limiter.yml | 19 +++++++++ .../FrameworkExtensionTestCase.php | 31 ++++++++++++++ 9 files changed, 203 insertions(+), 6 deletions(-) create mode 100644 Tests/DependencyInjection/Fixtures/php/http_client_rate_limiter.php create mode 100644 Tests/DependencyInjection/Fixtures/xml/http_client_rate_limiter.xml create mode 100644 Tests/DependencyInjection/Fixtures/yml/http_client_rate_limiter.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index cc2151d6a..6fe797e3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * Deprecate the `router.cache_dir` config option * Add `rate_limiter` tags to rate limiter services * Add `secrets:reveal` command + * Add `rate_limiter` option to `http_client.default_options` and `http_client.scoped_clients` 7.0 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 1a9916ed7..c5efbc30f 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1715,17 +1715,32 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e ->fixXmlConfig('scoped_client') ->beforeNormalization() ->always(function ($config) { - if (empty($config['scoped_clients']) || !\is_array($config['default_options']['retry_failed'] ?? null)) { + if (empty($config['scoped_clients'])) { + return $config; + } + + $hasDefaultRateLimiter = isset($config['default_options']['rate_limiter']); + $hasDefaultRetryFailed = \is_array($config['default_options']['retry_failed'] ?? null); + + if (!$hasDefaultRateLimiter && !$hasDefaultRetryFailed) { return $config; } foreach ($config['scoped_clients'] as &$scopedConfig) { - if (!isset($scopedConfig['retry_failed']) || true === $scopedConfig['retry_failed']) { - $scopedConfig['retry_failed'] = $config['default_options']['retry_failed']; - continue; + if ($hasDefaultRateLimiter) { + if (!isset($scopedConfig['rate_limiter']) || true === $scopedConfig['rate_limiter']) { + $scopedConfig['rate_limiter'] = $config['default_options']['rate_limiter']; + } elseif (false === $scopedConfig['rate_limiter']) { + $scopedConfig['rate_limiter'] = null; + } } - if (\is_array($scopedConfig['retry_failed'])) { - $scopedConfig['retry_failed'] += $config['default_options']['retry_failed']; + + if ($hasDefaultRetryFailed) { + if (!isset($scopedConfig['retry_failed']) || true === $scopedConfig['retry_failed']) { + $scopedConfig['retry_failed'] = $config['default_options']['retry_failed']; + } elseif (\is_array($scopedConfig['retry_failed'])) { + $scopedConfig['retry_failed'] += $config['default_options']['retry_failed']; + } } } @@ -1830,6 +1845,10 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e ->normalizeKeys(false) ->variablePrototype()->end() ->end() + ->scalarNode('rate_limiter') + ->defaultNull() + ->info('Rate limiter name to use for throttling requests') + ->end() ->append($this->createHttpClientRetrySection()) ->end() ->end() @@ -1978,6 +1997,10 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e ->normalizeKeys(false) ->variablePrototype()->end() ->end() + ->scalarNode('rate_limiter') + ->defaultNull() + ->info('Rate limiter name to use for throttling requests') + ->end() ->append($this->createHttpClientRetrySection()) ->end() ->end() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 38de7421d..ff525bb5e 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -85,6 +85,7 @@ use Symfony\Component\HttpClient\Retry\GenericRetryStrategy; use Symfony\Component\HttpClient\RetryableHttpClient; use Symfony\Component\HttpClient\ScopingHttpClient; +use Symfony\Component\HttpClient\ThrottlingHttpClient; use Symfony\Component\HttpClient\UriTemplateHttpClient; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -345,6 +346,7 @@ public function load(array $configs, ContainerBuilder $container): void } if ($this->readConfigEnabled('http_client', $container, $config['http_client'])) { + $this->readConfigEnabled('rate_limiter', $container, $config['rate_limiter']); // makes sure that isInitializedConfigEnabled() will work $this->registerHttpClientConfiguration($config['http_client'], $container, $loader); } @@ -2409,6 +2411,8 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder $loader->load('http_client.php'); $options = $config['default_options'] ?? []; + $rateLimiter = $options['rate_limiter'] ?? null; + unset($options['rate_limiter']); $retryOptions = $options['retry_failed'] ?? ['enabled' => false]; unset($options['retry_failed']); $defaultUriTemplateVars = $options['vars'] ?? []; @@ -2430,6 +2434,10 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder $container->removeAlias(HttpClient::class); } + if (null !== $rateLimiter) { + $this->registerThrottlingHttpClient($rateLimiter, 'http_client', $container); + } + if ($this->readConfigEnabled('http_client.retry_failed', $container, $retryOptions)) { $this->registerRetryableHttpClient($retryOptions, 'http_client', $container); } @@ -2451,6 +2459,8 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder $scope = $scopeConfig['scope'] ?? null; unset($scopeConfig['scope']); + $rateLimiter = $scopeConfig['rate_limiter'] ?? null; + unset($scopeConfig['rate_limiter']); $retryOptions = $scopeConfig['retry_failed'] ?? ['enabled' => false]; unset($scopeConfig['retry_failed']); @@ -2470,6 +2480,10 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder ; } + if (null !== $rateLimiter) { + $this->registerThrottlingHttpClient($rateLimiter, $name, $container); + } + if ($this->readConfigEnabled('http_client.scoped_clients.'.$name.'.retry_failed', $container, $retryOptions)) { $this->registerRetryableHttpClient($retryOptions, $name, $container); } @@ -2507,6 +2521,25 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder } } + private function registerThrottlingHttpClient(string $rateLimiter, string $name, ContainerBuilder $container): void + { + if (!class_exists(ThrottlingHttpClient::class)) { + throw new LogicException('Rate limiter support cannot be enabled as version 7.1+ of the HttpClient component is required.'); + } + + if (!$this->isInitializedConfigEnabled('rate_limiter')) { + throw new LogicException('Rate limiter cannot be used within HttpClient as the RateLimiter component is not enabled.'); + } + + $container->register($name.'.throttling.limiter', LimiterInterface::class) + ->setFactory([new Reference('limiter.'.$rateLimiter), 'create']); + + $container + ->register($name.'.throttling', ThrottlingHttpClient::class) + ->setDecoratedService($name, null, 15) // higher priority than RetryableHttpClient (10) + ->setArguments([new Reference($name.'.throttling.inner'), new Reference($name.'.throttling.limiter')]); + } + private function registerRetryableHttpClient(array $options, string $name, ContainerBuilder $container): void { if (null !== $options['retry_strategy']) { diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index 9279eaf68..8ddadcc78 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -661,6 +661,7 @@ + @@ -691,6 +692,7 @@ + diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index d56cfa90d..0c80a6f10 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -530,6 +530,46 @@ public function testEnabledLockNeedsResources() ]); } + public function testScopedHttpClientsInheritRateLimiterAndRetryFailedConfiguration() + { + $processor = new Processor(); + $configuration = new Configuration(true); + + $config = $processor->processConfiguration($configuration, [[ + 'http_client' => [ + 'default_options' => ['rate_limiter' => 'default_limiter', 'retry_failed' => ['max_retries' => 77]], + 'scoped_clients' => [ + 'foo' => ['base_uri' => 'http://example.com'], + 'bar' => ['base_uri' => 'http://example.com', 'rate_limiter' => true, 'retry_failed' => true], + 'baz' => ['base_uri' => 'http://example.com', 'rate_limiter' => false, 'retry_failed' => false], + 'qux' => ['base_uri' => 'http://example.com', 'rate_limiter' => 'foo_limiter', 'retry_failed' => ['max_retries' => 88, 'delay' => 999]], + ], + ], + ]]); + + $scopedClients = $config['http_client']['scoped_clients']; + + $this->assertSame('default_limiter', $scopedClients['foo']['rate_limiter']); + $this->assertTrue($scopedClients['foo']['retry_failed']['enabled']); + $this->assertSame(77, $scopedClients['foo']['retry_failed']['max_retries']); + $this->assertSame(1000, $scopedClients['foo']['retry_failed']['delay']); + + $this->assertSame('default_limiter', $scopedClients['bar']['rate_limiter']); + $this->assertTrue($scopedClients['bar']['retry_failed']['enabled']); + $this->assertSame(77, $scopedClients['bar']['retry_failed']['max_retries']); + $this->assertSame(1000, $scopedClients['bar']['retry_failed']['delay']); + + $this->assertNull($scopedClients['baz']['rate_limiter']); + $this->assertFalse($scopedClients['baz']['retry_failed']['enabled']); + $this->assertSame(3, $scopedClients['baz']['retry_failed']['max_retries']); + $this->assertSame(1000, $scopedClients['baz']['retry_failed']['delay']); + + $this->assertSame('foo_limiter', $scopedClients['qux']['rate_limiter']); + $this->assertTrue($scopedClients['qux']['retry_failed']['enabled']); + $this->assertSame(88, $scopedClients['qux']['retry_failed']['max_retries']); + $this->assertSame(999, $scopedClients['qux']['retry_failed']['delay']); + } + protected static function getBundleDefaultConfig() { return [ diff --git a/Tests/DependencyInjection/Fixtures/php/http_client_rate_limiter.php b/Tests/DependencyInjection/Fixtures/php/http_client_rate_limiter.php new file mode 100644 index 000000000..c8256d913 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/http_client_rate_limiter.php @@ -0,0 +1,27 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'rate_limiter' => [ + 'foo_limiter' => [ + 'lock_factory' => null, + 'policy' => 'token_bucket', + 'limit' => 10, + 'rate' => ['interval' => '5 seconds', 'amount' => 10], + ], + ], + 'http_client' => [ + 'default_options' => [ + 'rate_limiter' => 'default_limiter', + ], + 'scoped_clients' => [ + 'foo' => [ + 'base_uri' => 'http://example.com', + 'rate_limiter' => 'foo_limiter', + ], + ], + ], +]); diff --git a/Tests/DependencyInjection/Fixtures/xml/http_client_rate_limiter.xml b/Tests/DependencyInjection/Fixtures/xml/http_client_rate_limiter.xml new file mode 100644 index 000000000..8c9dbcdad --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/http_client_rate_limiter.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + diff --git a/Tests/DependencyInjection/Fixtures/yml/http_client_rate_limiter.yml b/Tests/DependencyInjection/Fixtures/yml/http_client_rate_limiter.yml new file mode 100644 index 000000000..6376192b7 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/http_client_rate_limiter.yml @@ -0,0 +1,19 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + rate_limiter: + foo_limiter: + lock_factory: null + policy: token_bucket + limit: 10 + rate: { interval: '5 seconds', amount: 10 } + http_client: + default_options: + rate_limiter: default_limiter + scoped_clients: + foo: + base_uri: http://example.com + rate_limiter: foo_limiter diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 3d1e9c29b..7cde707c5 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -38,6 +38,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; @@ -51,6 +52,7 @@ use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\RetryableHttpClient; use Symfony\Component\HttpClient\ScopingHttpClient; +use Symfony\Component\HttpClient\ThrottlingHttpClient; use Symfony\Component\HttpFoundation\IpUtils; use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass; use Symfony\Component\HttpKernel\Fragment\FragmentUriGeneratorInterface; @@ -1986,6 +1988,35 @@ public function testHttpClientFullDefaultOptions() $this->assertSame(['foo' => ['bar' => 'baz']], $defaultOptions['extra']); } + public function testHttpClientRateLimiter() + { + if (!class_exists(ThrottlingHttpClient::class)) { + $this->expectException(LogicException::class); + } + + $container = $this->createContainerFromFile('http_client_rate_limiter'); + + $this->assertTrue($container->hasDefinition('http_client.throttling')); + $definition = $container->getDefinition('http_client.throttling'); + $this->assertSame(ThrottlingHttpClient::class, $definition->getClass()); + $this->assertSame('http_client', $definition->getDecoratedService()[0]); + $this->assertCount(2, $arguments = $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $arguments[0]); + $this->assertSame('http_client.throttling.inner', (string) $arguments[0]); + $this->assertInstanceOf(Reference::class, $arguments[1]); + $this->assertSame('http_client.throttling.limiter', (string) $arguments[1]); + + $this->assertTrue($container->hasDefinition('foo.throttling')); + $definition = $container->getDefinition('foo.throttling'); + $this->assertSame(ThrottlingHttpClient::class, $definition->getClass()); + $this->assertSame('foo', $definition->getDecoratedService()[0]); + $this->assertCount(2, $arguments = $definition->getArguments()); + $this->assertInstanceOf(Reference::class, $arguments[0]); + $this->assertSame('foo.throttling.inner', (string) $arguments[0]); + $this->assertInstanceOf(Reference::class, $arguments[1]); + $this->assertSame('foo.throttling.limiter', (string) $arguments[1]); + } + public static function provideMailer(): array { return [ From e16613811d4b0f9d7a78750d910e0e5b8eccc2dd Mon Sep 17 00:00:00 2001 From: Nicolas Valverde <64469669+n-valverde@users.noreply.github.com> Date: Thu, 22 Jun 2023 15:26:14 +0200 Subject: [PATCH 061/281] [DependencyInjection] Add `CheckAliasValidityPass` to check interface compatibility --- CHANGELOG.md | 1 + Command/ContainerLintCommand.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fe797e3a..950c2b8c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.1 --- + * Add `CheckAliasValidityPass` to `lint:container` command * Add `private_ranges` as a shortcut for private IP address ranges to the `trusted_proxies` option * Mark classes `ConfigBuilderCacheWarmer`, `Router`, `SerializerCacheWarmer`, `TranslationsCacheWarmer`, `Translator` and `ValidatorCacheWarmer` as `final` * Move the Router `cache_dir` to `kernel.build_dir` diff --git a/Command/ContainerLintCommand.php b/Command/ContainerLintCommand.php index b63ebe431..cd6e0657c 100644 --- a/Command/ContainerLintCommand.php +++ b/Command/ContainerLintCommand.php @@ -19,6 +19,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\Compiler\CheckAliasValidityPass; use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\Compiler\ResolveFactoryClassPass; @@ -107,6 +108,7 @@ private function getContainerBuilder(): ContainerBuilder $container->setParameter('container.build_hash', 'lint_container'); $container->setParameter('container.build_id', 'lint_container'); + $container->addCompilerPass(new CheckAliasValidityPass(), PassConfig::TYPE_BEFORE_REMOVING, -100); $container->addCompilerPass(new CheckTypeDeclarationsPass(true), PassConfig::TYPE_AFTER_REMOVING, -100); return $this->container = $container; From 8966e1c9730dd9b4ac56ea7fdbcb0642a8eb2f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 8 Feb 2024 18:09:44 +0100 Subject: [PATCH 062/281] [workflow] determines places form transitions --- DependencyInjection/Configuration.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index e5b15e88f..d02e85fe1 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -486,8 +486,6 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void return array_values($places); }) ->end() - ->isRequired() - ->requiresAtLeastOneElement() ->prototype('array') ->children() ->scalarNode('name') From 427ab31004594fc33be53edff3465451739bcc6b Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 8 Feb 2024 16:14:05 +0100 Subject: [PATCH 063/281] [FrameworkBundle] Use CPP --- CacheWarmer/AbstractPhpFileCacheWarmer.php | 8 ++---- CacheWarmer/SerializerCacheWarmer.php | 9 +++--- CacheWarmer/ValidatorCacheWarmer.php | 9 +++--- Command/AssetsInstallCommand.php | 12 +++----- Command/CacheClearCommand.php | 8 +++--- Command/CachePoolClearCommand.php | 12 +++----- Command/CachePoolDeleteCommand.php | 12 +++----- Command/CachePoolInvalidateTagsCommand.php | 7 ++--- Command/CachePoolListCommand.php | 9 ++---- Command/CachePoolPruneCommand.php | 9 ++---- Command/CacheWarmupCommand.php | 9 ++---- Command/DebugAutowiringCommand.php | 9 +++--- Command/EventDispatcherDebugCommand.php | 9 ++---- Command/RouterDebugCommand.php | 12 +++----- Command/RouterMatchCommand.php | 12 +++----- Command/SecretsDecryptToLocalCommand.php | 12 +++----- Command/SecretsEncryptFromLocalCommand.php | 12 +++----- Command/SecretsGenerateKeysCommand.php | 12 +++----- Command/SecretsListCommand.php | 12 +++----- Command/SecretsRemoveCommand.php | 12 +++----- Command/SecretsSetCommand.php | 12 +++----- Command/TranslationDebugCommand.php | 30 +++++++------------- Command/TranslationUpdateCommand.php | 33 ++++++++-------------- Console/Application.php | 8 ++---- Console/Descriptor/TextDescriptor.php | 8 ++---- Controller/RedirectController.php | 14 ++++----- Controller/TemplateController.php | 8 ++---- DependencyInjection/Configuration.php | 8 ++---- HttpCache/HttpCache.php | 12 ++++---- Routing/DelegatingLoader.php | 12 ++++---- Secrets/DotenvVault.php | 7 ++--- Test/TestBrowserToken.php | 11 ++++---- Translation/Translator.php | 23 +++++++-------- 33 files changed, 145 insertions(+), 247 deletions(-) diff --git a/CacheWarmer/AbstractPhpFileCacheWarmer.php b/CacheWarmer/AbstractPhpFileCacheWarmer.php index 3d7c99e4f..d809888be 100644 --- a/CacheWarmer/AbstractPhpFileCacheWarmer.php +++ b/CacheWarmer/AbstractPhpFileCacheWarmer.php @@ -19,14 +19,12 @@ abstract class AbstractPhpFileCacheWarmer implements CacheWarmerInterface { - private string $phpArrayFile; - /** * @param string $phpArrayFile The PHP file where metadata are cached */ - public function __construct(string $phpArrayFile) - { - $this->phpArrayFile = $phpArrayFile; + public function __construct( + private string $phpArrayFile, + ) { } public function isOptional(): bool diff --git a/CacheWarmer/SerializerCacheWarmer.php b/CacheWarmer/SerializerCacheWarmer.php index 228a763fe..46da4daaa 100644 --- a/CacheWarmer/SerializerCacheWarmer.php +++ b/CacheWarmer/SerializerCacheWarmer.php @@ -28,16 +28,15 @@ */ class SerializerCacheWarmer extends AbstractPhpFileCacheWarmer { - private array $loaders; - /** * @param LoaderInterface[] $loaders The serializer metadata loaders * @param string $phpArrayFile The PHP file where metadata are cached */ - public function __construct(array $loaders, string $phpArrayFile) - { + public function __construct( + private array $loaders, + string $phpArrayFile, + ) { parent::__construct($phpArrayFile); - $this->loaders = $loaders; } protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter, ?string $buildDir = null): bool diff --git a/CacheWarmer/ValidatorCacheWarmer.php b/CacheWarmer/ValidatorCacheWarmer.php index abf350562..6ecaa4bd1 100644 --- a/CacheWarmer/ValidatorCacheWarmer.php +++ b/CacheWarmer/ValidatorCacheWarmer.php @@ -29,15 +29,14 @@ */ class ValidatorCacheWarmer extends AbstractPhpFileCacheWarmer { - private ValidatorBuilder $validatorBuilder; - /** * @param string $phpArrayFile The PHP file where metadata are cached */ - public function __construct(ValidatorBuilder $validatorBuilder, string $phpArrayFile) - { + public function __construct( + private ValidatorBuilder $validatorBuilder, + string $phpArrayFile, + ) { parent::__construct($phpArrayFile); - $this->validatorBuilder = $validatorBuilder; } protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter, ?string $buildDir = null): bool diff --git a/Command/AssetsInstallCommand.php b/Command/AssetsInstallCommand.php index 264955d79..cb05fe827 100644 --- a/Command/AssetsInstallCommand.php +++ b/Command/AssetsInstallCommand.php @@ -41,15 +41,11 @@ class AssetsInstallCommand extends Command public const METHOD_ABSOLUTE_SYMLINK = 'absolute symlink'; public const METHOD_RELATIVE_SYMLINK = 'relative symlink'; - private Filesystem $filesystem; - private string $projectDir; - - public function __construct(Filesystem $filesystem, string $projectDir) - { + public function __construct( + private Filesystem $filesystem, + private string $projectDir, + ) { parent::__construct(); - - $this->filesystem = $filesystem; - $this->projectDir = $projectDir; } protected function configure(): void diff --git a/Command/CacheClearCommand.php b/Command/CacheClearCommand.php index 40880df47..6f5510169 100644 --- a/Command/CacheClearCommand.php +++ b/Command/CacheClearCommand.php @@ -37,14 +37,14 @@ #[AsCommand(name: 'cache:clear', description: 'Clear the cache')] class CacheClearCommand extends Command { - private CacheClearerInterface $cacheClearer; private Filesystem $filesystem; - public function __construct(CacheClearerInterface $cacheClearer, ?Filesystem $filesystem = null) - { + public function __construct( + private CacheClearerInterface $cacheClearer, + ?Filesystem $filesystem = null, + ) { parent::__construct(); - $this->cacheClearer = $cacheClearer; $this->filesystem = $filesystem ?? new Filesystem(); } diff --git a/Command/CachePoolClearCommand.php b/Command/CachePoolClearCommand.php index 8b8b9cd35..d5320e7a9 100644 --- a/Command/CachePoolClearCommand.php +++ b/Command/CachePoolClearCommand.php @@ -32,18 +32,14 @@ #[AsCommand(name: 'cache:pool:clear', description: 'Clear cache pools')] final class CachePoolClearCommand extends Command { - private Psr6CacheClearer $poolClearer; - private ?array $poolNames; - /** * @param string[]|null $poolNames */ - public function __construct(Psr6CacheClearer $poolClearer, ?array $poolNames = null) - { + public function __construct( + private Psr6CacheClearer $poolClearer, + private ?array $poolNames = null, + ) { parent::__construct(); - - $this->poolClearer = $poolClearer; - $this->poolNames = $poolNames; } protected function configure(): void diff --git a/Command/CachePoolDeleteCommand.php b/Command/CachePoolDeleteCommand.php index dfa307bc0..e634e00f0 100644 --- a/Command/CachePoolDeleteCommand.php +++ b/Command/CachePoolDeleteCommand.php @@ -29,18 +29,14 @@ #[AsCommand(name: 'cache:pool:delete', description: 'Delete an item from a cache pool')] final class CachePoolDeleteCommand extends Command { - private Psr6CacheClearer $poolClearer; - private ?array $poolNames; - /** * @param string[]|null $poolNames */ - public function __construct(Psr6CacheClearer $poolClearer, ?array $poolNames = null) - { + public function __construct( + private Psr6CacheClearer $poolClearer, + private ?array $poolNames = null, + ) { parent::__construct(); - - $this->poolClearer = $poolClearer; - $this->poolNames = $poolNames; } protected function configure(): void diff --git a/Command/CachePoolInvalidateTagsCommand.php b/Command/CachePoolInvalidateTagsCommand.php index 9e6ef9330..f879a6d0d 100644 --- a/Command/CachePoolInvalidateTagsCommand.php +++ b/Command/CachePoolInvalidateTagsCommand.php @@ -30,14 +30,13 @@ #[AsCommand(name: 'cache:pool:invalidate-tags', description: 'Invalidate cache tags for all or a specific pool')] final class CachePoolInvalidateTagsCommand extends Command { - private ServiceProviderInterface $pools; private array $poolNames; - public function __construct(ServiceProviderInterface $pools) - { + public function __construct( + private ServiceProviderInterface $pools, + ) { parent::__construct(); - $this->pools = $pools; $this->poolNames = array_keys($pools->getProvidedServices()); } diff --git a/Command/CachePoolListCommand.php b/Command/CachePoolListCommand.php index 2659ad8fe..6b8e71eb0 100644 --- a/Command/CachePoolListCommand.php +++ b/Command/CachePoolListCommand.php @@ -25,16 +25,13 @@ #[AsCommand(name: 'cache:pool:list', description: 'List available cache pools')] final class CachePoolListCommand extends Command { - private array $poolNames; - /** * @param string[] $poolNames */ - public function __construct(array $poolNames) - { + public function __construct( + private array $poolNames, + ) { parent::__construct(); - - $this->poolNames = $poolNames; } protected function configure(): void diff --git a/Command/CachePoolPruneCommand.php b/Command/CachePoolPruneCommand.php index fc0dc6d79..fba1033f9 100644 --- a/Command/CachePoolPruneCommand.php +++ b/Command/CachePoolPruneCommand.php @@ -26,16 +26,13 @@ #[AsCommand(name: 'cache:pool:prune', description: 'Prune cache pools')] final class CachePoolPruneCommand extends Command { - private iterable $pools; - /** * @param iterable $pools */ - public function __construct(iterable $pools) - { + public function __construct( + private iterable $pools, + ) { parent::__construct(); - - $this->pools = $pools; } protected function configure(): void diff --git a/Command/CacheWarmupCommand.php b/Command/CacheWarmupCommand.php index 6f1073de4..a4b32a561 100644 --- a/Command/CacheWarmupCommand.php +++ b/Command/CacheWarmupCommand.php @@ -31,13 +31,10 @@ #[AsCommand(name: 'cache:warmup', description: 'Warm up an empty cache')] class CacheWarmupCommand extends Command { - private CacheWarmerAggregate $cacheWarmer; - - public function __construct(CacheWarmerAggregate $cacheWarmer) - { + public function __construct( + private CacheWarmerAggregate $cacheWarmer, + ) { parent::__construct(); - - $this->cacheWarmer = $cacheWarmer; } protected function configure(): void diff --git a/Command/DebugAutowiringCommand.php b/Command/DebugAutowiringCommand.php index f6efd8bef..77011b185 100644 --- a/Command/DebugAutowiringCommand.php +++ b/Command/DebugAutowiringCommand.php @@ -33,11 +33,10 @@ #[AsCommand(name: 'debug:autowiring', description: 'List classes/interfaces you can use for autowiring')] class DebugAutowiringCommand extends ContainerDebugCommand { - private ?FileLinkFormatter $fileLinkFormatter; - - public function __construct(?string $name = null, ?FileLinkFormatter $fileLinkFormatter = null) - { - $this->fileLinkFormatter = $fileLinkFormatter; + public function __construct( + ?string $name = null, + private ?FileLinkFormatter $fileLinkFormatter = null, + ) { parent::__construct($name); } diff --git a/Command/EventDispatcherDebugCommand.php b/Command/EventDispatcherDebugCommand.php index 1a74e8682..52816e7de 100644 --- a/Command/EventDispatcherDebugCommand.php +++ b/Command/EventDispatcherDebugCommand.php @@ -37,13 +37,10 @@ class EventDispatcherDebugCommand extends Command { private const DEFAULT_DISPATCHER = 'event_dispatcher'; - private ContainerInterface $dispatchers; - - public function __construct(ContainerInterface $dispatchers) - { + public function __construct( + private ContainerInterface $dispatchers, + ) { parent::__construct(); - - $this->dispatchers = $dispatchers; } protected function configure(): void diff --git a/Command/RouterDebugCommand.php b/Command/RouterDebugCommand.php index 9318b46be..54df49431 100644 --- a/Command/RouterDebugCommand.php +++ b/Command/RouterDebugCommand.php @@ -39,15 +39,11 @@ class RouterDebugCommand extends Command { use BuildDebugContainerTrait; - private RouterInterface $router; - private ?FileLinkFormatter $fileLinkFormatter; - - public function __construct(RouterInterface $router, ?FileLinkFormatter $fileLinkFormatter = null) - { + public function __construct( + private RouterInterface $router, + private ?FileLinkFormatter $fileLinkFormatter = null, + ) { parent::__construct(); - - $this->router = $router; - $this->fileLinkFormatter = $fileLinkFormatter; } protected function configure(): void diff --git a/Command/RouterMatchCommand.php b/Command/RouterMatchCommand.php index 7efd1f3ed..475b403ca 100644 --- a/Command/RouterMatchCommand.php +++ b/Command/RouterMatchCommand.php @@ -33,18 +33,14 @@ #[AsCommand(name: 'router:match', description: 'Help debug routes by simulating a path info match')] class RouterMatchCommand extends Command { - private RouterInterface $router; - private iterable $expressionLanguageProviders; - /** * @param iterable $expressionLanguageProviders */ - public function __construct(RouterInterface $router, iterable $expressionLanguageProviders = []) - { + public function __construct( + private RouterInterface $router, + private iterable $expressionLanguageProviders = [], + ) { parent::__construct(); - - $this->router = $router; - $this->expressionLanguageProviders = $expressionLanguageProviders; } protected function configure(): void diff --git a/Command/SecretsDecryptToLocalCommand.php b/Command/SecretsDecryptToLocalCommand.php index ac711e3db..f76e1d05a 100644 --- a/Command/SecretsDecryptToLocalCommand.php +++ b/Command/SecretsDecryptToLocalCommand.php @@ -28,14 +28,10 @@ #[AsCommand(name: 'secrets:decrypt-to-local', description: 'Decrypt all secrets and stores them in the local vault')] final class SecretsDecryptToLocalCommand extends Command { - private AbstractVault $vault; - private ?AbstractVault $localVault; - - public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null) - { - $this->vault = $vault; - $this->localVault = $localVault; - + public function __construct( + private AbstractVault $vault, + private ?AbstractVault $localVault = null, + ) { parent::__construct(); } diff --git a/Command/SecretsEncryptFromLocalCommand.php b/Command/SecretsEncryptFromLocalCommand.php index 46e0baffc..9740098e5 100644 --- a/Command/SecretsEncryptFromLocalCommand.php +++ b/Command/SecretsEncryptFromLocalCommand.php @@ -27,14 +27,10 @@ #[AsCommand(name: 'secrets:encrypt-from-local', description: 'Encrypt all local secrets to the vault')] final class SecretsEncryptFromLocalCommand extends Command { - private AbstractVault $vault; - private ?AbstractVault $localVault; - - public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null) - { - $this->vault = $vault; - $this->localVault = $localVault; - + public function __construct( + private AbstractVault $vault, + private ?AbstractVault $localVault = null, + ) { parent::__construct(); } diff --git a/Command/SecretsGenerateKeysCommand.php b/Command/SecretsGenerateKeysCommand.php index 989eff9fd..66a752eac 100644 --- a/Command/SecretsGenerateKeysCommand.php +++ b/Command/SecretsGenerateKeysCommand.php @@ -30,14 +30,10 @@ #[AsCommand(name: 'secrets:generate-keys', description: 'Generate new encryption keys')] final class SecretsGenerateKeysCommand extends Command { - private AbstractVault $vault; - private ?AbstractVault $localVault; - - public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null) - { - $this->vault = $vault; - $this->localVault = $localVault; - + public function __construct( + private AbstractVault $vault, + private ?AbstractVault $localVault = null, + ) { parent::__construct(); } diff --git a/Command/SecretsListCommand.php b/Command/SecretsListCommand.php index 9a24f4a90..cdfc51c39 100644 --- a/Command/SecretsListCommand.php +++ b/Command/SecretsListCommand.php @@ -31,14 +31,10 @@ #[AsCommand(name: 'secrets:list', description: 'List all secrets')] final class SecretsListCommand extends Command { - private AbstractVault $vault; - private ?AbstractVault $localVault; - - public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null) - { - $this->vault = $vault; - $this->localVault = $localVault; - + public function __construct( + private AbstractVault $vault, + private ?AbstractVault $localVault = null, + ) { parent::__construct(); } diff --git a/Command/SecretsRemoveCommand.php b/Command/SecretsRemoveCommand.php index 1789f2981..11660b00d 100644 --- a/Command/SecretsRemoveCommand.php +++ b/Command/SecretsRemoveCommand.php @@ -32,14 +32,10 @@ #[AsCommand(name: 'secrets:remove', description: 'Remove a secret from the vault')] final class SecretsRemoveCommand extends Command { - private AbstractVault $vault; - private ?AbstractVault $localVault; - - public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null) - { - $this->vault = $vault; - $this->localVault = $localVault; - + public function __construct( + private AbstractVault $vault, + private ?AbstractVault $localVault = null, + ) { parent::__construct(); } diff --git a/Command/SecretsSetCommand.php b/Command/SecretsSetCommand.php index 2d2b8c5cb..49a20af76 100644 --- a/Command/SecretsSetCommand.php +++ b/Command/SecretsSetCommand.php @@ -33,14 +33,10 @@ #[AsCommand(name: 'secrets:set', description: 'Set a secret in the vault')] final class SecretsSetCommand extends Command { - private AbstractVault $vault; - private ?AbstractVault $localVault; - - public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null) - { - $this->vault = $vault; - $this->localVault = $localVault; - + public function __construct( + private AbstractVault $vault, + private ?AbstractVault $localVault = null, + ) { parent::__construct(); } diff --git a/Command/TranslationDebugCommand.php b/Command/TranslationDebugCommand.php index 7095d70ab..2438d3feb 100644 --- a/Command/TranslationDebugCommand.php +++ b/Command/TranslationDebugCommand.php @@ -50,27 +50,17 @@ class TranslationDebugCommand extends Command public const MESSAGE_UNUSED = 1; public const MESSAGE_EQUALS_FALLBACK = 2; - private TranslatorInterface $translator; - private TranslationReaderInterface $reader; - private ExtractorInterface $extractor; - private ?string $defaultTransPath; - private ?string $defaultViewsPath; - private array $transPaths; - private array $codePaths; - private array $enabledLocales; - - public function __construct(TranslatorInterface $translator, TranslationReaderInterface $reader, ExtractorInterface $extractor, ?string $defaultTransPath = null, ?string $defaultViewsPath = null, array $transPaths = [], array $codePaths = [], array $enabledLocales = []) - { + public function __construct( + private TranslatorInterface $translator, + private TranslationReaderInterface $reader, + private ExtractorInterface $extractor, + private ?string $defaultTransPath = null, + private ?string $defaultViewsPath = null, + private array $transPaths = [], + private array $codePaths = [], + private array $enabledLocales = [], + ) { parent::__construct(); - - $this->translator = $translator; - $this->reader = $reader; - $this->extractor = $extractor; - $this->defaultTransPath = $defaultTransPath; - $this->defaultViewsPath = $defaultViewsPath; - $this->transPaths = $transPaths; - $this->codePaths = $codePaths; - $this->enabledLocales = $enabledLocales; } protected function configure(): void diff --git a/Command/TranslationUpdateCommand.php b/Command/TranslationUpdateCommand.php index fc95e0217..1a883f81e 100644 --- a/Command/TranslationUpdateCommand.php +++ b/Command/TranslationUpdateCommand.php @@ -50,29 +50,18 @@ class TranslationUpdateCommand extends Command 'xlf20' => ['xlf', '2.0'], ]; - private TranslationWriterInterface $writer; - private TranslationReaderInterface $reader; - private ExtractorInterface $extractor; - private string $defaultLocale; - private ?string $defaultTransPath; - private ?string $defaultViewsPath; - private array $transPaths; - private array $codePaths; - private array $enabledLocales; - - public function __construct(TranslationWriterInterface $writer, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultLocale, ?string $defaultTransPath = null, ?string $defaultViewsPath = null, array $transPaths = [], array $codePaths = [], array $enabledLocales = []) - { + public function __construct( + private TranslationWriterInterface $writer, + private TranslationReaderInterface $reader, + private ExtractorInterface $extractor, + private string $defaultLocale, + private ?string $defaultTransPath = null, + private ?string $defaultViewsPath = null, + private array $transPaths = [], + private array $codePaths = [], + private array $enabledLocales = [], + ) { parent::__construct(); - - $this->writer = $writer; - $this->reader = $reader; - $this->extractor = $extractor; - $this->defaultLocale = $defaultLocale; - $this->defaultTransPath = $defaultTransPath; - $this->defaultViewsPath = $defaultViewsPath; - $this->transPaths = $transPaths; - $this->codePaths = $codePaths; - $this->enabledLocales = $enabledLocales; } protected function configure(): void diff --git a/Console/Application.php b/Console/Application.php index 14a907e2e..1c41849e7 100644 --- a/Console/Application.php +++ b/Console/Application.php @@ -30,14 +30,12 @@ */ class Application extends BaseApplication { - private KernelInterface $kernel; private bool $commandsRegistered = false; private array $registrationErrors = []; - public function __construct(KernelInterface $kernel) - { - $this->kernel = $kernel; - + public function __construct( + private KernelInterface $kernel, + ) { parent::__construct('Symfony', Kernel::VERSION); $inputDefinition = $this->getDefinition(); diff --git a/Console/Descriptor/TextDescriptor.php b/Console/Descriptor/TextDescriptor.php index 1ce0539da..8a3c58708 100644 --- a/Console/Descriptor/TextDescriptor.php +++ b/Console/Descriptor/TextDescriptor.php @@ -38,11 +38,9 @@ */ class TextDescriptor extends Descriptor { - private ?FileLinkFormatter $fileLinkFormatter; - - public function __construct(?FileLinkFormatter $fileLinkFormatter = null) - { - $this->fileLinkFormatter = $fileLinkFormatter; + public function __construct( + private ?FileLinkFormatter $fileLinkFormatter = null, + ) { } protected function describeRouteCollection(RouteCollection $routes, array $options = []): void diff --git a/Controller/RedirectController.php b/Controller/RedirectController.php index fbb52ead7..1001fad63 100644 --- a/Controller/RedirectController.php +++ b/Controller/RedirectController.php @@ -27,15 +27,11 @@ */ class RedirectController { - private ?UrlGeneratorInterface $router; - private ?int $httpPort; - private ?int $httpsPort; - - public function __construct(?UrlGeneratorInterface $router = null, ?int $httpPort = null, ?int $httpsPort = null) - { - $this->router = $router; - $this->httpPort = $httpPort; - $this->httpsPort = $httpsPort; + public function __construct( + private ?UrlGeneratorInterface $router = null, + private ?int $httpPort = null, + private ?int $httpsPort = null, + ) { } /** diff --git a/Controller/TemplateController.php b/Controller/TemplateController.php index 97631572c..bcbcc382d 100644 --- a/Controller/TemplateController.php +++ b/Controller/TemplateController.php @@ -23,11 +23,9 @@ */ class TemplateController { - private ?Environment $twig; - - public function __construct(?Environment $twig = null) - { - $this->twig = $twig; + public function __construct( + private ?Environment $twig = null, + ) { } /** diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index e5b15e88f..722984e53 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -56,14 +56,12 @@ */ class Configuration implements ConfigurationInterface { - private bool $debug; - /** * @param bool $debug Whether debugging is enabled or not */ - public function __construct(bool $debug) - { - $this->debug = $debug; + public function __construct( + private bool $debug, + ) { } /** diff --git a/HttpCache/HttpCache.php b/HttpCache/HttpCache.php index 38eee7361..f163708cc 100644 --- a/HttpCache/HttpCache.php +++ b/HttpCache/HttpCache.php @@ -28,19 +28,19 @@ class HttpCache extends BaseHttpCache { protected ?string $cacheDir = null; - protected KernelInterface $kernel; private ?StoreInterface $store = null; - private ?SurrogateInterface $surrogate; private array $options; /** * @param $cache The cache directory (default used if null) or the storage instance */ - public function __construct(KernelInterface $kernel, string|StoreInterface|null $cache = null, ?SurrogateInterface $surrogate = null, ?array $options = null) - { - $this->kernel = $kernel; - $this->surrogate = $surrogate; + public function __construct( + protected KernelInterface $kernel, + string|StoreInterface|null $cache = null, + private ?SurrogateInterface $surrogate = null, + ?array $options = null, + ) { $this->options = $options ?? []; if ($cache instanceof StoreInterface) { diff --git a/Routing/DelegatingLoader.php b/Routing/DelegatingLoader.php index 3239d1094..42d461773 100644 --- a/Routing/DelegatingLoader.php +++ b/Routing/DelegatingLoader.php @@ -29,14 +29,12 @@ class DelegatingLoader extends BaseDelegatingLoader { private bool $loading = false; - private array $defaultOptions; - private array $defaultRequirements; - - public function __construct(LoaderResolverInterface $resolver, array $defaultOptions = [], array $defaultRequirements = []) - { - $this->defaultOptions = $defaultOptions; - $this->defaultRequirements = $defaultRequirements; + public function __construct( + LoaderResolverInterface $resolver, + private array $defaultOptions = [], + private array $defaultRequirements = [], + ) { parent::__construct($resolver); } diff --git a/Secrets/DotenvVault.php b/Secrets/DotenvVault.php index 994b31d18..7bdd74c37 100644 --- a/Secrets/DotenvVault.php +++ b/Secrets/DotenvVault.php @@ -16,10 +16,9 @@ */ class DotenvVault extends AbstractVault { - private string $dotenvFile; - - public function __construct(string $dotenvFile) - { + public function __construct( + private string $dotenvFile, + ) { $this->dotenvFile = strtr($dotenvFile, '/', \DIRECTORY_SEPARATOR); } diff --git a/Test/TestBrowserToken.php b/Test/TestBrowserToken.php index 25d71d084..e186b2c44 100644 --- a/Test/TestBrowserToken.php +++ b/Test/TestBrowserToken.php @@ -21,17 +21,16 @@ */ class TestBrowserToken extends AbstractToken { - private string $firewallName; - - public function __construct(array $roles = [], ?UserInterface $user = null, string $firewallName = 'main') - { + public function __construct( + array $roles = [], + ?UserInterface $user = null, + private string $firewallName = 'main', + ) { parent::__construct($roles); if (null !== $user) { $this->setUser($user); } - - $this->firewallName = $firewallName; } public function getFirewallName(): string diff --git a/Translation/Translator.php b/Translation/Translator.php index 1482f0244..870d69ae1 100644 --- a/Translation/Translator.php +++ b/Translation/Translator.php @@ -26,8 +26,6 @@ */ class Translator extends BaseTranslator implements WarmableInterface { - protected ContainerInterface $container; - protected array $loaderIds; protected array $options = [ 'cache_dir' => null, 'debug' => false, @@ -59,11 +57,6 @@ class Translator extends BaseTranslator implements WarmableInterface */ private array $scannedDirectories; - /** - * @var string[] - */ - private array $enabledLocales; - /** * Constructor. * @@ -74,14 +67,18 @@ class Translator extends BaseTranslator implements WarmableInterface * * resource_files: List of translation resources available grouped by locale. * * cache_vary: An array of data that is serialized to generate the cached catalogue name. * + * @param string[] $enabledLocales + * * @throws InvalidArgumentException */ - public function __construct(ContainerInterface $container, MessageFormatterInterface $formatter, string $defaultLocale, array $loaderIds = [], array $options = [], array $enabledLocales = []) - { - $this->container = $container; - $this->loaderIds = $loaderIds; - $this->enabledLocales = $enabledLocales; - + public function __construct( + protected ContainerInterface $container, + MessageFormatterInterface $formatter, + string $defaultLocale, + protected array $loaderIds = [], + array $options = [], + private array $enabledLocales = [], + ) { // check option names if ($diff = array_diff(array_keys($options), array_keys($this->options))) { throw new InvalidArgumentException(sprintf('The Translator does not support the following options: \'%s\'.', implode('\', \'', $diff))); From 6083b478b06abe0be4f6fd1d39db4f33684fc59d Mon Sep 17 00:00:00 2001 From: Joseph Bielawski Date: Tue, 13 Feb 2024 16:18:05 +0100 Subject: [PATCH 064/281] Add new Pushy notifier bridge --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_transports.php | 1 + 2 files changed, 2 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 23f000f90..644403f18 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2804,6 +2804,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\PagerDuty\PagerDutyTransportFactory::class => 'notifier.transport_factory.pager-duty', NotifierBridge\Plivo\PlivoTransportFactory::class => 'notifier.transport_factory.plivo', NotifierBridge\Pushover\PushoverTransportFactory::class => 'notifier.transport_factory.pushover', + NotifierBridge\Pushy\PushyTransportFactory::class => 'notifier.transport_factory.pushy', NotifierBridge\Redlink\RedlinkTransportFactory::class => 'notifier.transport_factory.redlink', NotifierBridge\RingCentral\RingCentralTransportFactory::class => 'notifier.transport_factory.ring-central', NotifierBridge\RocketChat\RocketChatTransportFactory::class => 'notifier.transport_factory.rocket-chat', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index c92d9b4b6..07962a1ed 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -87,6 +87,7 @@ 'ovh-cloud' => Bridge\OvhCloud\OvhCloudTransportFactory::class, 'plivo' => Bridge\Plivo\PlivoTransportFactory::class, 'pushover' => Bridge\Pushover\PushoverTransportFactory::class, + 'pushy' => Bridge\Pushy\PushyTransportFactory::class, 'redlink' => Bridge\Redlink\RedlinkTransportFactory::class, 'ring-central' => Bridge\RingCentral\RingCentralTransportFactory::class, 'sendberry' => Bridge\Sendberry\SendberryTransportFactory::class, From 4e97cadf98ba5188ae097ada3c6f0276040ae293 Mon Sep 17 00:00:00 2001 From: Valentin-Nicusor Barbu Date: Fri, 2 Feb 2024 18:13:46 +0200 Subject: [PATCH 065/281] [Notifier] Add SMSense bridge --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_transports.php | 1 + 2 files changed, 2 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index cf8bb8dfc..444cda2b6 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2759,6 +2759,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\SmsFactor\SmsFactorTransportFactory::class => 'notifier.transport_factory.sms-factor', NotifierBridge\Smsmode\SmsmodeTransportFactory::class => 'notifier.transport_factory.smsmode', NotifierBridge\SmsSluzba\SmsSluzbaTransportFactory::class => 'notifier.transport_factory.sms-sluzba', + NotifierBridge\Smsense\SmsenseTransportFactory::class => 'notifier.transport_factory.smsense', NotifierBridge\SpotHit\SpotHitTransportFactory::class => 'notifier.transport_factory.spot-hit', NotifierBridge\Telegram\TelegramTransportFactory::class => 'notifier.transport_factory.telegram', NotifierBridge\Telnyx\TelnyxTransportFactory::class => 'notifier.transport_factory.telnyx', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index c92d9b4b6..c1379cd3f 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -100,6 +100,7 @@ 'smsapi' => Bridge\Smsapi\SmsapiTransportFactory::class, 'smsbox' => Bridge\Smsbox\SmsboxTransportFactory::class, 'smsc' => Bridge\Smsc\SmscTransportFactory::class, + 'smsense' => Bridge\Smsense\SmsenseTransportFactory::class, 'smsmode' => Bridge\Smsmode\SmsmodeTransportFactory::class, 'spot-hit' => Bridge\SpotHit\SpotHitTransportFactory::class, 'telnyx' => Bridge\Telnyx\TelnyxTransportFactory::class, From 4f1f39848fa4ca8e05a09b253090fa14b49a6ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 2 Aug 2023 12:30:25 +0200 Subject: [PATCH 066/281] [FrameworkBundle][Workflow] Attach the workflow's configuration to the `workflow` tag --- CHANGELOG.md | 1 + DependencyInjection/FrameworkExtension.php | 2 +- .../FrameworkExtensionTestCase.php | 22 +++++++++++++++---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 950c2b8c5..52624c2cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ CHANGELOG * Add `rate_limiter` tags to rate limiter services * Add `secrets:reveal` command * Add `rate_limiter` option to `http_client.default_options` and `http_client.scoped_clients` + * Attach the workflow's configuration to the `workflow` tag 7.0 --- diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index d2a2afadd..7a52d9f9b 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -1029,7 +1029,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $workflowDefinition->replaceArgument(3, $name); $workflowDefinition->replaceArgument(4, $workflow['events_to_dispatch']); - $workflowDefinition->addTag('workflow', ['name' => $name]); + $workflowDefinition->addTag('workflow', ['name' => $name, 'metadata' => $workflow['metadata']]); if ('workflow' === $type) { $workflowDefinition->addTag('workflow.workflow', ['name' => $name]); } elseif ('state_machine' === $type) { diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 7bbbaf2d2..c73197607 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -84,7 +84,6 @@ use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Webhook\Client\RequestParser; use Symfony\Component\Webhook\Controller\WebhookController; -use Symfony\Component\Workflow; use Symfony\Component\Workflow\Exception\InvalidDefinitionException; use Symfony\Component\Workflow\Metadata\InMemoryMetadataStore; use Symfony\Component\Workflow\WorkflowEvents; @@ -302,7 +301,15 @@ public function testWorkflows() $this->assertArrayHasKey('index_4', $args); $this->assertNull($args['index_4'], 'Workflows has eventsToDispatch=null'); - $this->assertSame(['workflow' => [['name' => 'article']], 'workflow.workflow' => [['name' => 'article']]], $container->getDefinition('workflow.article')->getTags()); + $tags = $container->getDefinition('workflow.article')->getTags(); + $this->assertArrayHasKey('workflow', $tags); + $this->assertArrayHasKey('workflow.workflow', $tags); + $this->assertSame([['name' => 'article']], $tags['workflow.workflow']); + $this->assertSame('article', $tags['workflow'][0]['name'] ?? null); + $this->assertSame([ + 'title' => 'article workflow', + 'description' => 'workflow for articles', + ], $tags['workflow'][0]['metadata'] ?? null); $this->assertTrue($container->hasDefinition('workflow.article.definition'), 'Workflow definition is registered as a service'); @@ -333,7 +340,14 @@ public function testWorkflows() $this->assertSame('state_machine.abstract', $container->getDefinition('state_machine.pull_request')->getParent()); $this->assertTrue($container->hasDefinition('state_machine.pull_request.definition'), 'State machine definition is registered as a service'); - $this->assertSame(['workflow' => [['name' => 'pull_request']], 'workflow.state_machine' => [['name' => 'pull_request']]], $container->getDefinition('state_machine.pull_request')->getTags()); + $tags = $container->getDefinition('state_machine.pull_request')->getTags(); + $this->assertArrayHasKey('workflow', $tags); + $this->assertArrayHasKey('workflow.state_machine', $tags); + $this->assertSame([['name' => 'pull_request']], $tags['workflow.state_machine']); + $this->assertSame('pull_request', $tags['workflow'][0]['name'] ?? null); + $this->assertSame([ + 'title' => 'workflow title', + ], $tags['workflow'][0]['metadata'] ?? null); $stateMachineDefinition = $container->getDefinition('state_machine.pull_request.definition'); @@ -357,7 +371,7 @@ public function testWorkflows() $this->assertSame('state_machine.pull_request.metadata_store', (string) $metadataStoreReference); $metadataStoreDefinition = $container->getDefinition('state_machine.pull_request.metadata_store'); - $this->assertSame(Workflow\Metadata\InMemoryMetadataStore::class, $metadataStoreDefinition->getClass()); + $this->assertSame(InMemoryMetadataStore::class, $metadataStoreDefinition->getClass()); $this->assertSame(InMemoryMetadataStore::class, $metadataStoreDefinition->getClass()); $workflowMetadata = $metadataStoreDefinition->getArgument(0); From 12901443fc62e4a4e23dfe398ee4b86755e7c22c Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Wed, 6 Mar 2024 11:55:15 +0100 Subject: [PATCH 067/281] [Filesystem] Add the readFile() method --- Command/AssetsInstallCommand.php | 2 +- Command/CacheClearCommand.php | 2 +- DependencyInjection/FrameworkExtension.php | 3 ++- .../CacheClearCommand/CacheClearCommandTest.php | 4 ++-- Tests/Secrets/SodiumVaultTest.php | 10 ++++++---- composer.json | 2 +- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Command/AssetsInstallCommand.php b/Command/AssetsInstallCommand.php index cb05fe827..32b38de9a 100644 --- a/Command/AssetsInstallCommand.php +++ b/Command/AssetsInstallCommand.php @@ -260,7 +260,7 @@ private function getPublicDirectory(ContainerInterface $container): string return $defaultPublicDir; } - $composerConfig = json_decode(file_get_contents($composerFilePath), true); + $composerConfig = json_decode($this->filesystem->readFile($composerFilePath), true, flags: \JSON_THROW_ON_ERROR); return $composerConfig['extra']['public-dir'] ?? $defaultPublicDir; } diff --git a/Command/CacheClearCommand.php b/Command/CacheClearCommand.php index 8a1dedd27..4122db6fd 100644 --- a/Command/CacheClearCommand.php +++ b/Command/CacheClearCommand.php @@ -232,7 +232,7 @@ private function warmup(string $warmupDir, string $realBuildDir): void $search = [$warmupDir, str_replace('\\', '\\\\', $warmupDir)]; $replace = str_replace('\\', '/', $realBuildDir); foreach (Finder::create()->files()->in($warmupDir) as $file) { - $content = str_replace($search, $replace, file_get_contents($file), $count); + $content = str_replace($search, $replace, $this->filesystem->readFile($file), $count); if ($count) { file_put_contents($file, $content); } diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 7a52d9f9b..911242bc2 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -71,6 +71,7 @@ use Symfony\Component\EventDispatcher\Attribute\AsEventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\Glob; use Symfony\Component\Form\Extension\HtmlSanitizer\Type\TextTypeHtmlSanitizerExtension; @@ -3141,7 +3142,7 @@ private function getPublicDirectory(ContainerBuilder $container): string } $container->addResource(new FileResource($composerFilePath)); - $composerConfig = json_decode(file_get_contents($composerFilePath), true); + $composerConfig = json_decode((new Filesystem())->readFile($composerFilePath), true, flags: \JSON_THROW_ON_ERROR); return isset($composerConfig['extra']['public-dir']) ? $projectDir.'/'.$composerConfig['extra']['public-dir'] : $defaultPublicDir; } diff --git a/Tests/Command/CacheClearCommand/CacheClearCommandTest.php b/Tests/Command/CacheClearCommand/CacheClearCommandTest.php index 78b13905e..b950b5fd9 100644 --- a/Tests/Command/CacheClearCommand/CacheClearCommandTest.php +++ b/Tests/Command/CacheClearCommand/CacheClearCommandTest.php @@ -75,7 +75,7 @@ function () use ($file) { $kernelRef = new \ReflectionObject($this->kernel); $kernelFile = $kernelRef->getFileName(); /** @var ResourceInterface[] $meta */ - $meta = unserialize(file_get_contents($containerMetaFile)); + $meta = unserialize($this->fs->readFile($containerMetaFile)); $found = false; foreach ($meta as $resource) { if ((string) $resource === $kernelFile) { @@ -93,7 +93,7 @@ function () use ($file) { ); $this->assertMatchesRegularExpression( sprintf('/\'kernel.container_class\'\s*=>\s*\'%s\'/', $containerClass), - file_get_contents($containerFile), + $this->fs->readFile($containerFile), 'kernel.container_class is properly set on the dumped container' ); } diff --git a/Tests/Secrets/SodiumVaultTest.php b/Tests/Secrets/SodiumVaultTest.php index 603d13504..96d5dcea1 100644 --- a/Tests/Secrets/SodiumVaultTest.php +++ b/Tests/Secrets/SodiumVaultTest.php @@ -21,16 +21,18 @@ class SodiumVaultTest extends TestCase { private string $secretsDir; + private Filesystem $filesystem; protected function setUp(): void { + $this->filesystem = new Filesystem(); $this->secretsDir = sys_get_temp_dir().'/sf_secrets/test/'; - (new Filesystem())->remove($this->secretsDir); + $this->filesystem->remove($this->secretsDir); } protected function tearDown(): void { - (new Filesystem())->remove($this->secretsDir); + $this->filesystem->remove($this->secretsDir); } public function testGenerateKeys() @@ -41,8 +43,8 @@ public function testGenerateKeys() $this->assertFileExists($this->secretsDir.'/test.encrypt.public.php'); $this->assertFileExists($this->secretsDir.'/test.decrypt.private.php'); - $encKey = file_get_contents($this->secretsDir.'/test.encrypt.public.php'); - $decKey = file_get_contents($this->secretsDir.'/test.decrypt.private.php'); + $encKey = $this->filesystem->readFile($this->secretsDir.'/test.encrypt.public.php'); + $decKey = $this->filesystem->readFile($this->secretsDir.'/test.decrypt.private.php'); $this->assertFalse($vault->generateKeys()); $this->assertStringEqualsFile($this->secretsDir.'/test.encrypt.public.php', $encKey); diff --git a/composer.json b/composer.json index 7b9d1b3e5..4d66cf9bc 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/polyfill-mbstring": "~1.0", - "symfony/filesystem": "^6.4|^7.0", + "symfony/filesystem": "^7.1", "symfony/finder": "^6.4|^7.0", "symfony/routing": "^6.4|^7.0" }, From facab7ae1656d3217684dbbfc3ee4ee78f787229 Mon Sep 17 00:00:00 2001 From: Nicolas Appriou Date: Wed, 1 Feb 2023 11:54:16 +0100 Subject: [PATCH 068/281] [FrameworkBundle][HttpFoundation] reduce response constraints verbosity --- Test/BrowserKitAssertionsTrait.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Test/BrowserKitAssertionsTrait.php b/Test/BrowserKitAssertionsTrait.php index 125aa45a7..7eea648cb 100644 --- a/Test/BrowserKitAssertionsTrait.php +++ b/Test/BrowserKitAssertionsTrait.php @@ -28,14 +28,14 @@ */ trait BrowserKitAssertionsTrait { - public static function assertResponseIsSuccessful(string $message = ''): void + public static function assertResponseIsSuccessful(string $message = '', bool $verbose = true): void { - self::assertThatForResponse(new ResponseConstraint\ResponseIsSuccessful(), $message); + self::assertThatForResponse(new ResponseConstraint\ResponseIsSuccessful($verbose), $message); } - public static function assertResponseStatusCodeSame(int $expectedCode, string $message = ''): void + public static function assertResponseStatusCodeSame(int $expectedCode, string $message = '', bool $verbose = true): void { - self::assertThatForResponse(new ResponseConstraint\ResponseStatusCodeSame($expectedCode), $message); + self::assertThatForResponse(new ResponseConstraint\ResponseStatusCodeSame($expectedCode, $verbose), $message); } public static function assertResponseFormatSame(?string $expectedFormat, string $message = ''): void @@ -43,9 +43,9 @@ public static function assertResponseFormatSame(?string $expectedFormat, string self::assertThatForResponse(new ResponseConstraint\ResponseFormatSame(self::getRequest(), $expectedFormat), $message); } - public static function assertResponseRedirects(?string $expectedLocation = null, ?int $expectedCode = null, string $message = ''): void + public static function assertResponseRedirects(?string $expectedLocation = null, ?int $expectedCode = null, string $message = '', bool $verbose = true): void { - $constraint = new ResponseConstraint\ResponseIsRedirected(); + $constraint = new ResponseConstraint\ResponseIsRedirected($verbose); if ($expectedLocation) { if (class_exists(ResponseConstraint\ResponseHeaderLocationSame::class)) { $locationConstraint = new ResponseConstraint\ResponseHeaderLocationSame(self::getRequest(), $expectedLocation); @@ -100,9 +100,9 @@ public static function assertResponseCookieValueSame(string $name, string $expec ), $message); } - public static function assertResponseIsUnprocessable(string $message = ''): void + public static function assertResponseIsUnprocessable(string $message = '', bool $verbose = true): void { - self::assertThatForResponse(new ResponseConstraint\ResponseIsUnprocessable(), $message); + self::assertThatForResponse(new ResponseConstraint\ResponseIsUnprocessable($verbose), $message); } public static function assertBrowserHasCookie(string $name, string $path = '/', ?string $domain = null, string $message = ''): void From 000203b325e8df22412c03d3e3f5d28f2d12be08 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Mon, 18 Mar 2024 20:27:13 +0100 Subject: [PATCH 069/281] chore: CS fixes --- Test/DomCrawlerAssertionsTrait.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Test/DomCrawlerAssertionsTrait.php b/Test/DomCrawlerAssertionsTrait.php index a16709461..024c78f75 100644 --- a/Test/DomCrawlerAssertionsTrait.php +++ b/Test/DomCrawlerAssertionsTrait.php @@ -26,12 +26,12 @@ trait DomCrawlerAssertionsTrait { public static function assertSelectorExists(string $selector, string $message = ''): void { - self::assertThat(self::getCrawler(), new DomCrawlerConstraint\CrawlerSelectorExists($selector), $message); + self::assertThat(self::getCrawler(), new CrawlerSelectorExists($selector), $message); } public static function assertSelectorNotExists(string $selector, string $message = ''): void { - self::assertThat(self::getCrawler(), new LogicalNot(new DomCrawlerConstraint\CrawlerSelectorExists($selector)), $message); + self::assertThat(self::getCrawler(), new LogicalNot(new CrawlerSelectorExists($selector)), $message); } public static function assertSelectorCount(int $expectedCount, string $selector, string $message = ''): void @@ -42,7 +42,7 @@ public static function assertSelectorCount(int $expectedCount, string $selector, public static function assertSelectorTextContains(string $selector, string $text, string $message = ''): void { self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( - new DomCrawlerConstraint\CrawlerSelectorExists($selector), + new CrawlerSelectorExists($selector), new DomCrawlerConstraint\CrawlerSelectorTextContains($selector, $text) ), $message); } @@ -50,7 +50,7 @@ public static function assertSelectorTextContains(string $selector, string $text public static function assertAnySelectorTextContains(string $selector, string $text, string $message = ''): void { self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( - new DomCrawlerConstraint\CrawlerSelectorExists($selector), + new CrawlerSelectorExists($selector), new DomCrawlerConstraint\CrawlerAnySelectorTextContains($selector, $text) ), $message); } @@ -58,7 +58,7 @@ public static function assertAnySelectorTextContains(string $selector, string $t public static function assertSelectorTextSame(string $selector, string $text, string $message = ''): void { self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( - new DomCrawlerConstraint\CrawlerSelectorExists($selector), + new CrawlerSelectorExists($selector), new DomCrawlerConstraint\CrawlerSelectorTextSame($selector, $text) ), $message); } @@ -66,7 +66,7 @@ public static function assertSelectorTextSame(string $selector, string $text, st public static function assertAnySelectorTextSame(string $selector, string $text, string $message = ''): void { self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( - new DomCrawlerConstraint\CrawlerSelectorExists($selector), + new CrawlerSelectorExists($selector), new DomCrawlerConstraint\CrawlerAnySelectorTextSame($selector, $text) ), $message); } @@ -74,7 +74,7 @@ public static function assertAnySelectorTextSame(string $selector, string $text, public static function assertSelectorTextNotContains(string $selector, string $text, string $message = ''): void { self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( - new DomCrawlerConstraint\CrawlerSelectorExists($selector), + new CrawlerSelectorExists($selector), new LogicalNot(new DomCrawlerConstraint\CrawlerSelectorTextContains($selector, $text)) ), $message); } @@ -82,7 +82,7 @@ public static function assertSelectorTextNotContains(string $selector, string $t public static function assertAnySelectorTextNotContains(string $selector, string $text, string $message = ''): void { self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( - new DomCrawlerConstraint\CrawlerSelectorExists($selector), + new CrawlerSelectorExists($selector), new LogicalNot(new DomCrawlerConstraint\CrawlerAnySelectorTextContains($selector, $text)) ), $message); } @@ -100,7 +100,7 @@ public static function assertPageTitleContains(string $expectedTitle, string $me public static function assertInputValueSame(string $fieldName, string $expectedValue, string $message = ''): void { self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( - new DomCrawlerConstraint\CrawlerSelectorExists("input[name=\"$fieldName\"]"), + new CrawlerSelectorExists("input[name=\"$fieldName\"]"), new DomCrawlerConstraint\CrawlerSelectorAttributeValueSame("input[name=\"$fieldName\"]", 'value', $expectedValue) ), $message); } @@ -108,7 +108,7 @@ public static function assertInputValueSame(string $fieldName, string $expectedV public static function assertInputValueNotSame(string $fieldName, string $expectedValue, string $message = ''): void { self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( - new DomCrawlerConstraint\CrawlerSelectorExists("input[name=\"$fieldName\"]"), + new CrawlerSelectorExists("input[name=\"$fieldName\"]"), new LogicalNot(new DomCrawlerConstraint\CrawlerSelectorAttributeValueSame("input[name=\"$fieldName\"]", 'value', $expectedValue)) ), $message); } From 911ba6ce0126245dce935cf713d120db63228911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Thu, 21 Mar 2024 11:47:10 +0100 Subject: [PATCH 070/281] [DependencyInjection] Apply attribute configurator to child classes --- DependencyInjection/FrameworkExtension.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 911242bc2..7d584af8b 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -699,9 +699,9 @@ public function load(array $configs, ContainerBuilder $container): void $taskAttributeClass, static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribute, \ReflectionClass|\ReflectionMethod $reflector): void { $tagAttributes = get_object_vars($attribute) + [ - 'trigger' => match ($attribute::class) { - AsPeriodicTask::class => 'every', - AsCronTask::class => 'cron', + 'trigger' => match (true) { + $attribute instanceof AsPeriodicTask => 'every', + $attribute instanceof AsCronTask => 'cron', }, ]; if ($reflector instanceof \ReflectionMethod) { From 05ed395cc169aa68a1ea8dca02508195a4861a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Sat, 23 Mar 2024 11:52:11 +0100 Subject: [PATCH 071/281] Remove custom handler for Workflow listener attributes --- DependencyInjection/FrameworkExtension.php | 23 ---------------------- composer.json | 2 +- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 7d584af8b..1fd7881f3 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -1104,29 +1104,6 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $container->setParameter('workflow.has_guard_listeners', true); } } - - $listenerAttributes = [ - Workflow\Attribute\AsAnnounceListener::class, - Workflow\Attribute\AsCompletedListener::class, - Workflow\Attribute\AsEnterListener::class, - Workflow\Attribute\AsEnteredListener::class, - Workflow\Attribute\AsGuardListener::class, - Workflow\Attribute\AsLeaveListener::class, - Workflow\Attribute\AsTransitionListener::class, - ]; - - foreach ($listenerAttributes as $attribute) { - $container->registerAttributeForAutoconfiguration($attribute, static function (ChildDefinition $definition, AsEventListener $attribute, \ReflectionClass|\ReflectionMethod $reflector) { - $tagAttributes = get_object_vars($attribute); - if ($reflector instanceof \ReflectionMethod) { - if (isset($tagAttributes['method'])) { - throw new LogicException(sprintf('"%s" attribute cannot declare a method on "%s::%s()".', $attribute::class, $reflector->class, $reflector->name)); - } - $tagAttributes['method'] = $reflector->getName(); - } - $definition->addTag('kernel.event_listener', $tagAttributes); - }); - } } private function registerDebugConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void diff --git a/composer.json b/composer.json index 4d66cf9bc..af934f35d 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "ext-xml": "*", "symfony/cache": "^6.4|^7.0", "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dependency-injection": "^7.1", "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.4|^7.0", "symfony/event-dispatcher": "^6.4|^7.0", From a39bf97648316cef7379aff34e5b6a86d00d2559 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 24 Mar 2024 21:56:27 +0100 Subject: [PATCH 072/281] [Security] Support multiple signature algorithms and JWK/JWKSet for OIDC tokens --- DependencyInjection/Compiler/UnusedTagsPass.php | 1 + 1 file changed, 1 insertion(+) diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index 6f53e57f0..a2a141afb 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -82,6 +82,7 @@ class UnusedTagsPass implements CompilerPassInterface 'routing.route_loader', 'scheduler.schedule_provider', 'scheduler.task', + 'security.access_token_handler.oidc.signature_algorithm', 'security.authenticator.login_linker', 'security.expression_language_provider', 'security.remember_me_handler', From 03c2c663a821df3881a1b2cc9e6ad6ab8a3951d1 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Thu, 26 Oct 2023 10:56:04 +0200 Subject: [PATCH 073/281] [PropertyInfo] Deprecate PropertyInfo Type Co-authored-by: Baptiste Leduc --- Tests/Functional/PropertyInfoTest.php | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/Tests/Functional/PropertyInfoTest.php b/Tests/Functional/PropertyInfoTest.php index c61955d37..18cd61b08 100644 --- a/Tests/Functional/PropertyInfoTest.php +++ b/Tests/Functional/PropertyInfoTest.php @@ -11,7 +11,8 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; -use Symfony\Component\PropertyInfo\Type; +use Symfony\Component\PropertyInfo\Type as LegacyType; +use Symfony\Component\TypeInfo\Type; class PropertyInfoTest extends AbstractWebTestCase { @@ -19,7 +20,29 @@ public function testPhpDocPriority() { static::bootKernel(['test_case' => 'Serializer']); - $this->assertEquals([new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_INT))], static::getContainer()->get('property_info')->getTypes('Symfony\Bundle\FrameworkBundle\Tests\Functional\Dummy', 'codes')); + $propertyInfo = static::getContainer()->get('property_info'); + + if (!method_exists($propertyInfo, 'getType')) { + $this->markTestSkipped(); + } + + $this->assertEquals(Type::list(Type::int()), $propertyInfo->getType(Dummy::class, 'codes')); + } + + /** + * @group legacy + */ + public function testPhpDocPriorityLegacy() + { + static::bootKernel(['test_case' => 'Serializer']); + + $propertyInfo = static::getContainer()->get('property_info'); + + if (!method_exists($propertyInfo, 'getTypes')) { + $this->markTestSkipped(); + } + + $this->assertEquals([new LegacyType('array', false, null, true, new LegacyType('int'), new LegacyType('int'))], $propertyInfo->getTypes(Dummy::class, 'codes')); } } From bc336a14228dbe02d49a51a0199abc2684955366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 23 Feb 2024 16:46:28 +0100 Subject: [PATCH 074/281] [Mailer] Add support for allowing some users even if `recipients` is defined in `EnvelopeListener` --- CHANGELOG.md | 2 ++ DependencyInjection/Configuration.php | 13 ++++++++++++- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/schema/symfony-1.0.xsd | 1 + .../Fixtures/php/mailer_with_dsn.php | 1 + .../Fixtures/php/mailer_with_transports.php | 1 + .../Fixtures/xml/mailer_with_dsn.xml | 1 + .../Fixtures/xml/mailer_with_transports.xml | 2 ++ .../Fixtures/yml/mailer_with_dsn.yml | 2 ++ .../Fixtures/yml/mailer_with_transports.yml | 3 +++ .../FrameworkExtensionTestCase.php | 5 ++++- 11 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52624c2cd..df0d692eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ CHANGELOG * Add `secrets:reveal` command * Add `rate_limiter` option to `http_client.default_options` and `http_client.scoped_clients` * Attach the workflow's configuration to the `workflow` tag + * Add the `allowed_recipients` option for mailer to allow some users to receive + emails even if `recipients` is defined. 7.0 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 8d54f74ef..92c20d139 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -2117,12 +2117,23 @@ private function addMailerSection(ArrayNodeDefinition $rootNode, callable $enabl ->arrayNode('envelope') ->info('Mailer Envelope configuration') ->fixXmlConfig('recipient') + ->fixXmlConfig('allowed_recipient') ->children() ->scalarNode('sender')->end() ->arrayNode('recipients') ->performNoDeepMerging() ->beforeNormalization() - ->ifArray() + ->ifArray() + ->then(fn ($v) => array_filter(array_values($v))) + ->end() + ->prototype('scalar')->end() + ->end() + ->arrayNode('allowed_recipients') + ->info('A list of regular expressions that allow recipients when "recipients" option is defined.') + ->example(['.*@example\.com']) + ->performNoDeepMerging() + ->beforeNormalization() + ->ifArray() ->then(fn ($v) => array_filter(array_values($v))) ->end() ->prototype('scalar')->end() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 1fd7881f3..86355e5ca 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2647,6 +2647,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co $envelopeListener = $container->getDefinition('mailer.envelope_listener'); $envelopeListener->setArgument(0, $config['envelope']['sender'] ?? null); $envelopeListener->setArgument(1, $config['envelope']['recipients'] ?? null); + $envelopeListener->setArgument(2, $config['envelope']['allowed_recipients'] ?? []); if ($config['headers']) { $headers = new Definition(Headers::class); diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index ad02ec370..d8d23168d 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -764,6 +764,7 @@ + diff --git a/Tests/DependencyInjection/Fixtures/php/mailer_with_dsn.php b/Tests/DependencyInjection/Fixtures/php/mailer_with_dsn.php index 683872982..3357bf354 100644 --- a/Tests/DependencyInjection/Fixtures/php/mailer_with_dsn.php +++ b/Tests/DependencyInjection/Fixtures/php/mailer_with_dsn.php @@ -13,6 +13,7 @@ 'envelope' => [ 'sender' => 'sender@example.org', 'recipients' => ['redirected@example.org'], + 'allowed_recipients' => ['foobar@example\.org'], ], 'headers' => [ 'from' => 'from@example.org', diff --git a/Tests/DependencyInjection/Fixtures/php/mailer_with_transports.php b/Tests/DependencyInjection/Fixtures/php/mailer_with_transports.php index 361fe731c..e51fd056b 100644 --- a/Tests/DependencyInjection/Fixtures/php/mailer_with_transports.php +++ b/Tests/DependencyInjection/Fixtures/php/mailer_with_transports.php @@ -16,6 +16,7 @@ 'envelope' => [ 'sender' => 'sender@example.org', 'recipients' => ['redirected@example.org', 'redirected1@example.org'], + 'allowed_recipients' => ['foobar@example\.org', '.*@example\.com'], ], 'headers' => [ 'from' => 'from@example.org', diff --git a/Tests/DependencyInjection/Fixtures/xml/mailer_with_dsn.xml b/Tests/DependencyInjection/Fixtures/xml/mailer_with_dsn.xml index 3436cf417..d48b7423a 100644 --- a/Tests/DependencyInjection/Fixtures/xml/mailer_with_dsn.xml +++ b/Tests/DependencyInjection/Fixtures/xml/mailer_with_dsn.xml @@ -13,6 +13,7 @@ sender@example.org redirected@example.org + foobar@example\.org from@example.org bcc1@example.org diff --git a/Tests/DependencyInjection/Fixtures/xml/mailer_with_transports.xml b/Tests/DependencyInjection/Fixtures/xml/mailer_with_transports.xml index 1cd8523b6..9bfd18d91 100644 --- a/Tests/DependencyInjection/Fixtures/xml/mailer_with_transports.xml +++ b/Tests/DependencyInjection/Fixtures/xml/mailer_with_transports.xml @@ -16,6 +16,8 @@ sender@example.org redirected@example.org redirected1@example.org + foobar@example\.org + .*@example\.com from@example.org bcc1@example.org diff --git a/Tests/DependencyInjection/Fixtures/yml/mailer_with_dsn.yml b/Tests/DependencyInjection/Fixtures/yml/mailer_with_dsn.yml index e826d6bdc..ea703bdad 100644 --- a/Tests/DependencyInjection/Fixtures/yml/mailer_with_dsn.yml +++ b/Tests/DependencyInjection/Fixtures/yml/mailer_with_dsn.yml @@ -10,6 +10,8 @@ framework: sender: sender@example.org recipients: - redirected@example.org + allowed_recipients: + - foobar@example\.org headers: from: from@example.org bcc: [bcc1@example.org, bcc2@example.org] diff --git a/Tests/DependencyInjection/Fixtures/yml/mailer_with_transports.yml b/Tests/DependencyInjection/Fixtures/yml/mailer_with_transports.yml index 59a5f14fd..ae10f6aee 100644 --- a/Tests/DependencyInjection/Fixtures/yml/mailer_with_transports.yml +++ b/Tests/DependencyInjection/Fixtures/yml/mailer_with_transports.yml @@ -13,6 +13,9 @@ framework: recipients: - redirected@example.org - redirected1@example.org + allowed_recipients: + - foobar@example\.org + - .*@example\.com headers: from: from@example.org bcc: [bcc1@example.org, bcc2@example.org] diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index dd836c06f..2a26e786b 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -2043,6 +2043,7 @@ public static function provideMailer(): iterable 'mailer_with_dsn', ['main' => 'smtp://example.com'], ['redirected@example.org'], + ['foobar@example\.org'], ]; yield [ 'mailer_with_transports', @@ -2051,13 +2052,14 @@ public static function provideMailer(): iterable 'transport2' => 'smtp://example2.com', ], ['redirected@example.org', 'redirected1@example.org'], + ['foobar@example\.org', '.*@example\.com'], ]; } /** * @dataProvider provideMailer */ - public function testMailer(string $configFile, array $expectedTransports, array $expectedRecipients) + public function testMailer(string $configFile, array $expectedTransports, array $expectedRecipients, array $expectedAllowedRecipients) { $container = $this->createContainerFromFile($configFile); @@ -2070,6 +2072,7 @@ public function testMailer(string $configFile, array $expectedTransports, array $l = $container->getDefinition('mailer.envelope_listener'); $this->assertSame('sender@example.org', $l->getArgument(0)); $this->assertSame($expectedRecipients, $l->getArgument(1)); + $this->assertSame($expectedAllowedRecipients, $l->getArgument(2)); $this->assertEquals(new Reference('messenger.default_bus', ContainerInterface::NULL_ON_INVALID_REFERENCE), $container->getDefinition('mailer.mailer')->getArgument(1)); $this->assertTrue($container->hasDefinition('mailer.message_listener')); From 1bda227a3a8482d2f3496a2e454b639d92846973 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 13 Apr 2024 09:53:50 +0200 Subject: [PATCH 075/281] forward a Clock instance to the created InMemoryTransport --- Resources/config/messenger.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Resources/config/messenger.php b/Resources/config/messenger.php index f71fcf344..df2476096 100644 --- a/Resources/config/messenger.php +++ b/Resources/config/messenger.php @@ -135,6 +135,9 @@ ->tag('messenger.transport_factory') ->set('messenger.transport.in_memory.factory', InMemoryTransportFactory::class) + ->args([ + service('clock')->nullOnInvalid(), + ]) ->tag('messenger.transport_factory') ->tag('kernel.reset', ['method' => 'reset']) From 68faa46eeb25f7e04fdbf38ee21444d23662cfce Mon Sep 17 00:00:00 2001 From: Andrey Lebedev Date: Wed, 20 Mar 2024 19:51:25 +0400 Subject: [PATCH 076/281] [Notifier] LOX24 SMS bridge --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_transports.php | 1 + 2 files changed, 2 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 911242bc2..74a1c71ea 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2788,6 +2788,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\LightSms\LightSmsTransportFactory::class => 'notifier.transport_factory.light-sms', NotifierBridge\LineNotify\LineNotifyTransportFactory::class => 'notifier.transport_factory.line-notify', NotifierBridge\LinkedIn\LinkedInTransportFactory::class => 'notifier.transport_factory.linked-in', + NotifierBridge\Lox24\Lox24TransportFactory::class => 'notifier.transport_factory.lox24', NotifierBridge\Mailjet\MailjetTransportFactory::class => 'notifier.transport_factory.mailjet', NotifierBridge\Mastodon\MastodonTransportFactory::class => 'notifier.transport_factory.mastodon', NotifierBridge\Mattermost\MattermostTransportFactory::class => 'notifier.transport_factory.mattermost', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index cebb92350..df9be94ed 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -75,6 +75,7 @@ 'isendpro' => Bridge\Isendpro\IsendproTransportFactory::class, 'kaz-info-teh' => Bridge\KazInfoTeh\KazInfoTehTransportFactory::class, 'light-sms' => Bridge\LightSms\LightSmsTransportFactory::class, + 'lox24' => Bridge\Lox24\Lox24TransportFactory::class, 'mailjet' => Bridge\Mailjet\MailjetTransportFactory::class, 'message-bird' => Bridge\MessageBird\MessageBirdTransportFactory::class, 'message-media' => Bridge\MessageMedia\MessageMediaTransportFactory::class, From 8db28484a473acc4aab94cf349ae139921226391 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Fri, 19 Apr 2024 20:47:43 +0200 Subject: [PATCH 077/281] [FrameworkBundle] Add support for setting `headers` with `TemplateController` --- CHANGELOG.md | 5 +++++ Controller/TemplateController.php | 7 ++++++- Tests/Controller/TemplateControllerTest.php | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df0d692eb..75ffef1a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.2 +--- + + * Add support for setting `headers` with `Symfony\Bundle\FrameworkBundle\Controller\TemplateController` + 7.1 --- diff --git a/Controller/TemplateController.php b/Controller/TemplateController.php index bcbcc382d..125e04f62 100644 --- a/Controller/TemplateController.php +++ b/Controller/TemplateController.php @@ -37,8 +37,9 @@ public function __construct( * @param bool|null $private Whether or not caching should apply for client caches only * @param array $context The context (arguments) of the template * @param int $statusCode The HTTP status code to return with the response (200 "OK" by default) + * @param array $headers The HTTP headers to add to the response */ - public function templateAction(string $template, ?int $maxAge = null, ?int $sharedAge = null, ?bool $private = null, array $context = [], int $statusCode = 200): Response + public function templateAction(string $template, ?int $maxAge = null, ?int $sharedAge = null, ?bool $private = null, array $context = [], int $statusCode = 200, array $headers = []): Response { if (null === $this->twig) { throw new \LogicException('You cannot use the TemplateController if the Twig Bundle is not available. Try running "composer require symfony/twig-bundle".'); @@ -60,6 +61,10 @@ public function templateAction(string $template, ?int $maxAge = null, ?int $shar $response->setPublic(); } + foreach ($headers as $key => $value) { + $response->headers->set($key, $value); + } + return $response; } diff --git a/Tests/Controller/TemplateControllerTest.php b/Tests/Controller/TemplateControllerTest.php index c972151d2..28f63ac01 100644 --- a/Tests/Controller/TemplateControllerTest.php +++ b/Tests/Controller/TemplateControllerTest.php @@ -84,4 +84,18 @@ public function testStatusCode() $this->assertSame(201, $controller->templateAction($templateName, null, null, null, [], $statusCode)->getStatusCode()); $this->assertSame(200, $controller->templateAction($templateName)->getStatusCode()); } + + public function testHeaders() + { + $templateName = 'image.svg.twig'; + + $loader = new ArrayLoader(); + $loader->setTemplate($templateName, ''); + + $twig = new Environment($loader); + $controller = new TemplateController($twig); + + $this->assertSame('image/svg+xml', $controller->templateAction($templateName, headers: ['Content-Type' => 'image/svg+xml'])->headers->get('Content-Type')); + $this->assertNull($controller->templateAction($templateName)->headers->get('Content-Type')); + } } From 766daee94c7fa35637c85eb4e8e77acd2bfea49b Mon Sep 17 00:00:00 2001 From: Samael tomas Date: Fri, 26 Apr 2024 14:48:25 +0200 Subject: [PATCH 078/281] [Notifier] Add Primotexto bridge --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_transports.php | 1 + 2 files changed, 2 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index d293ae9f4..039dda9d5 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2783,6 +2783,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\OvhCloud\OvhCloudTransportFactory::class => 'notifier.transport_factory.ovh-cloud', NotifierBridge\PagerDuty\PagerDutyTransportFactory::class => 'notifier.transport_factory.pager-duty', NotifierBridge\Plivo\PlivoTransportFactory::class => 'notifier.transport_factory.plivo', + NotifierBridge\Primotexto\PrimotextoTransportFactory::class => 'notifier.transport_factory.primotexto', NotifierBridge\Pushover\PushoverTransportFactory::class => 'notifier.transport_factory.pushover', NotifierBridge\Pushy\PushyTransportFactory::class => 'notifier.transport_factory.pushy', NotifierBridge\Redlink\RedlinkTransportFactory::class => 'notifier.transport_factory.redlink', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index df9be94ed..078adb960 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -87,6 +87,7 @@ 'orange-sms' => Bridge\OrangeSms\OrangeSmsTransportFactory::class, 'ovh-cloud' => Bridge\OvhCloud\OvhCloudTransportFactory::class, 'plivo' => Bridge\Plivo\PlivoTransportFactory::class, + 'primotexto' => Bridge\Primotexto\PrimotextoTransportFactory::class, 'pushover' => Bridge\Pushover\PushoverTransportFactory::class, 'pushy' => Bridge\Pushy\PushyTransportFactory::class, 'redlink' => Bridge\Redlink\RedlinkTransportFactory::class, From e8cba2142d7a62bae0b96c1f6cb20e4ad39dc864 Mon Sep 17 00:00:00 2001 From: Faizan Akram Date: Thu, 18 Apr 2024 18:37:37 +0200 Subject: [PATCH 079/281] [DependencyInjection] Reset env vars when resetting the container --- CHANGELOG.md | 1 + Resources/config/secrets.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index df0d692eb..4b0475167 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ CHANGELOG * Attach the workflow's configuration to the `workflow` tag * Add the `allowed_recipients` option for mailer to allow some users to receive emails even if `recipients` is defined. + * Reset env vars when resetting the container 7.0 --- diff --git a/Resources/config/secrets.php b/Resources/config/secrets.php index a21d28270..8192f2f06 100644 --- a/Resources/config/secrets.php +++ b/Resources/config/secrets.php @@ -13,6 +13,7 @@ use Symfony\Bundle\FrameworkBundle\Secrets\DotenvVault; use Symfony\Bundle\FrameworkBundle\Secrets\SodiumVault; +use Symfony\Component\DependencyInjection\StaticEnvVarLoader; return static function (ContainerConfigurator $container) { $container->services() @@ -21,6 +22,9 @@ abstract_arg('Secret dir, set in FrameworkExtension'), service('secrets.decryption_key')->ignoreOnInvalid(), ]) + + ->set('secrets.env_var_loader', StaticEnvVarLoader::class) + ->args([service('secrets.vault')]) ->tag('container.env_var_loader') ->set('secrets.decryption_key') From 59f4fb50d93f4526f49ab30a9b7fde7df7ab9ced Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 22 May 2024 17:24:31 +0200 Subject: [PATCH 080/281] [FrameworkBundle] Derivate kernel.secret from the decryption secret when its env var is not defined --- CHANGELOG.md | 1 + DependencyInjection/FrameworkExtension.php | 7 +++++-- Resources/config/secrets.php | 1 + Secrets/SodiumVault.php | 9 ++++++++- Tests/Secrets/SodiumVaultTest.php | 9 +++++++++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8309ce3e1..370786e61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add support for setting `headers` with `Symfony\Bundle\FrameworkBundle\Controller\TemplateController` + * Derivate `kernel.secret` from the decryption secret when its env var is not defined 7.1 --- diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 8786d04bd..5586e6653 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -372,7 +372,7 @@ public function load(array $configs, ContainerBuilder $container): void $this->registerDebugConfiguration($config['php_errors'], $container, $loader); $this->registerRouterConfiguration($config['router'], $container, $loader, $config['enabled_locales']); $this->registerPropertyAccessConfiguration($config['property_access'], $container, $loader); - $this->registerSecretsConfiguration($config['secrets'], $container, $loader); + $this->registerSecretsConfiguration($config['secrets'], $container, $loader, $config['secret'] ?? null); $container->getDefinition('exception_listener')->replaceArgument(3, $config['exceptions']); @@ -1755,7 +1755,7 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui ; } - private function registerSecretsConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void + private function registerSecretsConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, ?string $secret): void { if (!$this->readConfigEnabled('secrets', $container, $config)) { $container->removeDefinition('console.command.secrets_set'); @@ -1771,6 +1771,9 @@ private function registerSecretsConfiguration(array $config, ContainerBuilder $c $loader->load('secrets.php'); + $container->resolveEnvPlaceholders($secret, null, $usedEnvs); + $secretEnvVar = 1 === \count($usedEnvs ?? []) ? substr(key($usedEnvs), 1 + (strrpos(key($usedEnvs), ':') ?: -1)) : null; + $container->getDefinition('secrets.vault')->replaceArgument(2, $secretEnvVar); $container->getDefinition('secrets.vault')->replaceArgument(0, $config['vault_directory']); if ($config['local_dotenv_file']) { diff --git a/Resources/config/secrets.php b/Resources/config/secrets.php index 8192f2f06..a82f397b8 100644 --- a/Resources/config/secrets.php +++ b/Resources/config/secrets.php @@ -21,6 +21,7 @@ ->args([ abstract_arg('Secret dir, set in FrameworkExtension'), service('secrets.decryption_key')->ignoreOnInvalid(), + abstract_arg('Secret env var, set in FrameworkExtension'), ]) ->set('secrets.env_var_loader', StaticEnvVarLoader::class) diff --git a/Secrets/SodiumVault.php b/Secrets/SodiumVault.php index dcf79869f..f09c3b3af 100644 --- a/Secrets/SodiumVault.php +++ b/Secrets/SodiumVault.php @@ -26,16 +26,18 @@ class SodiumVault extends AbstractVault implements EnvVarLoaderInterface private string|\Stringable|null $decryptionKey = null; private string $pathPrefix; private ?string $secretsDir; + private ?string $derivedSecretEnvVar; /** * @param $decryptionKey A string or a stringable object that defines the private key to use to decrypt the vault * or null to store generated keys in the provided $secretsDir */ - public function __construct(string $secretsDir, #[\SensitiveParameter] string|\Stringable|null $decryptionKey = null) + public function __construct(string $secretsDir, #[\SensitiveParameter] string|\Stringable|null $decryptionKey = null, ?string $derivedSecretEnvVar = null) { $this->pathPrefix = rtrim(strtr($secretsDir, '/', \DIRECTORY_SEPARATOR), \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR.basename($secretsDir).'.'; $this->decryptionKey = $decryptionKey; $this->secretsDir = $secretsDir; + $this->derivedSecretEnvVar = $derivedSecretEnvVar; } public function generateKeys(bool $override = false): bool @@ -177,6 +179,11 @@ public function loadEnvVars(): array $envs[$name] = LazyString::fromCallable($reveal, $name); } + if ($this->derivedSecretEnvVar && !\array_key_exists($this->derivedSecretEnvVar, $envs)) { + $decryptionKey = $this->decryptionKey; + $envs[$this->derivedSecretEnvVar] = LazyString::fromCallable(static fn () => base64_encode(hash('sha256', $decryptionKey, true))); + } + return $envs; } diff --git a/Tests/Secrets/SodiumVaultTest.php b/Tests/Secrets/SodiumVaultTest.php index 96d5dcea1..e31e8364f 100644 --- a/Tests/Secrets/SodiumVaultTest.php +++ b/Tests/Secrets/SodiumVaultTest.php @@ -75,4 +75,13 @@ public function testEncryptAndDecrypt() $this->assertSame([], $vault->list()); } + + public function testDerivedSecretEnvVar() + { + $vault = new SodiumVault($this->secretsDir, null, 'MY_SECRET'); + $vault->generateKeys(); + $vault->seal('FOO', 'bar'); + + $this->assertSame(['FOO', 'MY_SECRET'], array_keys($vault->loadEnvVars())); + } } From 029743710a18f7bd47db7843d6aaf2788f0dc84f Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: Thu, 23 May 2024 13:24:31 +0200 Subject: [PATCH 081/281] Do not require `http_client` service This makes it possible to use AssetMapper with `http_client` disabled on the framework. Example: ```php $configurator->extension( 'framework', [ 'http_client' => [ 'enabled' => false, ], 'asset_mapper' => [ 'paths' => [ 'assets/', ], ], ] ); ``` --- Resources/config/asset_mapper.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/config/asset_mapper.php b/Resources/config/asset_mapper.php index b7ce65f03..ae7039db2 100644 --- a/Resources/config/asset_mapper.php +++ b/Resources/config/asset_mapper.php @@ -197,7 +197,7 @@ ]) ->set('asset_mapper.importmap.resolver', JsDelivrEsmResolver::class) - ->args([service('http_client')]) + ->args([service('http_client')->nullOnInvalid()]) ->set('asset_mapper.importmap.renderer', ImportMapRenderer::class) ->args([ @@ -212,12 +212,12 @@ ->set('asset_mapper.importmap.auditor', ImportMapAuditor::class) ->args([ service('asset_mapper.importmap.config_reader'), - service('http_client'), + service('http_client')->nullOnInvalid(), ]) ->set('asset_mapper.importmap.update_checker', ImportMapUpdateChecker::class) ->args([ service('asset_mapper.importmap.config_reader'), - service('http_client'), + service('http_client')->nullOnInvalid(), ]) ->set('asset_mapper.importmap.command.require', ImportMapRequireCommand::class) From e180d81181fd3bc0320180b4e05ba48b48771769 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Thu, 11 Apr 2024 18:13:33 +0200 Subject: [PATCH 082/281] [Translator] Add lint:translations command --- DependencyInjection/FrameworkExtension.php | 6 ++++++ Resources/config/console.php | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 5586e6653..b5f325ff3 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -165,6 +165,7 @@ use Symfony\Component\String\LazyString; use Symfony\Component\String\Slugger\SluggerInterface; use Symfony\Component\Translation\Bridge as TranslationBridge; +use Symfony\Component\Translation\Command\TranslationLintCommand as BaseTranslationLintCommand; use Symfony\Component\Translation\Command\XliffLintCommand as BaseXliffLintCommand; use Symfony\Component\Translation\Extractor\PhpAstExtractor; use Symfony\Component\Translation\LocaleSwitcher; @@ -245,6 +246,10 @@ public function load(array $configs, ContainerBuilder $container): void $container->removeDefinition('console.command.yaml_lint'); } + if (!class_exists(BaseTranslationLintCommand::class)) { + $container->removeDefinition('console.command.translation_lint'); + } + if (!class_exists(DebugCommand::class)) { $container->removeDefinition('console.command.dotenv_debug'); } @@ -1413,6 +1418,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder $container->removeDefinition('console.command.translation_extract'); $container->removeDefinition('console.command.translation_pull'); $container->removeDefinition('console.command.translation_push'); + $container->removeDefinition('console.command.translation_lint'); return; } diff --git a/Resources/config/console.php b/Resources/config/console.php index b4f7dfcf3..9df82e20e 100644 --- a/Resources/config/console.php +++ b/Resources/config/console.php @@ -54,6 +54,7 @@ use Symfony\Component\Messenger\Command\StopWorkersCommand; use Symfony\Component\Scheduler\Command\DebugCommand as SchedulerDebugCommand; use Symfony\Component\Serializer\Command\DebugCommand as SerializerDebugCommand; +use Symfony\Component\Translation\Command\TranslationLintCommand; use Symfony\Component\Translation\Command\TranslationPullCommand; use Symfony\Component\Translation\Command\TranslationPushCommand; use Symfony\Component\Translation\Command\XliffLintCommand; @@ -317,6 +318,13 @@ ->set('console.command.yaml_lint', YamlLintCommand::class) ->tag('console.command') + ->set('console.command.translation_lint', TranslationLintCommand::class) + ->args([ + service('translator'), + param('kernel.enabled_locales'), + ]) + ->tag('console.command') + ->set('console.command.form_debug', \Symfony\Component\Form\Command\DebugCommand::class) ->args([ service('form.registry'), From 408da06829094b30d891e26271502d03e35658ca Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 7 Jun 2024 13:36:04 +0200 Subject: [PATCH 083/281] change notifier type for brevo from chatter to texter --- Resources/config/notifier_transports.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index df9be94ed..5ddc9ae24 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -27,7 +27,6 @@ $chatterFactories = [ 'bluesky' => Bridge\Bluesky\BlueskyTransportFactory::class, - 'brevo' => Bridge\Brevo\BrevoTransportFactory::class, 'chatwork' => Bridge\Chatwork\ChatworkTransportFactory::class, 'discord' => Bridge\Discord\DiscordTransportFactory::class, 'fake-chat' => Bridge\FakeChat\FakeChatTransportFactory::class, @@ -59,6 +58,7 @@ $texterFactories = [ 'all-my-sms' => Bridge\AllMySms\AllMySmsTransportFactory::class, 'bandwidth' => Bridge\Bandwidth\BandwidthTransportFactory::class, + 'brevo' => Bridge\Brevo\BrevoTransportFactory::class, 'click-send' => Bridge\ClickSend\ClickSendTransportFactory::class, 'clickatell' => Bridge\Clickatell\ClickatellTransportFactory::class, 'contact-everyone' => Bridge\ContactEveryone\ContactEveryoneTransportFactory::class, From a2c2e3c06f37bc7db3f08e9d91d5b9d5fb0c5022 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 17 Jun 2024 23:57:43 +0200 Subject: [PATCH 084/281] inject the missing logger service --- DependencyInjection/FrameworkExtension.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 437d5402a..3c82b4b0c 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2855,6 +2855,11 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ ->addArgument(new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE)); } + if (ContainerBuilder::willBeAvailable('symfony/bluesky-notifier', NotifierBridge\Bluesky\BlueskyTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier'])) { + $container->getDefinition($classToServices[NotifierBridge\Bluesky\BlueskyTransportFactory::class]) + ->addArgument(new Reference('logger')); + } + if (isset($config['admin_recipients'])) { $notifier = $container->getDefinition('notifier'); foreach ($config['admin_recipients'] as $i => $recipient) { From 3b777e2fcb3ed5946ebb408413edbf8c593d3e2d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 17 Jun 2024 23:56:05 +0200 Subject: [PATCH 085/281] wire a clock for the BlueSky transport in the FrameworkBundle --- DependencyInjection/FrameworkExtension.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index b5d7930ac..942358181 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2865,6 +2865,12 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ ->addArgument(new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE)); } + if (ContainerBuilder::willBeAvailable('symfony/bluesky-notifier', NotifierBridge\Bluesky\BlueskyTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier'])) { + $container->getDefinition($classToServices[NotifierBridge\Bluesky\BlueskyTransportFactory::class]) + ->addArgument(new Reference('logger')) + ->addArgument(new Reference('clock', ContainerBuilder::NULL_ON_INVALID_REFERENCE)); + } + if (isset($config['admin_recipients'])) { $notifier = $container->getDefinition('notifier'); foreach ($config['admin_recipients'] as $i => $recipient) { From da2a0363c4d46f980e6abc0c48301820d38a8c36 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Sat, 15 Jun 2024 00:52:50 -0400 Subject: [PATCH 086/281] Simpler kernel setup with MicroKernelTrait --- CHANGELOG.md | 2 ++ Kernel/MicroKernelTrait.php | 17 +++++++++++++---- Tests/Kernel/ConcreteMicroKernel.php | 8 -------- Tests/Kernel/MicroKernelTraitTest.php | 17 +++++++++++------ Tests/Kernel/SimpleKernel.php | 25 +++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 Tests/Kernel/SimpleKernel.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 370786e61..f2470d542 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ CHANGELOG * Add support for setting `headers` with `Symfony\Bundle\FrameworkBundle\Controller\TemplateController` * Derivate `kernel.secret` from the decryption secret when its env var is not defined + * Make the `config/` directory optional in `MicroKernelTrait`, add support for service arguments in the + invokable Kernel class, and register `FrameworkBundle` by default when the `bundles.php` file is missing 7.1 --- diff --git a/Kernel/MicroKernelTrait.php b/Kernel/MicroKernelTrait.php index b3f49c059..9e8237527 100644 --- a/Kernel/MicroKernelTrait.php +++ b/Kernel/MicroKernelTrait.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Kernel; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\Configurator\AbstractConfigurator; @@ -48,7 +49,7 @@ trait MicroKernelTrait */ private function configureContainer(ContainerConfigurator $container, LoaderInterface $loader, ContainerBuilder $builder): void { - $configDir = $this->getConfigDir(); + $configDir = preg_replace('{/config$}', '/{config}', $this->getConfigDir()); $container->import($configDir.'/{packages}/*.{php,yaml}'); $container->import($configDir.'/{packages}/'.$this->environment.'/*.{php,yaml}'); @@ -73,7 +74,7 @@ private function configureContainer(ContainerConfigurator $container, LoaderInte */ private function configureRoutes(RoutingConfigurator $routes): void { - $configDir = $this->getConfigDir(); + $configDir = preg_replace('{/config$}', '/{config}', $this->getConfigDir()); $routes->import($configDir.'/{routes}/'.$this->environment.'/*.{php,yaml}'); $routes->import($configDir.'/{routes}/*.{php,yaml}'); @@ -84,7 +85,7 @@ private function configureRoutes(RoutingConfigurator $routes): void $routes->import($configDir.'/{routes}.php'); } - if (false !== ($fileName = (new \ReflectionObject($this))->getFileName())) { + if ($fileName = (new \ReflectionObject($this))->getFileName()) { $routes->import($fileName, 'attribute'); } } @@ -130,7 +131,13 @@ public function getLogDir(): string public function registerBundles(): iterable { - $contents = require $this->getBundlesPath(); + if (!is_file($bundlesPath = $this->getBundlesPath())) { + yield new FrameworkBundle(); + + return; + } + + $contents = require $bundlesPath; foreach ($contents as $class => $envs) { if ($envs[$this->environment] ?? $envs['all'] ?? false) { yield new $class(); @@ -216,6 +223,8 @@ public function loadRoutes(LoaderInterface $loader): RouteCollection $route->setDefault('_controller', ['kernel', $controller[1]]); } elseif ($controller instanceof \Closure && $this === ($r = new \ReflectionFunction($controller))->getClosureThis() && !$r->isAnonymous()) { $route->setDefault('_controller', ['kernel', $r->name]); + } elseif ($this::class === $controller && method_exists($this, '__invoke')) { + $route->setDefault('_controller', 'kernel'); } } diff --git a/Tests/Kernel/ConcreteMicroKernel.php b/Tests/Kernel/ConcreteMicroKernel.php index fc5149699..a69618099 100644 --- a/Tests/Kernel/ConcreteMicroKernel.php +++ b/Tests/Kernel/ConcreteMicroKernel.php @@ -12,7 +12,6 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel; use Psr\Log\NullLogger; -use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -46,13 +45,6 @@ public function dangerousAction() throw new Danger(); } - public function registerBundles(): iterable - { - return [ - new FrameworkBundle(), - ]; - } - public function getCacheDir(): string { return $this->cacheDir = sys_get_temp_dir().'/sf_micro_kernel'; diff --git a/Tests/Kernel/MicroKernelTraitTest.php b/Tests/Kernel/MicroKernelTraitTest.php index 792acf5ef..eaa11eebe 100644 --- a/Tests/Kernel/MicroKernelTraitTest.php +++ b/Tests/Kernel/MicroKernelTraitTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Psr\Log\NullLogger; -use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; @@ -140,6 +139,17 @@ protected function configureRoutes(RoutingConfigurator $routes): void $this->assertSame('Hello World!', $response->getContent()); } + + public function testSimpleKernel() + { + $kernel = $this->kernel = new SimpleKernel('simple_kernel'); + $kernel->boot(); + + $request = Request::create('/'); + $response = $kernel->handle($request, HttpKernelInterface::MAIN_REQUEST, false); + + $this->assertSame('Hello World!', $response->getContent()); + } } abstract class MinimalKernel extends Kernel @@ -155,11 +165,6 @@ public function __construct(string $cacheDir) $this->cacheDir = sys_get_temp_dir().'/'.$cacheDir; } - public function registerBundles(): iterable - { - yield new FrameworkBundle(); - } - public function getCacheDir(): string { return $this->cacheDir; diff --git a/Tests/Kernel/SimpleKernel.php b/Tests/Kernel/SimpleKernel.php new file mode 100644 index 000000000..f586e2dc5 --- /dev/null +++ b/Tests/Kernel/SimpleKernel.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +final class SimpleKernel extends MinimalKernel +{ + #[Route('/')] + public function __invoke(UrlGeneratorInterface $urlGenerator): Response + { + return new Response('Hello World!'); + } +} From cabdf4bfc1a2df7f3ec57893621a0af04139ffb8 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Wed, 19 Jun 2024 15:52:13 -0400 Subject: [PATCH 087/281] Fix kernel configuration loading --- Kernel/MicroKernelTrait.php | 4 +- Tests/Kernel/MicroKernelTraitTest.php | 14 +++++++ Tests/Kernel/default/config/bundles.php | 16 ++++++++ Tests/Kernel/default/config/routes.yaml | 3 ++ Tests/Kernel/default/config/services.yaml | 4 ++ Tests/Kernel/default/src/DefaultKernel.php | 43 ++++++++++++++++++++++ 6 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 Tests/Kernel/default/config/bundles.php create mode 100644 Tests/Kernel/default/config/routes.yaml create mode 100644 Tests/Kernel/default/config/services.yaml create mode 100644 Tests/Kernel/default/src/DefaultKernel.php diff --git a/Kernel/MicroKernelTrait.php b/Kernel/MicroKernelTrait.php index 9e8237527..f40373a30 100644 --- a/Kernel/MicroKernelTrait.php +++ b/Kernel/MicroKernelTrait.php @@ -54,7 +54,7 @@ private function configureContainer(ContainerConfigurator $container, LoaderInte $container->import($configDir.'/{packages}/*.{php,yaml}'); $container->import($configDir.'/{packages}/'.$this->environment.'/*.{php,yaml}'); - if (is_file($configDir.'/services.yaml')) { + if (is_file($this->getConfigDir().'/services.yaml')) { $container->import($configDir.'/services.yaml'); $container->import($configDir.'/{services}_'.$this->environment.'.yaml'); } else { @@ -79,7 +79,7 @@ private function configureRoutes(RoutingConfigurator $routes): void $routes->import($configDir.'/{routes}/'.$this->environment.'/*.{php,yaml}'); $routes->import($configDir.'/{routes}/*.{php,yaml}'); - if (is_file($configDir.'/routes.yaml')) { + if (is_file($this->getConfigDir().'/routes.yaml')) { $routes->import($configDir.'/routes.yaml'); } else { $routes->import($configDir.'/{routes}.php'); diff --git a/Tests/Kernel/MicroKernelTraitTest.php b/Tests/Kernel/MicroKernelTraitTest.php index eaa11eebe..a9d2ae720 100644 --- a/Tests/Kernel/MicroKernelTraitTest.php +++ b/Tests/Kernel/MicroKernelTraitTest.php @@ -25,6 +25,7 @@ use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; +require_once __DIR__.'/default/src/DefaultKernel.php'; require_once __DIR__.'/flex-style/src/FlexStyleMicroKernel.php'; class MicroKernelTraitTest extends TestCase @@ -150,6 +151,19 @@ public function testSimpleKernel() $this->assertSame('Hello World!', $response->getContent()); } + + public function testDefaultKernel() + { + $kernel = $this->kernel = new DefaultKernel('test', false); + $kernel->boot(); + + $this->assertTrue($kernel->getContainer()->has('foo_service')); + + $request = Request::create('/'); + $response = $kernel->handle($request, HttpKernelInterface::MAIN_REQUEST, false); + + $this->assertSame('OK', $response->getContent()); + } } abstract class MinimalKernel extends Kernel diff --git a/Tests/Kernel/default/config/bundles.php b/Tests/Kernel/default/config/bundles.php new file mode 100644 index 000000000..fbde1ef1c --- /dev/null +++ b/Tests/Kernel/default/config/bundles.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; + +return [ + FrameworkBundle::class => ['all' => true], +]; diff --git a/Tests/Kernel/default/config/routes.yaml b/Tests/Kernel/default/config/routes.yaml new file mode 100644 index 000000000..5653fe0b9 --- /dev/null +++ b/Tests/Kernel/default/config/routes.yaml @@ -0,0 +1,3 @@ +welcome: + path: / + controller: 'kernel' diff --git a/Tests/Kernel/default/config/services.yaml b/Tests/Kernel/default/config/services.yaml new file mode 100644 index 000000000..fa0d7df8e --- /dev/null +++ b/Tests/Kernel/default/config/services.yaml @@ -0,0 +1,4 @@ +services: + foo_service: + class: stdClass + public: true diff --git a/Tests/Kernel/default/src/DefaultKernel.php b/Tests/Kernel/default/src/DefaultKernel.php new file mode 100644 index 000000000..93d120793 --- /dev/null +++ b/Tests/Kernel/default/src/DefaultKernel.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel; + +use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Kernel; + +class DefaultKernel extends Kernel +{ + use MicroKernelTrait; + + public function __invoke(): Response + { + return new Response('OK'); + } + + private string $cacheDir; + + public function getCacheDir(): string + { + return $this->cacheDir ??= sys_get_temp_dir().'/sf_default_kernel'; + } + + public function getLogDir(): string + { + return $this->cacheDir; + } + + public function getProjectDir(): string + { + return \dirname(__DIR__); + } +} From af5de4e04cd54dd7e3ac42a32c18f8ad545a0832 Mon Sep 17 00:00:00 2001 From: Patrick Landolt Date: Sun, 9 Jun 2024 17:30:25 +0200 Subject: [PATCH 088/281] [Mailer] Add mailomat bridge --- DependencyInjection/FrameworkExtension.php | 2 ++ Resources/config/mailer_transports.php | 2 ++ Resources/config/mailer_webhook.php | 7 +++++++ 3 files changed, 11 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 942358181..7119002cb 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2620,6 +2620,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co MailerBridge\MailerSend\Transport\MailerSendTransportFactory::class => 'mailer.transport_factory.mailersend', MailerBridge\Mailgun\Transport\MailgunTransportFactory::class => 'mailer.transport_factory.mailgun', MailerBridge\Mailjet\Transport\MailjetTransportFactory::class => 'mailer.transport_factory.mailjet', + MailerBridge\Mailomat\Transport\MailomatTransportFactory::class => 'mailer.transport_factory.mailomat', MailerBridge\MailPace\Transport\MailPaceTransportFactory::class => 'mailer.transport_factory.mailpace', MailerBridge\Mailchimp\Transport\MandrillTransportFactory::class => 'mailer.transport_factory.mailchimp', MailerBridge\Postmark\Transport\PostmarkTransportFactory::class => 'mailer.transport_factory.postmark', @@ -2643,6 +2644,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co MailerBridge\MailerSend\Webhook\MailerSendRequestParser::class => 'mailer.webhook.request_parser.mailersend', MailerBridge\Mailgun\Webhook\MailgunRequestParser::class => 'mailer.webhook.request_parser.mailgun', MailerBridge\Mailjet\Webhook\MailjetRequestParser::class => 'mailer.webhook.request_parser.mailjet', + MailerBridge\Mailomat\Webhook\MailomatRequestParser::class => 'mailer.webhook.request_parser.mailomat', MailerBridge\Postmark\Webhook\PostmarkRequestParser::class => 'mailer.webhook.request_parser.postmark', MailerBridge\Resend\Webhook\ResendRequestParser::class => 'mailer.webhook.request_parser.resend', MailerBridge\Sendgrid\Webhook\SendgridRequestParser::class => 'mailer.webhook.request_parser.sendgrid', diff --git a/Resources/config/mailer_transports.php b/Resources/config/mailer_transports.php index 5434b4c56..bdcd7e9c6 100644 --- a/Resources/config/mailer_transports.php +++ b/Resources/config/mailer_transports.php @@ -20,6 +20,7 @@ use Symfony\Component\Mailer\Bridge\MailerSend\Transport\MailerSendTransportFactory; use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory; use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetTransportFactory; +use Symfony\Component\Mailer\Bridge\Mailomat\Transport\MailomatTransportFactory; use Symfony\Component\Mailer\Bridge\MailPace\Transport\MailPaceTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; use Symfony\Component\Mailer\Bridge\Resend\Transport\ResendTransportFactory; @@ -52,6 +53,7 @@ 'mailersend' => MailerSendTransportFactory::class, 'mailgun' => MailgunTransportFactory::class, 'mailjet' => MailjetTransportFactory::class, + 'mailomat' => MailomatTransportFactory::class, 'mailpace' => MailPaceTransportFactory::class, 'native' => NativeTransportFactory::class, 'null' => NullTransportFactory::class, diff --git a/Resources/config/mailer_webhook.php b/Resources/config/mailer_webhook.php index f9d2b9686..64020c1b1 100644 --- a/Resources/config/mailer_webhook.php +++ b/Resources/config/mailer_webhook.php @@ -19,6 +19,8 @@ use Symfony\Component\Mailer\Bridge\Mailgun\Webhook\MailgunRequestParser; use Symfony\Component\Mailer\Bridge\Mailjet\RemoteEvent\MailjetPayloadConverter; use Symfony\Component\Mailer\Bridge\Mailjet\Webhook\MailjetRequestParser; +use Symfony\Component\Mailer\Bridge\Mailomat\RemoteEvent\MailomatPayloadConverter; +use Symfony\Component\Mailer\Bridge\Mailomat\Webhook\MailomatRequestParser; use Symfony\Component\Mailer\Bridge\Postmark\RemoteEvent\PostmarkPayloadConverter; use Symfony\Component\Mailer\Bridge\Postmark\Webhook\PostmarkRequestParser; use Symfony\Component\Mailer\Bridge\Resend\RemoteEvent\ResendPayloadConverter; @@ -48,6 +50,11 @@ ->args([service('mailer.payload_converter.mailjet')]) ->alias(MailjetRequestParser::class, 'mailer.webhook.request_parser.mailjet') + ->set('mailer.payload_converter.mailomat', MailomatPayloadConverter::class) + ->set('mailer.webhook.request_parser.mailomat', MailomatRequestParser::class) + ->args([service('mailer.payload_converter.mailomat')]) + ->alias(MailomatRequestParser::class, 'mailer.webhook.request_parser.mailomat') + ->set('mailer.payload_converter.postmark', PostmarkPayloadConverter::class) ->set('mailer.webhook.request_parser.postmark', PostmarkRequestParser::class) ->args([service('mailer.payload_converter.postmark')]) From 4875a8487ff2d8d577500fc135b8fa570f845a0a Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 20 Jun 2024 17:52:34 +0200 Subject: [PATCH 089/281] Prefix all sprintf() calls --- CacheWarmer/RouterCacheWarmer.php | 2 +- Command/AbstractConfigCommand.php | 12 +-- Command/AssetsInstallCommand.php | 12 +-- Command/CacheClearCommand.php | 8 +- Command/CachePoolClearCommand.php | 10 +-- Command/CachePoolDeleteCommand.php | 6 +- Command/CachePoolInvalidateTagsCommand.php | 8 +- Command/CachePoolPruneCommand.php | 2 +- Command/CacheWarmupCommand.php | 4 +- Command/ConfigDebugCommand.php | 14 ++-- Command/ConfigDumpReferenceCommand.php | 16 ++-- Command/ContainerDebugCommand.php | 12 +-- Command/ContainerLintCommand.php | 4 +- Command/DebugAutowiringCommand.php | 10 +-- Command/EventDispatcherDebugCommand.php | 6 +- Command/RouterDebugCommand.php | 4 +- Command/RouterMatchCommand.php | 8 +- Command/SecretsDecryptToLocalCommand.php | 6 +- Command/SecretsListCommand.php | 2 +- Command/SecretsRevealCommand.php | 2 +- Command/SecretsSetCommand.php | 6 +- Command/TranslationDebugCommand.php | 12 +-- Command/TranslationUpdateCommand.php | 14 ++-- Command/WorkflowDumpCommand.php | 2 +- Console/Application.php | 2 +- Console/Descriptor/Descriptor.php | 4 +- Console/Descriptor/JsonDescriptor.php | 8 +- Console/Descriptor/MarkdownDescriptor.php | 64 +++++++-------- Console/Descriptor/TextDescriptor.php | 70 ++++++++--------- Console/Descriptor/XmlDescriptor.php | 8 +- Controller/AbstractController.php | 6 +- Controller/ControllerResolver.php | 2 +- Controller/RedirectController.php | 4 +- DependencyInjection/Compiler/ProfilerPass.php | 2 +- .../Compiler/UnusedTagsPass.php | 4 +- DependencyInjection/Configuration.php | 6 +- DependencyInjection/FrameworkExtension.php | 78 +++++++++---------- EventListener/ConsoleProfilerListener.php | 2 +- .../SuggestMissingPackageSubscriber.php | 2 +- KernelBrowser.php | 4 +- Resources/config/cache.php | 4 +- Resources/config/collectors.php | 2 +- Resources/config/services.php | 2 +- Routing/Router.php | 6 +- Secrets/AbstractVault.php | 2 +- Secrets/DotenvVault.php | 8 +- Secrets/SodiumVault.php | 28 +++---- Test/BrowserKitAssertionsTrait.php | 2 +- Test/DomCrawlerAssertionsTrait.php | 8 +- Test/HttpClientAssertionsTrait.php | 6 +- Test/KernelTestCase.php | 4 +- Test/TestContainer.php | 2 +- Test/WebTestCase.php | 2 +- .../CacheClearCommandTest.php | 4 +- .../Descriptor/AbstractDescriptorTestCase.php | 8 +- .../Console/Descriptor/TextDescriptorTest.php | 2 +- Tests/Controller/TestAbstractController.php | 4 +- .../Compiler/UnusedTagsPassTest.php | 2 +- .../FrameworkExtensionTestCase.php | 22 +++--- .../Controller/SessionController.php | 4 +- .../Functional/CacheAttributeListenerTest.php | 4 +- Tests/Functional/ConfigDebugCommandTest.php | 2 +- .../Functional/ContainerDebugCommandTest.php | 6 +- Tests/Functional/app/AppKernel.php | 6 +- Translation/Translator.php | 2 +- 65 files changed, 295 insertions(+), 295 deletions(-) diff --git a/CacheWarmer/RouterCacheWarmer.php b/CacheWarmer/RouterCacheWarmer.php index eed548046..149d20ce3 100644 --- a/CacheWarmer/RouterCacheWarmer.php +++ b/CacheWarmer/RouterCacheWarmer.php @@ -46,7 +46,7 @@ public function warmUp(string $cacheDir, ?string $buildDir = null): array return (array) $router->warmUp($cacheDir, $buildDir); } - throw new \LogicException(sprintf('The router "%s" cannot be warmed up because it does not implement "%s".', get_debug_type($router), WarmableInterface::class)); + throw new \LogicException(\sprintf('The router "%s" cannot be warmed up because it does not implement "%s".', get_debug_type($router), WarmableInterface::class)); } public function isOptional(): bool diff --git a/Command/AbstractConfigCommand.php b/Command/AbstractConfigCommand.php index 479bbfe6a..fc3433c2d 100644 --- a/Command/AbstractConfigCommand.php +++ b/Command/AbstractConfigCommand.php @@ -114,7 +114,7 @@ protected function findExtension(string $name): ExtensionInterface foreach ($bundles as $bundle) { if ($name === $bundle->getName()) { if (!$bundle->getContainerExtension()) { - throw new \LogicException(sprintf('Bundle "%s" does not have a container extension.', $name)); + throw new \LogicException(\sprintf('Bundle "%s" does not have a container extension.', $name)); } return $bundle->getContainerExtension(); @@ -144,13 +144,13 @@ protected function findExtension(string $name): ExtensionInterface } if (!str_ends_with($name, 'Bundle')) { - $message = sprintf('No extensions with configuration available for "%s".', $name); + $message = \sprintf('No extensions with configuration available for "%s".', $name); } else { - $message = sprintf('No extension with alias "%s" is enabled.', $name); + $message = \sprintf('No extension with alias "%s" is enabled.', $name); } if (isset($guess) && $minScore < 3) { - $message .= sprintf("\n\nDid you mean \"%s\"?", $guess); + $message .= \sprintf("\n\nDid you mean \"%s\"?", $guess); } throw new LogicException($message); @@ -159,11 +159,11 @@ protected function findExtension(string $name): ExtensionInterface public function validateConfiguration(ExtensionInterface $extension, mixed $configuration): void { if (!$configuration) { - throw new \LogicException(sprintf('The extension with alias "%s" does not have its getConfiguration() method setup.', $extension->getAlias())); + throw new \LogicException(\sprintf('The extension with alias "%s" does not have its getConfiguration() method setup.', $extension->getAlias())); } if (!$configuration instanceof ConfigurationInterface) { - throw new \LogicException(sprintf('Configuration class "%s" should implement ConfigurationInterface in order to be dumpable.', get_debug_type($configuration))); + throw new \LogicException(\sprintf('Configuration class "%s" should implement ConfigurationInterface in order to be dumpable.', get_debug_type($configuration))); } } diff --git a/Command/AssetsInstallCommand.php b/Command/AssetsInstallCommand.php index 32b38de9a..5dc8c828e 100644 --- a/Command/AssetsInstallCommand.php +++ b/Command/AssetsInstallCommand.php @@ -93,7 +93,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $targetArg = $kernel->getProjectDir().'/'.$targetArg; if (!is_dir($targetArg)) { - throw new InvalidArgumentException(sprintf('The target directory "%s" does not exist.', $targetArg)); + throw new InvalidArgumentException(\sprintf('The target directory "%s" does not exist.', $targetArg)); } } @@ -130,7 +130,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $validAssetDirs[] = $assetDir; if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { - $message = sprintf("%s\n-> %s", $bundle->getName(), $targetDir); + $message = \sprintf("%s\n-> %s", $bundle->getName(), $targetDir); } else { $message = $bundle->getName(); } @@ -151,13 +151,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int } if ($method === $expectedMethod) { - $rows[] = [sprintf('%s', '\\' === \DIRECTORY_SEPARATOR ? 'OK' : "\xE2\x9C\x94" /* HEAVY CHECK MARK (U+2714) */), $message, $method]; + $rows[] = [\sprintf('%s', '\\' === \DIRECTORY_SEPARATOR ? 'OK' : "\xE2\x9C\x94" /* HEAVY CHECK MARK (U+2714) */), $message, $method]; } else { - $rows[] = [sprintf('%s', '\\' === \DIRECTORY_SEPARATOR ? 'WARNING' : '!'), $message, $method]; + $rows[] = [\sprintf('%s', '\\' === \DIRECTORY_SEPARATOR ? 'WARNING' : '!'), $message, $method]; } } catch (\Exception $e) { $exitCode = 1; - $rows[] = [sprintf('%s', '\\' === \DIRECTORY_SEPARATOR ? 'ERROR' : "\xE2\x9C\x98" /* HEAVY BALLOT X (U+2718) */), $message, $e->getMessage()]; + $rows[] = [\sprintf('%s', '\\' === \DIRECTORY_SEPARATOR ? 'ERROR' : "\xE2\x9C\x98" /* HEAVY BALLOT X (U+2718) */), $message, $e->getMessage()]; } } // remove the assets of the bundles that no longer exist @@ -230,7 +230,7 @@ private function symlink(string $originDir, string $targetDir, bool $relative = } $this->filesystem->symlink($originDir, $targetDir); if (!file_exists($targetDir)) { - throw new IOException(sprintf('Symbolic link "%s" was created but appears to be broken.', $targetDir), 0, null, $targetDir); + throw new IOException(\sprintf('Symbolic link "%s" was created but appears to be broken.', $targetDir), 0, null, $targetDir); } } diff --git a/Command/CacheClearCommand.php b/Command/CacheClearCommand.php index 55813664b..4ef2f5a1d 100644 --- a/Command/CacheClearCommand.php +++ b/Command/CacheClearCommand.php @@ -80,7 +80,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $fs->remove($oldCacheDir); if (!is_writable($realCacheDir)) { - throw new RuntimeException(sprintf('Unable to write in the "%s" directory.', $realCacheDir)); + throw new RuntimeException(\sprintf('Unable to write in the "%s" directory.', $realCacheDir)); } $useBuildDir = $realBuildDir !== $realCacheDir; @@ -89,7 +89,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $fs->remove($oldBuildDir); if (!is_writable($realBuildDir)) { - throw new RuntimeException(sprintf('Unable to write in the "%s" directory.', $realBuildDir)); + throw new RuntimeException(\sprintf('Unable to write in the "%s" directory.', $realBuildDir)); } if ($this->isNfs($realCacheDir)) { @@ -100,7 +100,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $fs->mkdir($realCacheDir); } - $io->comment(sprintf('Clearing the cache for the %s environment with debug %s', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + $io->comment(\sprintf('Clearing the cache for the %s environment with debug %s', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); if ($useBuildDir) { $this->cacheClearer->clear($realBuildDir); } @@ -189,7 +189,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->comment('Finished'); } - $io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully cleared.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + $io->success(\sprintf('Cache for the "%s" environment (debug=%s) was successfully cleared.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); return 0; } diff --git a/Command/CachePoolClearCommand.php b/Command/CachePoolClearCommand.php index d5320e7a9..5d840e597 100644 --- a/Command/CachePoolClearCommand.php +++ b/Command/CachePoolClearCommand.php @@ -95,28 +95,28 @@ protected function execute(InputInterface $input, OutputInterface $output): int } elseif ($pool instanceof Psr6CacheClearer) { $clearers[$id] = $pool; } else { - throw new InvalidArgumentException(sprintf('"%s" is not a cache pool nor a cache clearer.', $id)); + throw new InvalidArgumentException(\sprintf('"%s" is not a cache pool nor a cache clearer.', $id)); } } } foreach ($clearers as $id => $clearer) { - $io->comment(sprintf('Calling cache clearer: %s', $id)); + $io->comment(\sprintf('Calling cache clearer: %s', $id)); $clearer->clear($kernel->getContainer()->getParameter('kernel.cache_dir')); } $failure = false; foreach ($pools as $id => $pool) { - $io->comment(sprintf('Clearing cache pool: %s', $id)); + $io->comment(\sprintf('Clearing cache pool: %s', $id)); if ($pool instanceof CacheItemPoolInterface) { if (!$pool->clear()) { - $io->warning(sprintf('Cache pool "%s" could not be cleared.', $pool)); + $io->warning(\sprintf('Cache pool "%s" could not be cleared.', $pool)); $failure = true; } } else { if (false === $this->poolClearer->clearPool($id)) { - $io->warning(sprintf('Cache pool "%s" could not be cleared.', $pool)); + $io->warning(\sprintf('Cache pool "%s" could not be cleared.', $pool)); $failure = true; } } diff --git a/Command/CachePoolDeleteCommand.php b/Command/CachePoolDeleteCommand.php index e634e00f0..8fb1d1aaa 100644 --- a/Command/CachePoolDeleteCommand.php +++ b/Command/CachePoolDeleteCommand.php @@ -63,16 +63,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int $cachePool = $this->poolClearer->getPool($pool); if (!$cachePool->hasItem($key)) { - $io->note(sprintf('Cache item "%s" does not exist in cache pool "%s".', $key, $pool)); + $io->note(\sprintf('Cache item "%s" does not exist in cache pool "%s".', $key, $pool)); return 0; } if (!$cachePool->deleteItem($key)) { - throw new \Exception(sprintf('Cache item "%s" could not be deleted.', $key)); + throw new \Exception(\sprintf('Cache item "%s" could not be deleted.', $key)); } - $io->success(sprintf('Cache item "%s" was successfully deleted.', $key)); + $io->success(\sprintf('Cache item "%s" was successfully deleted.', $key)); return 0; } diff --git a/Command/CachePoolInvalidateTagsCommand.php b/Command/CachePoolInvalidateTagsCommand.php index f879a6d0d..f92f0d634 100644 --- a/Command/CachePoolInvalidateTagsCommand.php +++ b/Command/CachePoolInvalidateTagsCommand.php @@ -64,26 +64,26 @@ protected function execute(InputInterface $input, OutputInterface $output): int $errors = false; foreach ($pools as $name) { - $io->comment(sprintf('Invalidating tag(s): %s from pool %s.', $tagList, $name)); + $io->comment(\sprintf('Invalidating tag(s): %s from pool %s.', $tagList, $name)); try { $pool = $this->pools->get($name); } catch (ServiceNotFoundException) { - $io->error(sprintf('Pool "%s" not found.', $name)); + $io->error(\sprintf('Pool "%s" not found.', $name)); $errors = true; continue; } if (!$pool instanceof TagAwareCacheInterface) { - $io->error(sprintf('Pool "%s" is not taggable.', $name)); + $io->error(\sprintf('Pool "%s" is not taggable.', $name)); $errors = true; continue; } if (!$pool->invalidateTags($tags)) { - $io->error(sprintf('Cache tag(s) "%s" could not be invalidated for pool "%s".', $tagList, $name)); + $io->error(\sprintf('Cache tag(s) "%s" could not be invalidated for pool "%s".', $tagList, $name)); $errors = true; } } diff --git a/Command/CachePoolPruneCommand.php b/Command/CachePoolPruneCommand.php index fba1033f9..745a001cc 100644 --- a/Command/CachePoolPruneCommand.php +++ b/Command/CachePoolPruneCommand.php @@ -52,7 +52,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io = new SymfonyStyle($input, $output); foreach ($this->pools as $name => $pool) { - $io->comment(sprintf('Pruning cache pool: %s', $name)); + $io->comment(\sprintf('Pruning cache pool: %s', $name)); $pool->prune(); } diff --git a/Command/CacheWarmupCommand.php b/Command/CacheWarmupCommand.php index a4b32a561..b096b0801 100644 --- a/Command/CacheWarmupCommand.php +++ b/Command/CacheWarmupCommand.php @@ -58,7 +58,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io = new SymfonyStyle($input, $output); $kernel = $this->getApplication()->getKernel(); - $io->comment(sprintf('Warming up the cache for the %s environment with debug %s', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + $io->comment(\sprintf('Warming up the cache for the %s environment with debug %s', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); if (!$input->getOption('no-optional-warmers')) { $this->cacheWarmer->enableOptionalWarmers(); @@ -76,7 +76,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int Preloader::append($preloadFile, $preload); } - $io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully warmed.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + $io->success(\sprintf('Cache for the "%s" environment (debug=%s) was successfully warmed.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); return 0; } diff --git a/Command/ConfigDebugCommand.php b/Command/ConfigDebugCommand.php index cc116fc68..fb79b39bf 100644 --- a/Command/ConfigDebugCommand.php +++ b/Command/ConfigDebugCommand.php @@ -41,7 +41,7 @@ class ConfigDebugCommand extends AbstractConfigCommand { protected function configure(): void { - $commentedHelpFormats = array_map(fn ($format) => sprintf('%s', $format), $this->getAvailableFormatOptions()); + $commentedHelpFormats = array_map(fn ($format) => \sprintf('%s', $format), $this->getAvailableFormatOptions()); $helpFormats = implode('", "', $commentedHelpFormats); $this @@ -49,7 +49,7 @@ protected function configure(): void new InputArgument('name', InputArgument::OPTIONAL, 'The bundle name or the extension alias'), new InputArgument('path', InputArgument::OPTIONAL, 'The configuration option path'), new InputOption('resolve-env', null, InputOption::VALUE_NONE, 'Display resolved environment variable values instead of placeholders'), - new InputOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), class_exists(Yaml::class) ? 'txt' : 'json'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, \sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), class_exists(Yaml::class) ? 'txt' : 'json'), ]) ->setHelp(<<%command.name% command dumps the current configuration for an @@ -106,7 +106,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (null === $path = $input->getArgument('path')) { if ('txt' === $input->getOption('format')) { $io->title( - sprintf('Current configuration for %s', $name === $extensionAlias ? sprintf('extension with alias "%s"', $extensionAlias) : sprintf('"%s"', $name)) + \sprintf('Current configuration for %s', $name === $extensionAlias ? \sprintf('extension with alias "%s"', $extensionAlias) : \sprintf('"%s"', $name)) ); } @@ -123,7 +123,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 1; } - $io->title(sprintf('Current configuration for "%s.%s"', $extensionAlias, $path)); + $io->title(\sprintf('Current configuration for "%s.%s"', $extensionAlias, $path)); $io->writeln($this->convertToFormat($config, $format)); @@ -135,7 +135,7 @@ private function convertToFormat(mixed $config, string $format): string return match ($format) { 'txt', 'yaml' => Yaml::dump($config, 10), 'json' => json_encode($config, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE), - default => throw new InvalidArgumentException(sprintf('Supported formats are "%s".', implode('", "', $this->getAvailableFormatOptions()))), + default => throw new InvalidArgumentException(\sprintf('Supported formats are "%s".', implode('", "', $this->getAvailableFormatOptions()))), }; } @@ -162,7 +162,7 @@ private function getConfigForPath(array $config, string $path, string $alias): m foreach ($steps as $step) { if (!\array_key_exists($step, $config)) { - throw new LogicException(sprintf('Unable to find configuration for "%s.%s".', $alias, $path)); + throw new LogicException(\sprintf('Unable to find configuration for "%s.%s".', $alias, $path)); } $config = $config[$step]; @@ -190,7 +190,7 @@ private function getConfigForExtension(ExtensionInterface $extension, ContainerB // Fall back to default config if the extension has one if (!$extension instanceof ConfigurationExtensionInterface && !$extension instanceof ConfigurationInterface) { - throw new \LogicException(sprintf('The extension with alias "%s" does not have configuration.', $extensionAlias)); + throw new \LogicException(\sprintf('The extension with alias "%s" does not have configuration.', $extensionAlias)); } $configs = $container->getExtensionConfig($extensionAlias); diff --git a/Command/ConfigDumpReferenceCommand.php b/Command/ConfigDumpReferenceCommand.php index 3231e5a47..27dc01b11 100644 --- a/Command/ConfigDumpReferenceCommand.php +++ b/Command/ConfigDumpReferenceCommand.php @@ -39,14 +39,14 @@ class ConfigDumpReferenceCommand extends AbstractConfigCommand { protected function configure(): void { - $commentedHelpFormats = array_map(fn ($format) => sprintf('%s', $format), $this->getAvailableFormatOptions()); + $commentedHelpFormats = array_map(fn ($format) => \sprintf('%s', $format), $this->getAvailableFormatOptions()); $helpFormats = implode('", "', $commentedHelpFormats); $this ->setDefinition([ new InputArgument('name', InputArgument::OPTIONAL, 'The Bundle name or the extension alias'), new InputArgument('path', InputArgument::OPTIONAL, 'The configuration option path'), - new InputOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'yaml'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, \sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'yaml'), ]) ->setHelp(<<%command.name% command dumps the default configuration for an @@ -118,27 +118,27 @@ protected function execute(InputInterface $input, OutputInterface $output): int } if ($name === $extension->getAlias()) { - $message = sprintf('Default configuration for extension with alias: "%s"', $name); + $message = \sprintf('Default configuration for extension with alias: "%s"', $name); } else { - $message = sprintf('Default configuration for "%s"', $name); + $message = \sprintf('Default configuration for "%s"', $name); } if (null !== $path) { - $message .= sprintf(' at path "%s"', $path); + $message .= \sprintf(' at path "%s"', $path); } switch ($format) { case 'yaml': - $io->writeln(sprintf('# %s', $message)); + $io->writeln(\sprintf('# %s', $message)); $dumper = new YamlReferenceDumper(); break; case 'xml': - $io->writeln(sprintf('', $message)); + $io->writeln(\sprintf('', $message)); $dumper = new XmlReferenceDumper(); break; default: $io->writeln($message); - throw new InvalidArgumentException(sprintf('Supported formats are "%s".', implode('", "', $this->getAvailableFormatOptions()))); + throw new InvalidArgumentException(\sprintf('Supported formats are "%s".', implode('", "', $this->getAvailableFormatOptions()))); } $io->writeln(null === $path ? $dumper->dump($configuration, $extension->getNamespace()) : $dumper->dumpAtPath($configuration, $path)); diff --git a/Command/ContainerDebugCommand.php b/Command/ContainerDebugCommand.php index df6aef5dd..9619d1972 100644 --- a/Command/ContainerDebugCommand.php +++ b/Command/ContainerDebugCommand.php @@ -52,7 +52,7 @@ protected function configure(): void new InputOption('types', null, InputOption::VALUE_NONE, 'Display types (classes/interfaces) available in the container'), new InputOption('env-var', null, InputOption::VALUE_REQUIRED, 'Display a specific environment variable used in the container'), new InputOption('env-vars', null, InputOption::VALUE_NONE, 'Display environment variables used in the container'), - new InputOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'txt'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, \sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'txt'), new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'), new InputOption('deprecations', null, InputOption::VALUE_NONE, 'Display deprecations generated when compiling and warming up the container'), ]) @@ -171,19 +171,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($object->hasDefinition($options['id'])) { $definition = $object->getDefinition($options['id']); if ($definition->isDeprecated()) { - $errorIo->warning($definition->getDeprecation($options['id'])['message'] ?? sprintf('The "%s" service is deprecated.', $options['id'])); + $errorIo->warning($definition->getDeprecation($options['id'])['message'] ?? \sprintf('The "%s" service is deprecated.', $options['id'])); } } if ($object->hasAlias($options['id'])) { $alias = $object->getAlias($options['id']); if ($alias->isDeprecated()) { - $errorIo->warning($alias->getDeprecation($options['id'])['message'] ?? sprintf('The "%s" alias is deprecated.', $options['id'])); + $errorIo->warning($alias->getDeprecation($options['id'])['message'] ?? \sprintf('The "%s" alias is deprecated.', $options['id'])); } } } if (isset($options['id']) && isset($kernel->getContainer()->getRemovedIds()[$options['id']])) { - $errorIo->note(sprintf('The "%s" service or alias has been removed or inlined when the container was compiled.', $options['id'])); + $errorIo->note(\sprintf('The "%s" service or alias has been removed or inlined when the container was compiled.', $options['id'])); } } catch (ServiceNotFoundException $e) { if ('' !== $e->getId() && '@' === $e->getId()[0]) { @@ -277,7 +277,7 @@ private function findProperServiceName(InputInterface $input, SymfonyStyle $io, $matchingServices = $this->findServiceIdsContaining($container, $name, $showHidden); if (!$matchingServices) { - throw new InvalidArgumentException(sprintf('No services found that match "%s".', $name)); + throw new InvalidArgumentException(\sprintf('No services found that match "%s".', $name)); } if (1 === \count($matchingServices)) { @@ -295,7 +295,7 @@ private function findProperTagName(InputInterface $input, SymfonyStyle $io, Cont $matchingTags = $this->findTagsContaining($container, $tagName); if (!$matchingTags) { - throw new InvalidArgumentException(sprintf('No tags found that match "%s".', $tagName)); + throw new InvalidArgumentException(\sprintf('No tags found that match "%s".', $tagName)); } if (1 === \count($matchingTags)) { diff --git a/Command/ContainerLintCommand.php b/Command/ContainerLintCommand.php index cd6e0657c..0e6c72d16 100644 --- a/Command/ContainerLintCommand.php +++ b/Command/ContainerLintCommand.php @@ -81,7 +81,7 @@ private function getContainerBuilder(): ContainerBuilder if (!$kernel->isDebug() || !$kernelContainer->getParameter('debug.container.dump') || !(new ConfigCache($kernelContainer->getParameter('debug.container.dump'), true))->isFresh()) { if (!$kernel instanceof Kernel) { - throw new RuntimeException(sprintf('This command does not support the application kernel: "%s" does not extend "%s".', get_debug_type($kernel), Kernel::class)); + throw new RuntimeException(\sprintf('This command does not support the application kernel: "%s" does not extend "%s".', get_debug_type($kernel), Kernel::class)); } $buildContainer = \Closure::bind(function (): ContainerBuilder { @@ -92,7 +92,7 @@ private function getContainerBuilder(): ContainerBuilder $container = $buildContainer(); } else { if (!$kernelContainer instanceof Container) { - throw new RuntimeException(sprintf('This command does not support the application container: "%s" does not extend "%s".', get_debug_type($kernelContainer), Container::class)); + throw new RuntimeException(\sprintf('This command does not support the application container: "%s" does not extend "%s".', get_debug_type($kernelContainer), Container::class)); } (new XmlFileLoader($container = new ContainerBuilder($parameterBag = new EnvPlaceholderParameterBag()), new FileLocator()))->load($kernelContainer->getParameter('debug.container.dump')); diff --git a/Command/DebugAutowiringCommand.php b/Command/DebugAutowiringCommand.php index 77011b185..e159c5a39 100644 --- a/Command/DebugAutowiringCommand.php +++ b/Command/DebugAutowiringCommand.php @@ -77,7 +77,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $serviceIds = array_filter($serviceIds, fn ($serviceId) => false !== stripos(str_replace('\\', '', $serviceId), $searchNormalized) && !str_starts_with($serviceId, '.')); if (!$serviceIds) { - $errorIo->error(sprintf('No autowirable classes or interfaces found matching "%s"', $search)); + $errorIo->error(\sprintf('No autowirable classes or interfaces found matching "%s"', $search)); return 1; } @@ -96,7 +96,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->title('Autowirable Types'); $io->text('The following classes & interfaces can be used as type-hints when autowiring:'); if ($search) { - $io->text(sprintf('(only showing classes/interfaces matching %s)', $search)); + $io->text(\sprintf('(only showing classes/interfaces matching %s)', $search)); } $hasAlias = []; $all = $input->getOption('all'); @@ -119,10 +119,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int } } - $serviceLine = sprintf('%s', $serviceId); + $serviceLine = \sprintf('%s', $serviceId); if ('' !== $fileLink = $this->getFileLink($previousId)) { $serviceLine = substr($serviceId, \strlen($previousId)); - $serviceLine = sprintf('%s', $fileLink, $previousId).('' !== $serviceLine ? sprintf('%s', $serviceLine) : ''); + $serviceLine = \sprintf('%s', $fileLink, $previousId).('' !== $serviceLine ? \sprintf('%s', $serviceLine) : ''); } if ($container->hasAlias($serviceId)) { @@ -167,7 +167,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->newLine(); if (0 < $serviceIdsNb) { - $io->text(sprintf('%s more concrete service%s would be displayed when adding the "--all" option.', $serviceIdsNb, $serviceIdsNb > 1 ? 's' : '')); + $io->text(\sprintf('%s more concrete service%s would be displayed when adding the "--all" option.', $serviceIdsNb, $serviceIdsNb > 1 ? 's' : '')); } if ($all) { $io->text('Pro-tip: use interfaces in your type-hints instead of classes to benefit from the dependency inversion principle.'); diff --git a/Command/EventDispatcherDebugCommand.php b/Command/EventDispatcherDebugCommand.php index 52816e7de..a3531d800 100644 --- a/Command/EventDispatcherDebugCommand.php +++ b/Command/EventDispatcherDebugCommand.php @@ -49,7 +49,7 @@ protected function configure(): void ->setDefinition([ new InputArgument('event', InputArgument::OPTIONAL, 'An event name or a part of the event name'), new InputOption('dispatcher', null, InputOption::VALUE_REQUIRED, 'To view events of a specific event dispatcher', self::DEFAULT_DISPATCHER), - new InputOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'txt'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, \sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'txt'), new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'), ]) ->setHelp(<<<'EOF' @@ -75,7 +75,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $options = []; $dispatcherServiceName = $input->getOption('dispatcher'); if (!$this->dispatchers->has($dispatcherServiceName)) { - $io->getErrorStyle()->error(sprintf('Event dispatcher "%s" is not available.', $dispatcherServiceName)); + $io->getErrorStyle()->error(\sprintf('Event dispatcher "%s" is not available.', $dispatcherServiceName)); return 1; } @@ -89,7 +89,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int // if there is no direct match, try find partial matches $events = $this->searchForEvent($dispatcher, $event); if (0 === \count($events)) { - $io->getErrorStyle()->warning(sprintf('The event "%s" does not have any registered listeners.', $event)); + $io->getErrorStyle()->warning(\sprintf('The event "%s" does not have any registered listeners.', $event)); return 0; } elseif (1 === \count($events)) { diff --git a/Command/RouterDebugCommand.php b/Command/RouterDebugCommand.php index 54df49431..d57c0623f 100644 --- a/Command/RouterDebugCommand.php +++ b/Command/RouterDebugCommand.php @@ -53,7 +53,7 @@ protected function configure(): void new InputArgument('name', InputArgument::OPTIONAL, 'A route name'), new InputOption('show-controllers', null, InputOption::VALUE_NONE, 'Show assigned controllers in overview'), new InputOption('show-aliases', null, InputOption::VALUE_NONE, 'Show aliases in overview'), - new InputOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'txt'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, \sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'txt'), new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw route(s)'), ]) ->setHelp(<<<'EOF' @@ -103,7 +103,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } if (!$route) { - throw new InvalidArgumentException(sprintf('The route "%s" does not exist.', $name)); + throw new InvalidArgumentException(\sprintf('The route "%s" does not exist.', $name)); } $helper->describe($io, $route, [ diff --git a/Command/RouterMatchCommand.php b/Command/RouterMatchCommand.php index 475b403ca..3f0ea3cb5 100644 --- a/Command/RouterMatchCommand.php +++ b/Command/RouterMatchCommand.php @@ -93,21 +93,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int $matches = false; foreach ($traces as $trace) { if (TraceableUrlMatcher::ROUTE_ALMOST_MATCHES == $trace['level']) { - $io->text(sprintf('Route "%s" almost matches but %s', $trace['name'], lcfirst($trace['log']))); + $io->text(\sprintf('Route "%s" almost matches but %s', $trace['name'], lcfirst($trace['log']))); } elseif (TraceableUrlMatcher::ROUTE_MATCHES == $trace['level']) { - $io->success(sprintf('Route "%s" matches', $trace['name'])); + $io->success(\sprintf('Route "%s" matches', $trace['name'])); $routerDebugCommand = $this->getApplication()->find('debug:router'); $routerDebugCommand->run(new ArrayInput(['name' => $trace['name']]), $output); $matches = true; } elseif ($input->getOption('verbose')) { - $io->text(sprintf('Route "%s" does not match: %s', $trace['name'], $trace['log'])); + $io->text(\sprintf('Route "%s" does not match: %s', $trace['name'], $trace['log'])); } } if (!$matches) { - $io->error(sprintf('None of the routes match the path "%s"', $input->getArgument('path_info'))); + $io->error(\sprintf('None of the routes match the path "%s"', $input->getArgument('path_info'))); return 1; } diff --git a/Command/SecretsDecryptToLocalCommand.php b/Command/SecretsDecryptToLocalCommand.php index f76e1d05a..b078769d8 100644 --- a/Command/SecretsDecryptToLocalCommand.php +++ b/Command/SecretsDecryptToLocalCommand.php @@ -64,7 +64,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $secrets = $this->vault->list(true); - $io->comment(sprintf('%d secret%s found in the vault.', \count($secrets), 1 !== \count($secrets) ? 's' : '')); + $io->comment(\sprintf('%d secret%s found in the vault.', \count($secrets), 1 !== \count($secrets) ? 's' : '')); $skipped = 0; if (!$input->getOption('force')) { @@ -78,14 +78,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($skipped > 0) { $io->warning([ - sprintf('%d secret%s already overridden in the local vault and will be skipped.', $skipped, 1 !== $skipped ? 's are' : ' is'), + \sprintf('%d secret%s already overridden in the local vault and will be skipped.', $skipped, 1 !== $skipped ? 's are' : ' is'), 'Use the --force flag to override these.', ]); } foreach ($secrets as $k => $v) { if (null === $v) { - $io->error($this->vault->getLastMessage() ?? sprintf('Secret "%s" has been skipped as there was an error reading it.', $k)); + $io->error($this->vault->getLastMessage() ?? \sprintf('Secret "%s" has been skipped as there was an error reading it.', $k)); continue; } diff --git a/Command/SecretsListCommand.php b/Command/SecretsListCommand.php index cdfc51c39..920b3b1fc 100644 --- a/Command/SecretsListCommand.php +++ b/Command/SecretsListCommand.php @@ -62,7 +62,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->comment('Use "%env()%" to reference a secret in a config file.'); if (!$reveal = $input->getOption('reveal')) { - $io->comment(sprintf('To reveal the secrets run php %s %s --reveal', $_SERVER['PHP_SELF'], $this->getName())); + $io->comment(\sprintf('To reveal the secrets run php %s %s --reveal', $_SERVER['PHP_SELF'], $this->getName())); } $secrets = $this->vault->list($reveal); diff --git a/Command/SecretsRevealCommand.php b/Command/SecretsRevealCommand.php index bcbdea11f..150186b1d 100644 --- a/Command/SecretsRevealCommand.php +++ b/Command/SecretsRevealCommand.php @@ -59,7 +59,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->writeln($localSecrets[$name]); } else { if (!\array_key_exists($name, $secrets)) { - $io->error(sprintf('The secret "%s" does not exist.', $name)); + $io->error(\sprintf('The secret "%s" does not exist.', $name)); return self::INVALID; } diff --git a/Command/SecretsSetCommand.php b/Command/SecretsSetCommand.php index 49a20af76..f7e8eeaa6 100644 --- a/Command/SecretsSetCommand.php +++ b/Command/SecretsSetCommand.php @@ -84,7 +84,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } if ($this->localVault === $vault && !\array_key_exists($name, $this->vault->list())) { - $io->error(sprintf('Secret "%s" does not exist in the vault, you cannot override it locally.', $name)); + $io->error(\sprintf('Secret "%s" does not exist in the vault, you cannot override it locally.', $name)); return 1; } @@ -103,9 +103,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int } elseif (is_file($file) && is_readable($file)) { $value = file_get_contents($file); } elseif (!is_file($file)) { - throw new \InvalidArgumentException(sprintf('File not found: "%s".', $file)); + throw new \InvalidArgumentException(\sprintf('File not found: "%s".', $file)); } elseif (!is_readable($file)) { - throw new \InvalidArgumentException(sprintf('File is not readable: "%s".', $file)); + throw new \InvalidArgumentException(\sprintf('File is not readable: "%s".', $file)); } if ($vault->generateKeys()) { diff --git a/Command/TranslationDebugCommand.php b/Command/TranslationDebugCommand.php index 2438d3feb..9cdfdae04 100644 --- a/Command/TranslationDebugCommand.php +++ b/Command/TranslationDebugCommand.php @@ -145,7 +145,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $codePaths = [$path.'/templates']; if (!is_dir($transPaths[0])) { - throw new InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0])); + throw new InvalidArgumentException(\sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0])); } } } elseif ($input->getOption('all')) { @@ -171,10 +171,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int // No defined or extracted messages if (!$allMessages || null !== $domain && empty($allMessages[$domain])) { - $outputMessage = sprintf('No defined or extracted messages for locale "%s"', $locale); + $outputMessage = \sprintf('No defined or extracted messages for locale "%s"', $locale); if (null !== $domain) { - $outputMessage .= sprintf(' and domain "%s"', $domain); + $outputMessage .= \sprintf(' and domain "%s"', $domain); } $io->getErrorStyle()->warning($outputMessage); @@ -186,9 +186,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int $fallbackCatalogues = $this->loadFallbackCatalogues($locale, $transPaths); // Display header line - $headers = ['State', 'Domain', 'Id', sprintf('Message Preview (%s)', $locale)]; + $headers = ['State', 'Domain', 'Id', \sprintf('Message Preview (%s)', $locale)]; foreach ($fallbackCatalogues as $fallbackCatalogue) { - $headers[] = sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale()); + $headers[] = \sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale()); } $rows = []; // Iterate all message ids and determine their state @@ -310,7 +310,7 @@ private function formatStates(array $states): string private function formatId(string $id): string { - return sprintf('%s', $id); + return \sprintf('%s', $id); } private function sanitizeString(string $string, int $length = 40): string diff --git a/Command/TranslationUpdateCommand.php b/Command/TranslationUpdateCommand.php index 1a883f81e..2f61c81d6 100644 --- a/Command/TranslationUpdateCommand.php +++ b/Command/TranslationUpdateCommand.php @@ -172,13 +172,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int $codePaths = [$path.'/templates']; if (!is_dir($transPaths[0])) { - throw new InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0])); + throw new InvalidArgumentException(\sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0])); } } } $io->title('Translation Messages Extractor and Dumper'); - $io->comment(sprintf('Generating "%s" translation files for "%s"', $input->getArgument('locale'), $currentName)); + $io->comment(\sprintf('Generating "%s" translation files for "%s"', $input->getArgument('locale'), $currentName)); $io->comment('Parsing templates...'); $extractedCatalogue = $this->extractMessages($input->getArgument('locale'), $codePaths, $input->getOption('prefix')); @@ -217,8 +217,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $list = array_merge( array_diff($allKeys, $newKeys), - array_map(fn ($id) => sprintf('%s', $id), $newKeys), - array_map(fn ($id) => sprintf('%s', $id), array_keys($operation->getObsoleteMessages($domain))) + array_map(fn ($id) => \sprintf('%s', $id), $newKeys), + array_map(fn ($id) => \sprintf('%s', $id), array_keys($operation->getObsoleteMessages($domain))) ); $domainMessagesCount = \count($list); @@ -238,17 +238,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int } } - $io->section(sprintf('Messages extracted for domain "%s" (%d message%s)', $domain, $domainMessagesCount, $domainMessagesCount > 1 ? 's' : '')); + $io->section(\sprintf('Messages extracted for domain "%s" (%d message%s)', $domain, $domainMessagesCount, $domainMessagesCount > 1 ? 's' : '')); $io->listing($list); $extractedMessagesCount += $domainMessagesCount; } if ('xlf' === $format) { - $io->comment(sprintf('Xliff output version is %s', $xliffVersion)); + $io->comment(\sprintf('Xliff output version is %s', $xliffVersion)); } - $resultMessage = sprintf('%d message%s successfully extracted', $extractedMessagesCount, $extractedMessagesCount > 1 ? 's were' : ' was'); + $resultMessage = \sprintf('%d message%s successfully extracted', $extractedMessagesCount, $extractedMessagesCount > 1 ? 's were' : ' was'); } // save the files diff --git a/Command/WorkflowDumpCommand.php b/Command/WorkflowDumpCommand.php index d732305d4..201fb8be8 100644 --- a/Command/WorkflowDumpCommand.php +++ b/Command/WorkflowDumpCommand.php @@ -75,7 +75,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $workflowName = $input->getArgument('name'); if (!$this->workflows->has($workflowName)) { - throw new InvalidArgumentException(sprintf('The workflow named "%s" cannot be found.', $workflowName)); + throw new InvalidArgumentException(\sprintf('The workflow named "%s" cannot be found.', $workflowName)); } $workflow = $this->workflows->get($workflowName); $type = $workflow instanceof StateMachine ? 'state_machine' : 'workflow'; diff --git a/Console/Application.php b/Console/Application.php index 1c41849e7..274e7b06d 100644 --- a/Console/Application.php +++ b/Console/Application.php @@ -156,7 +156,7 @@ public function all(?string $namespace = null): array public function getLongVersion(): string { - return parent::getLongVersion().sprintf(' (env: %s, debug: %s)', $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false'); + return parent::getLongVersion().\sprintf(' (env: %s, debug: %s)', $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false'); } public function add(Command $command): ?Command diff --git a/Console/Descriptor/Descriptor.php b/Console/Descriptor/Descriptor.php index 8541f71bb..af5c3b10a 100644 --- a/Console/Descriptor/Descriptor.php +++ b/Console/Descriptor/Descriptor.php @@ -62,7 +62,7 @@ public function describe(OutputInterface $output, mixed $object, array $options $object instanceof Alias => $this->describeContainerAlias($object, $options), $object instanceof EventDispatcherInterface => $this->describeEventDispatcherListeners($object, $options), \is_callable($object) => $this->describeCallable($object, $options), - default => throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_debug_type($object))), + default => throw new \InvalidArgumentException(\sprintf('Object of type "%s" is not describable.', get_debug_type($object))), }; if ($object instanceof ContainerBuilder) { @@ -133,7 +133,7 @@ protected function formatValue(mixed $value): string } if (\is_object($value)) { - return sprintf('object(%s)', $value::class); + return \sprintf('object(%s)', $value::class); } if (\is_string($value)) { diff --git a/Console/Descriptor/JsonDescriptor.php b/Console/Descriptor/JsonDescriptor.php index 88cf4162c..5b83f0746 100644 --- a/Console/Descriptor/JsonDescriptor.php +++ b/Console/Descriptor/JsonDescriptor.php @@ -156,7 +156,7 @@ protected function describeContainerParameter(mixed $parameter, ?array $deprecat $data = [$key => $parameter]; if ($deprecation) { - $data['_deprecation'] = sprintf('Since %s %s: %s', $deprecation[0], $deprecation[1], sprintf(...\array_slice($deprecation, 2))); + $data['_deprecation'] = \sprintf('Since %s %s: %s', $deprecation[0], $deprecation[1], \sprintf(...\array_slice($deprecation, 2))); } $this->writeData($data, $options); @@ -169,7 +169,7 @@ protected function describeContainerEnvVars(array $envs, array $options = []): v protected function describeContainerDeprecations(ContainerBuilder $container, array $options = []): void { - $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $container->getParameter('kernel.build_dir'), $container->getParameter('kernel.container_class')); + $containerDeprecationFilePath = \sprintf('%s/%sDeprecations.log', $container->getParameter('kernel.build_dir'), $container->getParameter('kernel.container_class')); if (!file_exists($containerDeprecationFilePath)) { throw new RuntimeException('The deprecation file does not exist, please try warming the cache first.'); } @@ -236,7 +236,7 @@ protected function sortParameters(ParameterBag $parameters): array $deprecations = []; foreach ($deprecated as $parameter => $deprecation) { - $deprecations[$parameter] = sprintf('Since %s %s: %s', $deprecation[0], $deprecation[1], sprintf(...\array_slice($deprecation, 2))); + $deprecations[$parameter] = \sprintf('Since %s %s: %s', $deprecation[0], $deprecation[1], \sprintf(...\array_slice($deprecation, 2))); } $sortedParameters['_deprecations'] = $deprecations; @@ -280,7 +280,7 @@ private function getContainerDefinitionData(Definition $definition, bool $omitTa if ($factory[0] instanceof Reference) { $data['factory_service'] = (string) $factory[0]; } elseif ($factory[0] instanceof Definition) { - $data['factory_service'] = sprintf('inline factory service (%s)', $factory[0]->getClass() ?? 'class not configured'); + $data['factory_service'] = \sprintf('inline factory service (%s)', $factory[0]->getClass() ?? 'class not configured'); } else { $data['factory_class'] = $factory[0]; } diff --git a/Console/Descriptor/MarkdownDescriptor.php b/Console/Descriptor/MarkdownDescriptor.php index 7965990bd..5203d14c3 100644 --- a/Console/Descriptor/MarkdownDescriptor.php +++ b/Console/Descriptor/MarkdownDescriptor.php @@ -40,7 +40,7 @@ protected function describeRouteCollection(RouteCollection $routes, array $optio } $this->describeRoute($route, ['name' => $name]); if (($showAliases ??= $options['show_aliases'] ?? false) && $aliases = ($reverseAliases ??= $this->getReverseAliases($routes))[$name] ?? []) { - $this->write(sprintf("- Aliases: \n%s", implode("\n", array_map(static fn (string $alias): string => sprintf(' - %s', $alias), $aliases)))); + $this->write(\sprintf("- Aliases: \n%s", implode("\n", array_map(static fn (string $alias): string => \sprintf(' - %s', $alias), $aliases)))); } } $this->write("\n"); @@ -75,11 +75,11 @@ protected function describeContainerParameters(ParameterBag $parameters, array $ $this->write("Container parameters\n====================\n"); foreach ($this->sortParameters($parameters) as $key => $value) { - $this->write(sprintf( + $this->write(\sprintf( "\n- `%s`: `%s`%s", $key, $this->formatParameter($value), - isset($deprecatedParameters[$key]) ? sprintf(' *Since %s %s: %s*', $deprecatedParameters[$key][0], $deprecatedParameters[$key][1], sprintf(...\array_slice($deprecatedParameters[$key], 2))) : '' + isset($deprecatedParameters[$key]) ? \sprintf(' *Since %s %s: %s*', $deprecatedParameters[$key][0], $deprecatedParameters[$key][1], \sprintf(...\array_slice($deprecatedParameters[$key], 2))) : '' )); } } @@ -111,13 +111,13 @@ protected function describeContainerService(object $service, array $options = [] } elseif ($service instanceof Definition) { $this->describeContainerDefinition($service, $childOptions, $container); } else { - $this->write(sprintf('**`%s`:** `%s`', $options['id'], $service::class)); + $this->write(\sprintf('**`%s`:** `%s`', $options['id'], $service::class)); } } protected function describeContainerDeprecations(ContainerBuilder $container, array $options = []): void { - $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $container->getParameter('kernel.build_dir'), $container->getParameter('kernel.container_class')); + $containerDeprecationFilePath = \sprintf('%s/%sDeprecations.log', $container->getParameter('kernel.build_dir'), $container->getParameter('kernel.container_class')); if (!file_exists($containerDeprecationFilePath)) { throw new RuntimeException('The deprecation file does not exist, please try warming the cache first.'); } @@ -132,11 +132,11 @@ protected function describeContainerDeprecations(ContainerBuilder $container, ar $formattedLogs = []; $remainingCount = 0; foreach ($logs as $log) { - $formattedLogs[] = sprintf("- %sx: \"%s\" in %s:%s\n", $log['count'], $log['message'], $log['file'], $log['line']); + $formattedLogs[] = \sprintf("- %sx: \"%s\" in %s:%s\n", $log['count'], $log['message'], $log['file'], $log['line']); $remainingCount += $log['count']; } - $this->write(sprintf("## Remaining deprecations (%s)\n\n", $remainingCount)); + $this->write(\sprintf("## Remaining deprecations (%s)\n\n", $remainingCount)); foreach ($formattedLogs as $formattedLog) { $this->write($formattedLog); } @@ -201,7 +201,7 @@ protected function describeContainerServices(ContainerBuilder $container, array $this->write("\n\nServices\n--------\n"); foreach ($services['services'] as $id => $service) { $this->write("\n"); - $this->write(sprintf('- `%s`: `%s`', $id, $service::class)); + $this->write(\sprintf('- `%s`: `%s`', $id, $service::class)); } } } @@ -244,7 +244,7 @@ protected function describeContainerDefinition(Definition $definition, array $op if ($factory[0] instanceof Reference) { $output .= "\n".'- Factory Service: `'.$factory[0].'`'; } elseif ($factory[0] instanceof Definition) { - $output .= "\n".sprintf('- Factory Service: inline factory service (%s)', $factory[0]->getClass() ? sprintf('`%s`', $factory[0]->getClass()) : 'not configured'); + $output .= "\n".\sprintf('- Factory Service: inline factory service (%s)', $factory[0]->getClass() ? \sprintf('`%s`', $factory[0]->getClass()) : 'not configured'); } else { $output .= "\n".'- Factory Class: `'.$factory[0].'`'; } @@ -273,7 +273,7 @@ protected function describeContainerDefinition(Definition $definition, array $op $inEdges = null !== $container && isset($options['id']) ? $this->getServiceEdges($container, $options['id']) : []; $output .= "\n".'- Usages: '.($inEdges ? implode(', ', $inEdges) : 'none'); - $this->write(isset($options['id']) ? sprintf("### %s\n\n%s\n", $options['id'], $output) : $output); + $this->write(isset($options['id']) ? \sprintf("### %s\n\n%s\n", $options['id'], $output) : $output); } protected function describeContainerAlias(Alias $alias, array $options = [], ?ContainerBuilder $container = null): void @@ -287,7 +287,7 @@ protected function describeContainerAlias(Alias $alias, array $options = [], ?Co return; } - $this->write(sprintf("### %s\n\n%s\n", $options['id'], $output)); + $this->write(\sprintf("### %s\n\n%s\n", $options['id'], $output)); if (!$container) { return; @@ -300,7 +300,7 @@ protected function describeContainerAlias(Alias $alias, array $options = [], ?Co protected function describeContainerParameter(mixed $parameter, ?array $deprecation, array $options = []): void { if (isset($options['parameter'])) { - $this->write(sprintf("%s\n%s\n\n%s%s", $options['parameter'], str_repeat('=', \strlen($options['parameter'])), $this->formatParameter($parameter), $deprecation ? sprintf("\n\n*Since %s %s: %s*", $deprecation[0], $deprecation[1], sprintf(...\array_slice($deprecation, 2))) : '')); + $this->write(\sprintf("%s\n%s\n\n%s%s", $options['parameter'], str_repeat('=', \strlen($options['parameter'])), $this->formatParameter($parameter), $deprecation ? \sprintf("\n\n*Since %s %s: %s*", $deprecation[0], $deprecation[1], \sprintf(...\array_slice($deprecation, 2))) : '')); } else { $this->write($parameter); } @@ -319,35 +319,35 @@ protected function describeEventDispatcherListeners(EventDispatcherInterface $ev $title = 'Registered listeners'; if (null !== $dispatcherServiceName) { - $title .= sprintf(' of event dispatcher "%s"', $dispatcherServiceName); + $title .= \sprintf(' of event dispatcher "%s"', $dispatcherServiceName); } if (null !== $event) { - $title .= sprintf(' for event `%s` ordered by descending priority', $event); + $title .= \sprintf(' for event `%s` ordered by descending priority', $event); $registeredListeners = $eventDispatcher->getListeners($event); } else { // Try to see if "events" exists $registeredListeners = \array_key_exists('events', $options) ? array_combine($options['events'], array_map(fn ($event) => $eventDispatcher->getListeners($event), $options['events'])) : $eventDispatcher->getListeners(); } - $this->write(sprintf('# %s', $title)."\n"); + $this->write(\sprintf('# %s', $title)."\n"); if (null !== $event) { foreach ($registeredListeners as $order => $listener) { - $this->write("\n".sprintf('## Listener %d', $order + 1)."\n"); + $this->write("\n".\sprintf('## Listener %d', $order + 1)."\n"); $this->describeCallable($listener); - $this->write(sprintf('- Priority: `%d`', $eventDispatcher->getListenerPriority($event, $listener))."\n"); + $this->write(\sprintf('- Priority: `%d`', $eventDispatcher->getListenerPriority($event, $listener))."\n"); } } else { ksort($registeredListeners); foreach ($registeredListeners as $eventListened => $eventListeners) { - $this->write("\n".sprintf('## %s', $eventListened)."\n"); + $this->write("\n".\sprintf('## %s', $eventListened)."\n"); foreach ($eventListeners as $order => $eventListener) { - $this->write("\n".sprintf('### Listener %d', $order + 1)."\n"); + $this->write("\n".\sprintf('### Listener %d', $order + 1)."\n"); $this->describeCallable($eventListener); - $this->write(sprintf('- Priority: `%d`', $eventDispatcher->getListenerPriority($eventListened, $eventListener))."\n"); + $this->write(\sprintf('- Priority: `%d`', $eventDispatcher->getListenerPriority($eventListened, $eventListener))."\n"); } } } @@ -361,16 +361,16 @@ protected function describeCallable(mixed $callable, array $options = []): void $string .= "\n- Type: `function`"; if (\is_object($callable[0])) { - $string .= "\n".sprintf('- Name: `%s`', $callable[1]); - $string .= "\n".sprintf('- Class: `%s`', $callable[0]::class); + $string .= "\n".\sprintf('- Name: `%s`', $callable[1]); + $string .= "\n".\sprintf('- Class: `%s`', $callable[0]::class); } else { if (!str_starts_with($callable[1], 'parent::')) { - $string .= "\n".sprintf('- Name: `%s`', $callable[1]); - $string .= "\n".sprintf('- Class: `%s`', $callable[0]); + $string .= "\n".\sprintf('- Name: `%s`', $callable[1]); + $string .= "\n".\sprintf('- Class: `%s`', $callable[0]); $string .= "\n- Static: yes"; } else { - $string .= "\n".sprintf('- Name: `%s`', substr($callable[1], 8)); - $string .= "\n".sprintf('- Class: `%s`', $callable[0]); + $string .= "\n".\sprintf('- Name: `%s`', substr($callable[1], 8)); + $string .= "\n".\sprintf('- Class: `%s`', $callable[0]); $string .= "\n- Static: yes"; $string .= "\n- Parent: yes"; } @@ -385,12 +385,12 @@ protected function describeCallable(mixed $callable, array $options = []): void $string .= "\n- Type: `function`"; if (!str_contains($callable, '::')) { - $string .= "\n".sprintf('- Name: `%s`', $callable); + $string .= "\n".\sprintf('- Name: `%s`', $callable); } else { $callableParts = explode('::', $callable); - $string .= "\n".sprintf('- Name: `%s`', $callableParts[1]); - $string .= "\n".sprintf('- Class: `%s`', $callableParts[0]); + $string .= "\n".\sprintf('- Name: `%s`', $callableParts[1]); + $string .= "\n".\sprintf('- Class: `%s`', $callableParts[0]); $string .= "\n- Static: yes"; } @@ -408,10 +408,10 @@ protected function describeCallable(mixed $callable, array $options = []): void return; } - $string .= "\n".sprintf('- Name: `%s`', $r->name); + $string .= "\n".\sprintf('- Name: `%s`', $r->name); if ($class = $r->getClosureCalledClass()) { - $string .= "\n".sprintf('- Class: `%s`', $class->name); + $string .= "\n".\sprintf('- Class: `%s`', $class->name); if (!$r->getClosureThis()) { $string .= "\n- Static: yes"; } @@ -424,7 +424,7 @@ protected function describeCallable(mixed $callable, array $options = []): void if (method_exists($callable, '__invoke')) { $string .= "\n- Type: `object`"; - $string .= "\n".sprintf('- Name: `%s`', $callable::class); + $string .= "\n".\sprintf('- Name: `%s`', $callable::class); $this->write($string."\n"); diff --git a/Console/Descriptor/TextDescriptor.php b/Console/Descriptor/TextDescriptor.php index d728128ce..6ee8b04a1 100644 --- a/Console/Descriptor/TextDescriptor.php +++ b/Console/Descriptor/TextDescriptor.php @@ -131,7 +131,7 @@ protected function describeContainerParameters(ParameterBag $parameters, array $ if (isset($deprecatedParameters[$parameter])) { $tableRows[] = [new TableCell( - sprintf('(Since %s %s: %s)', $deprecatedParameters[$parameter][0], $deprecatedParameters[$parameter][1], sprintf(...\array_slice($deprecatedParameters[$parameter], 2))), + \sprintf('(Since %s %s: %s)', $deprecatedParameters[$parameter][0], $deprecatedParameters[$parameter][1], \sprintf(...\array_slice($deprecatedParameters[$parameter], 2))), ['colspan' => 2] )]; } @@ -152,7 +152,7 @@ protected function describeContainerTags(ContainerBuilder $container, array $opt } foreach ($this->findDefinitionsByTag($container, $showHidden) as $tag => $definitions) { - $options['output']->section(sprintf('"%s" tag', $tag)); + $options['output']->section(\sprintf('"%s" tag', $tag)); $options['output']->listing(array_keys($definitions)); } } @@ -168,7 +168,7 @@ protected function describeContainerService(object $service, array $options = [] } elseif ($service instanceof Definition) { $this->describeContainerDefinition($service, $options, $container); } else { - $options['output']->title(sprintf('Information for Service "%s"', $options['id'])); + $options['output']->title(\sprintf('Information for Service "%s"', $options['id'])); $options['output']->table( ['Service ID', 'Class'], [ @@ -190,7 +190,7 @@ protected function describeContainerServices(ContainerBuilder $container, array } if ($showTag) { - $title .= sprintf(' Tagged with "%s" Tag', $options['tag']); + $title .= \sprintf(' Tagged with "%s" Tag', $options['tag']); } $options['output']->title($title); @@ -247,7 +247,7 @@ protected function describeContainerServices(ContainerBuilder $container, array foreach ($serviceIds as $serviceId) { $definition = $this->resolveServiceDefinition($container, $serviceId); - $styledServiceId = $rawOutput ? $serviceId : sprintf('%s', OutputFormatter::escape($serviceId)); + $styledServiceId = $rawOutput ? $serviceId : \sprintf('%s', OutputFormatter::escape($serviceId)); if ($definition instanceof Definition) { if ($showTag) { foreach ($this->sortByPriority($definition->getTag($showTag)) as $key => $tag) { @@ -270,7 +270,7 @@ protected function describeContainerServices(ContainerBuilder $container, array } } elseif ($definition instanceof Alias) { $alias = $definition; - $tableRows[] = array_merge([$styledServiceId, sprintf('alias for "%s"', $alias)], $tagsCount ? array_fill(0, $tagsCount, '') : []); + $tableRows[] = array_merge([$styledServiceId, \sprintf('alias for "%s"', $alias)], $tagsCount ? array_fill(0, $tagsCount, '') : []); } else { $tableRows[] = array_merge([$styledServiceId, $definition::class], $tagsCount ? array_fill(0, $tagsCount, '') : []); } @@ -282,7 +282,7 @@ protected function describeContainerServices(ContainerBuilder $container, array protected function describeContainerDefinition(Definition $definition, array $options = [], ?ContainerBuilder $container = null): void { if (isset($options['id'])) { - $options['output']->title(sprintf('Information for Service "%s"', $options['id'])); + $options['output']->title(\sprintf('Information for Service "%s"', $options['id'])); } if ('' !== $classDescription = $this->getClassDescription((string) $definition->getClass())) { @@ -299,13 +299,13 @@ protected function describeContainerDefinition(Definition $definition, array $op $tagInformation = []; foreach ($tags as $tagName => $tagData) { foreach ($tagData as $tagParameters) { - $parameters = array_map(fn ($key, $value) => sprintf('%s: %s', $key, \is_array($value) ? $this->formatParameter($value) : $value), array_keys($tagParameters), array_values($tagParameters)); + $parameters = array_map(fn ($key, $value) => \sprintf('%s: %s', $key, \is_array($value) ? $this->formatParameter($value) : $value), array_keys($tagParameters), array_values($tagParameters)); $parameters = implode(', ', $parameters); if ('' === $parameters) { - $tagInformation[] = sprintf('%s', $tagName); + $tagInformation[] = \sprintf('%s', $tagName); } else { - $tagInformation[] = sprintf('%s (%s)', $tagName, $parameters); + $tagInformation[] = \sprintf('%s (%s)', $tagName, $parameters); } } } @@ -341,7 +341,7 @@ protected function describeContainerDefinition(Definition $definition, array $op if ($factory[0] instanceof Reference) { $tableRows[] = ['Factory Service', $factory[0]]; } elseif ($factory[0] instanceof Definition) { - $tableRows[] = ['Factory Service', sprintf('inline factory service (%s)', $factory[0]->getClass() ?? 'class not configured')]; + $tableRows[] = ['Factory Service', \sprintf('inline factory service (%s)', $factory[0]->getClass() ?? 'class not configured')]; } else { $tableRows[] = ['Factory Class', $factory[0]]; } @@ -359,27 +359,27 @@ protected function describeContainerDefinition(Definition $definition, array $op $argument = $argument->getValues()[0]; } if ($argument instanceof Reference) { - $argumentsInformation[] = sprintf('Service(%s)', (string) $argument); + $argumentsInformation[] = \sprintf('Service(%s)', (string) $argument); } elseif ($argument instanceof IteratorArgument) { if ($argument instanceof TaggedIteratorArgument) { - $argumentsInformation[] = sprintf('Tagged Iterator for "%s"%s', $argument->getTag(), $options['is_debug'] ? '' : sprintf(' (%d element(s))', \count($argument->getValues()))); + $argumentsInformation[] = \sprintf('Tagged Iterator for "%s"%s', $argument->getTag(), $options['is_debug'] ? '' : \sprintf(' (%d element(s))', \count($argument->getValues()))); } else { - $argumentsInformation[] = sprintf('Iterator (%d element(s))', \count($argument->getValues())); + $argumentsInformation[] = \sprintf('Iterator (%d element(s))', \count($argument->getValues())); } foreach ($argument->getValues() as $ref) { - $argumentsInformation[] = sprintf('- Service(%s)', $ref); + $argumentsInformation[] = \sprintf('- Service(%s)', $ref); } } elseif ($argument instanceof ServiceLocatorArgument) { - $argumentsInformation[] = sprintf('Service locator (%d element(s))', \count($argument->getValues())); + $argumentsInformation[] = \sprintf('Service locator (%d element(s))', \count($argument->getValues())); } elseif ($argument instanceof Definition) { $argumentsInformation[] = 'Inlined Service'; } elseif ($argument instanceof \UnitEnum) { $argumentsInformation[] = ltrim(var_export($argument, true), '\\'); } elseif ($argument instanceof AbstractArgument) { - $argumentsInformation[] = sprintf('Abstract argument (%s)', $argument->getText()); + $argumentsInformation[] = \sprintf('Abstract argument (%s)', $argument->getText()); } else { - $argumentsInformation[] = \is_array($argument) ? sprintf('Array (%d element(s))', \count($argument)) : $argument; + $argumentsInformation[] = \is_array($argument) ? \sprintf('Array (%d element(s))', \count($argument)) : $argument; } } @@ -394,7 +394,7 @@ protected function describeContainerDefinition(Definition $definition, array $op protected function describeContainerDeprecations(ContainerBuilder $container, array $options = []): void { - $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $container->getParameter('kernel.build_dir'), $container->getParameter('kernel.container_class')); + $containerDeprecationFilePath = \sprintf('%s/%sDeprecations.log', $container->getParameter('kernel.build_dir'), $container->getParameter('kernel.container_class')); if (!file_exists($containerDeprecationFilePath)) { $options['output']->warning('The deprecation file does not exist, please try warming the cache first.'); @@ -411,19 +411,19 @@ protected function describeContainerDeprecations(ContainerBuilder $container, ar $formattedLogs = []; $remainingCount = 0; foreach ($logs as $log) { - $formattedLogs[] = sprintf("%sx: %s\n in %s:%s", $log['count'], $log['message'], $log['file'], $log['line']); + $formattedLogs[] = \sprintf("%sx: %s\n in %s:%s", $log['count'], $log['message'], $log['file'], $log['line']); $remainingCount += $log['count']; } - $options['output']->title(sprintf('Remaining deprecations (%s)', $remainingCount)); + $options['output']->title(\sprintf('Remaining deprecations (%s)', $remainingCount)); $options['output']->listing($formattedLogs); } protected function describeContainerAlias(Alias $alias, array $options = [], ?ContainerBuilder $container = null): void { if ($alias->isPublic() && !$alias->isPrivate()) { - $options['output']->comment(sprintf('This service is a public alias for the service %s', (string) $alias)); + $options['output']->comment(\sprintf('This service is a public alias for the service %s', (string) $alias)); } else { - $options['output']->comment(sprintf('This service is a private alias for the service %s', (string) $alias)); + $options['output']->comment(\sprintf('This service is a private alias for the service %s', (string) $alias)); } if (!$container) { @@ -442,7 +442,7 @@ protected function describeContainerParameter(mixed $parameter, ?array $deprecat if ($deprecation) { $rows[] = [new TableCell( - sprintf('(Since %s %s: %s)', $deprecation[0], $deprecation[1], sprintf(...\array_slice($deprecation, 2))), + \sprintf('(Since %s %s: %s)', $deprecation[0], $deprecation[1], \sprintf(...\array_slice($deprecation, 2))), ['colspan' => 2] )]; } @@ -520,11 +520,11 @@ protected function describeEventDispatcherListeners(EventDispatcherInterface $ev $title = 'Registered Listeners'; if (null !== $dispatcherServiceName) { - $title .= sprintf(' of Event Dispatcher "%s"', $dispatcherServiceName); + $title .= \sprintf(' of Event Dispatcher "%s"', $dispatcherServiceName); } if (null !== $event) { - $title .= sprintf(' for "%s" Event', $event); + $title .= \sprintf(' for "%s" Event', $event); $registeredListeners = $eventDispatcher->getListeners($event); } else { $title .= ' Grouped by Event'; @@ -538,7 +538,7 @@ protected function describeEventDispatcherListeners(EventDispatcherInterface $ev } else { ksort($registeredListeners); foreach ($registeredListeners as $eventListened => $eventListeners) { - $options['output']->section(sprintf('"%s" event', $eventListened)); + $options['output']->section(\sprintf('"%s" event', $eventListened)); $this->renderEventListenerTable($eventDispatcher, $eventListened, $eventListeners, $options['output']); } } @@ -555,7 +555,7 @@ private function renderEventListenerTable(EventDispatcherInterface $eventDispatc $tableRows = []; foreach ($eventListeners as $order => $listener) { - $tableRows[] = [sprintf('#%d', $order + 1), $this->formatCallable($listener), $eventDispatcher->getListenerPriority($event, $listener)]; + $tableRows[] = [\sprintf('#%d', $order + 1), $this->formatCallable($listener), $eventDispatcher->getListenerPriority($event, $listener)]; } $io->table($tableHeaders, $tableRows); @@ -571,7 +571,7 @@ private function formatRouterConfig(array $config): string $configAsString = ''; foreach ($config as $key => $value) { - $configAsString .= sprintf("\n%s: %s", $key, $this->formatValue($value)); + $configAsString .= \sprintf("\n%s: %s", $key, $this->formatValue($value)); } return trim($configAsString); @@ -625,7 +625,7 @@ private function formatControllerLink(mixed $controller, string $anchorText, ?ca $fileLink = $this->fileLinkFormatter->format($r->getFileName(), $r->getStartLine()); if ($fileLink) { - return sprintf('%s', $fileLink, $anchorText); + return \sprintf('%s', $fileLink, $anchorText); } return $anchorText; @@ -635,14 +635,14 @@ private function formatCallable(mixed $callable): string { if (\is_array($callable)) { if (\is_object($callable[0])) { - return sprintf('%s::%s()', $callable[0]::class, $callable[1]); + return \sprintf('%s::%s()', $callable[0]::class, $callable[1]); } - return sprintf('%s::%s()', $callable[0], $callable[1]); + return \sprintf('%s::%s()', $callable[0], $callable[1]); } if (\is_string($callable)) { - return sprintf('%s()', $callable); + return \sprintf('%s()', $callable); } if ($callable instanceof \Closure) { @@ -651,14 +651,14 @@ private function formatCallable(mixed $callable): string return 'Closure()'; } if ($class = $r->getClosureCalledClass()) { - return sprintf('%s::%s()', $class->name, $r->name); + return \sprintf('%s::%s()', $class->name, $r->name); } return $r->name.'()'; } if (method_exists($callable, '__invoke')) { - return sprintf('%s::__invoke()', $callable::class); + return \sprintf('%s::__invoke()', $callable::class); } throw new \InvalidArgumentException('Callable is not describable.'); diff --git a/Console/Descriptor/XmlDescriptor.php b/Console/Descriptor/XmlDescriptor.php index c52b19667..dd2744f80 100644 --- a/Console/Descriptor/XmlDescriptor.php +++ b/Console/Descriptor/XmlDescriptor.php @@ -110,7 +110,7 @@ protected function describeContainerEnvVars(array $envs, array $options = []): v protected function describeContainerDeprecations(ContainerBuilder $container, array $options = []): void { - $containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $container->getParameter('kernel.build_dir'), $container->getParameter('kernel.container_class')); + $containerDeprecationFilePath = \sprintf('%s/%sDeprecations.log', $container->getParameter('kernel.build_dir'), $container->getParameter('kernel.container_class')); if (!file_exists($containerDeprecationFilePath)) { throw new RuntimeException('The deprecation file does not exist, please try warming the cache first.'); } @@ -243,7 +243,7 @@ private function getContainerParametersDocument(ParameterBag $parameters): \DOMD $parameterXML->appendChild(new \DOMText($this->formatParameter($value))); if (isset($deprecatedParameters[$key])) { - $parameterXML->setAttribute('deprecated', sprintf('Since %s %s: %s', $deprecatedParameters[$key][0], $deprecatedParameters[$key][1], sprintf(...\array_slice($deprecatedParameters[$key], 2)))); + $parameterXML->setAttribute('deprecated', \sprintf('Since %s %s: %s', $deprecatedParameters[$key][0], $deprecatedParameters[$key][1], \sprintf(...\array_slice($deprecatedParameters[$key], 2)))); } } @@ -341,7 +341,7 @@ private function getContainerDefinitionDocument(Definition $definition, ?string if ($factory[0] instanceof Reference) { $factoryXML->setAttribute('service', (string) $factory[0]); } elseif ($factory[0] instanceof Definition) { - $factoryXML->setAttribute('service', sprintf('inline factory service (%s)', $factory[0]->getClass() ?? 'not configured')); + $factoryXML->setAttribute('service', \sprintf('inline factory service (%s)', $factory[0]->getClass() ?? 'not configured')); } else { $factoryXML->setAttribute('class', $factory[0]); } @@ -490,7 +490,7 @@ private function getContainerParameterDocument(mixed $parameter, ?array $depreca $parameterXML->setAttribute('key', $options['parameter']); if ($deprecation) { - $parameterXML->setAttribute('deprecated', sprintf('Since %s %s: %s', $deprecation[0], $deprecation[1], sprintf(...\array_slice($deprecation, 2)))); + $parameterXML->setAttribute('deprecated', \sprintf('Since %s %s: %s', $deprecation[0], $deprecation[1], \sprintf(...\array_slice($deprecation, 2)))); } } diff --git a/Controller/AbstractController.php b/Controller/AbstractController.php index f0c1d98ee..af453619b 100644 --- a/Controller/AbstractController.php +++ b/Controller/AbstractController.php @@ -72,7 +72,7 @@ public function setContainer(ContainerInterface $container): ?ContainerInterface protected function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null { if (!$this->container->has('parameter_bag')) { - throw new ServiceNotFoundException('parameter_bag.', null, null, [], sprintf('The "%s::getParameter()" method is missing a parameter bag to work properly. Did you forget to register your controller as a service subscriber? This can be fixed either by using autoconfiguration or by manually wiring a "parameter_bag" in the service locator passed to the controller.', static::class)); + throw new ServiceNotFoundException('parameter_bag.', null, null, [], \sprintf('The "%s::getParameter()" method is missing a parameter bag to work properly. Did you forget to register your controller as a service subscriber? This can be fixed either by using autoconfiguration or by manually wiring a "parameter_bag" in the service locator passed to the controller.', static::class)); } return $this->container->get('parameter_bag')->get($name); @@ -182,7 +182,7 @@ protected function addFlash(string $type, mixed $message): void } if (!$session instanceof FlashBagAwareSessionInterface) { - throw new \LogicException(sprintf('You cannot use the addFlash method because class "%s" doesn\'t implement "%s".', get_debug_type($session), FlashBagAwareSessionInterface::class)); + throw new \LogicException(\sprintf('You cannot use the addFlash method because class "%s" doesn\'t implement "%s".', get_debug_type($session), FlashBagAwareSessionInterface::class)); } $session->getFlashBag()->add($type, $message); @@ -415,7 +415,7 @@ protected function sendEarlyHints(iterable $links = [], ?Response $response = nu private function doRenderView(string $view, ?string $block, array $parameters, string $method): string { if (!$this->container->has('twig')) { - throw new \LogicException(sprintf('You cannot use the "%s" method if the Twig Bundle is not available. Try running "composer require symfony/twig-bundle".', $method)); + throw new \LogicException(\sprintf('You cannot use the "%s" method if the Twig Bundle is not available. Try running "composer require symfony/twig-bundle".', $method)); } foreach ($parameters as $k => $v) { diff --git a/Controller/ControllerResolver.php b/Controller/ControllerResolver.php index af8b49429..269709963 100644 --- a/Controller/ControllerResolver.php +++ b/Controller/ControllerResolver.php @@ -26,7 +26,7 @@ protected function instantiateController(string $class): object if ($controller instanceof AbstractController) { if (null === $previousContainer = $controller->setContainer($this->container)) { - throw new \LogicException(sprintf('"%s" has no container set, did you forget to define it as a service subscriber?', $class)); + throw new \LogicException(\sprintf('"%s" has no container set, did you forget to define it as a service subscriber?', $class)); } else { $controller->setContainer($previousContainer); } diff --git a/Controller/RedirectController.php b/Controller/RedirectController.php index 1001fad63..65d2be121 100644 --- a/Controller/RedirectController.php +++ b/Controller/RedirectController.php @@ -168,7 +168,7 @@ public function __invoke(Request $request): Response if (\array_key_exists('route', $p)) { if (\array_key_exists('path', $p)) { - throw new \RuntimeException(sprintf('Ambiguous redirection settings, use the "path" or "route" parameter, not both: "%s" and "%s" found respectively in "%s" routing configuration.', $p['path'], $p['route'], $request->attributes->get('_route'))); + throw new \RuntimeException(\sprintf('Ambiguous redirection settings, use the "path" or "route" parameter, not both: "%s" and "%s" found respectively in "%s" routing configuration.', $p['path'], $p['route'], $request->attributes->get('_route'))); } return $this->redirectAction($request, $p['route'], $p['permanent'] ?? false, $p['ignoreAttributes'] ?? false, $p['keepRequestMethod'] ?? false, $p['keepQueryParams'] ?? false); @@ -178,6 +178,6 @@ public function __invoke(Request $request): Response return $this->urlRedirectAction($request, $p['path'], $p['permanent'] ?? false, $p['scheme'] ?? null, $p['httpPort'] ?? null, $p['httpsPort'] ?? null, $p['keepRequestMethod'] ?? false); } - throw new \RuntimeException(sprintf('The parameter "path" or "route" is required to configure the redirect action in "%s" routing configuration.', $request->attributes->get('_route'))); + throw new \RuntimeException(\sprintf('The parameter "path" or "route" is required to configure the redirect action in "%s" routing configuration.', $request->attributes->get('_route'))); } } diff --git a/DependencyInjection/Compiler/ProfilerPass.php b/DependencyInjection/Compiler/ProfilerPass.php index 05fe0a451..4da07e64a 100644 --- a/DependencyInjection/Compiler/ProfilerPass.php +++ b/DependencyInjection/Compiler/ProfilerPass.php @@ -42,7 +42,7 @@ public function process(ContainerBuilder $container): void if (isset($attributes[0]['template']) || is_subclass_of($collectorClass, TemplateAwareDataCollectorInterface::class)) { $idForTemplate = $attributes[0]['id'] ?? $collectorClass; if (!$idForTemplate) { - throw new InvalidArgumentException(sprintf('Data collector service "%s" must have an id attribute in order to specify a template.', $id)); + throw new InvalidArgumentException(\sprintf('Data collector service "%s" must have an id attribute in order to specify a template.', $id)); } $template = [$idForTemplate, $attributes[0]['template'] ?? $collectorClass::getTemplate()]; } diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index a2a141afb..ae2523e51 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -128,9 +128,9 @@ public function process(ContainerBuilder $container): void } $services = array_keys($container->findTaggedServiceIds($tag)); - $message = sprintf('Tag "%s" was defined on service(s) "%s", but was never used.', $tag, implode('", "', $services)); + $message = \sprintf('Tag "%s" was defined on service(s) "%s", but was never used.', $tag, implode('", "', $services)); if ($candidates) { - $message .= sprintf(' Did you mean "%s"?', implode('", "', $candidates)); + $message .= \sprintf(' Did you mean "%s"?', implode('", "', $candidates)); } $container->log($this, $message); diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 92c20d139..d5137dc2b 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -361,7 +361,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void foreach ($workflows as $key => $workflow) { if (isset($workflow['enabled']) && false === $workflow['enabled']) { - throw new LogicException(sprintf('Cannot disable a single workflow. Remove the configuration for the workflow "%s" instead.', $key)); + throw new LogicException(\sprintf('Cannot disable a single workflow. Remove the configuration for the workflow "%s" instead.', $key)); } unset($workflows[$key]['enabled']); @@ -1328,7 +1328,7 @@ private function addExceptionsSection(ArrayNodeDefinition $rootNode): void ->info('The level of log message. Null to let Symfony decide.') ->validate() ->ifTrue(fn ($v) => null !== $v && !\in_array($v, $logLevels, true)) - ->thenInvalid(sprintf('The log level is not valid. Pick one among "%s".', implode('", "', $logLevels))) + ->thenInvalid(\sprintf('The log level is not valid. Pick one among "%s".', implode('", "', $logLevels))) ->end() ->defaultNull() ->end() @@ -1496,7 +1496,7 @@ private function addMessengerSection(ArrayNodeDefinition $rootNode, callable $en ->end() ->validate() ->ifTrue(fn ($v) => isset($v['buses']) && null !== $v['default_bus'] && !isset($v['buses'][$v['default_bus']])) - ->then(fn ($v) => throw new InvalidConfigurationException(sprintf('The specified default bus "%s" is not configured. Available buses are "%s".', $v['default_bus'], implode('", "', array_keys($v['buses']))))) + ->then(fn ($v) => throw new InvalidConfigurationException(\sprintf('The specified default bus "%s" is not configured. Available buses are "%s".', $v['default_bus'], implode('", "', array_keys($v['buses']))))) ->end() ->children() ->arrayNode('routing') diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 942358181..fd6cb4b83 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -669,7 +669,7 @@ public function load(array $configs, ContainerBuilder $container): void $tagAttributes = get_object_vars($attribute); if ($reflector instanceof \ReflectionMethod) { if (isset($tagAttributes['method'])) { - throw new LogicException(sprintf('AsEventListener attribute cannot declare a method on "%s::%s()".', $reflector->class, $reflector->name)); + throw new LogicException(\sprintf('AsEventListener attribute cannot declare a method on "%s::%s()".', $reflector->class, $reflector->name)); } $tagAttributes['method'] = $reflector->getName(); } @@ -687,7 +687,7 @@ public function load(array $configs, ContainerBuilder $container): void unset($tagAttributes['fromTransport']); if ($reflector instanceof \ReflectionMethod) { if (isset($tagAttributes['method'])) { - throw new LogicException(sprintf('AsMessageHandler attribute cannot declare a method on "%s::%s()".', $reflector->class, $reflector->name)); + throw new LogicException(\sprintf('AsMessageHandler attribute cannot declare a method on "%s::%s()".', $reflector->class, $reflector->name)); } $tagAttributes['method'] = $reflector->getName(); } @@ -711,7 +711,7 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu ]; if ($reflector instanceof \ReflectionMethod) { if (isset($tagAttributes['method'])) { - throw new LogicException(sprintf('"%s" attribute cannot declare a method on "%s::%s()".', $attribute::class, $reflector->class, $reflector->name)); + throw new LogicException(\sprintf('"%s" attribute cannot declare a method on "%s::%s()".', $attribute::class, $reflector->class, $reflector->name)); } $tagAttributes['method'] = $reflector->getName(); } @@ -897,7 +897,7 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ // Choose storage class based on the DSN [$class] = explode(':', $config['dsn'], 2); if ('file' !== $class) { - throw new \LogicException(sprintf('Driver "%s" is not supported for the profiler.', $class)); + throw new \LogicException(\sprintf('Driver "%s" is not supported for the profiler.', $class)); } $container->setParameter('profiler.storage.dsn', $config['dsn']); @@ -936,7 +936,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ foreach ($config['workflows'] as $name => $workflow) { $type = $workflow['type']; - $workflowId = sprintf('%s.%s', $type, $name); + $workflowId = \sprintf('%s.%s', $type, $name); // Process Metadata (workflow + places (transition is done in the "create transition" block)) $metadataStoreDefinition = new Definition(Workflow\Metadata\InMemoryMetadataStore::class, [[], [], null]); @@ -962,14 +962,14 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ foreach ($workflow['transitions'] as $transition) { if ('workflow' === $type) { $transitionDefinition = new Definition(Workflow\Transition::class, [$transition['name'], $transition['from'], $transition['to']]); - $transitionId = sprintf('.%s.transition.%s', $workflowId, $transitionCounter++); + $transitionId = \sprintf('.%s.transition.%s', $workflowId, $transitionCounter++); $container->setDefinition($transitionId, $transitionDefinition); $transitions[] = new Reference($transitionId); if (isset($transition['guard'])) { $configuration = new Definition(Workflow\EventListener\GuardExpression::class); $configuration->addArgument(new Reference($transitionId)); $configuration->addArgument($transition['guard']); - $eventName = sprintf('workflow.%s.guard.%s', $name, $transition['name']); + $eventName = \sprintf('workflow.%s.guard.%s', $name, $transition['name']); $guardsConfiguration[$eventName][] = $configuration; } if ($transition['metadata']) { @@ -982,14 +982,14 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ foreach ($transition['from'] as $from) { foreach ($transition['to'] as $to) { $transitionDefinition = new Definition(Workflow\Transition::class, [$transition['name'], $from, $to]); - $transitionId = sprintf('.%s.transition.%s', $workflowId, $transitionCounter++); + $transitionId = \sprintf('.%s.transition.%s', $workflowId, $transitionCounter++); $container->setDefinition($transitionId, $transitionDefinition); $transitions[] = new Reference($transitionId); if (isset($transition['guard'])) { $configuration = new Definition(Workflow\EventListener\GuardExpression::class); $configuration->addArgument(new Reference($transitionId)); $configuration->addArgument($transition['guard']); - $eventName = sprintf('workflow.%s.guard.%s', $name, $transition['name']); + $eventName = \sprintf('workflow.%s.guard.%s', $name, $transition['name']); $guardsConfiguration[$eventName][] = $configuration; } if ($transition['metadata']) { @@ -1003,7 +1003,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ } } $metadataStoreDefinition->replaceArgument(2, $transitionsMetadataDefinition); - $container->setDefinition(sprintf('%s.metadata_store', $workflowId), $metadataStoreDefinition); + $container->setDefinition(\sprintf('%s.metadata_store', $workflowId), $metadataStoreDefinition); // Create places $places = array_column($workflow['places'], 'name'); @@ -1014,7 +1014,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $definitionDefinition->addArgument($places); $definitionDefinition->addArgument($transitions); $definitionDefinition->addArgument($initialMarking); - $definitionDefinition->addArgument(new Reference(sprintf('%s.metadata_store', $workflowId))); + $definitionDefinition->addArgument(new Reference(\sprintf('%s.metadata_store', $workflowId))); // Create MarkingStore $markingStoreDefinition = null; @@ -1029,8 +1029,8 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ } // Create Workflow - $workflowDefinition = new ChildDefinition(sprintf('%s.abstract', $type)); - $workflowDefinition->replaceArgument(0, new Reference(sprintf('%s.definition', $workflowId))); + $workflowDefinition = new ChildDefinition(\sprintf('%s.abstract', $type)); + $workflowDefinition->replaceArgument(0, new Reference(\sprintf('%s.definition', $workflowId))); $workflowDefinition->replaceArgument(1, $markingStoreDefinition); $workflowDefinition->replaceArgument(3, $name); $workflowDefinition->replaceArgument(4, $workflow['events_to_dispatch']); @@ -1044,7 +1044,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ // Store to container $container->setDefinition($workflowId, $workflowDefinition); - $container->setDefinition(sprintf('%s.definition', $workflowId), $definitionDefinition); + $container->setDefinition(\sprintf('%s.definition', $workflowId), $definitionDefinition); $container->registerAliasForArgument($workflowId, WorkflowInterface::class, $name.'.'.$type); $container->registerAliasForArgument($workflowId, WorkflowInterface::class, $name); @@ -1073,11 +1073,11 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ if ($workflow['audit_trail']['enabled']) { $listener = new Definition(Workflow\EventListener\AuditTrailListener::class); $listener->addTag('monolog.logger', ['channel' => 'workflow']); - $listener->addTag('kernel.event_listener', ['event' => sprintf('workflow.%s.leave', $name), 'method' => 'onLeave']); - $listener->addTag('kernel.event_listener', ['event' => sprintf('workflow.%s.transition', $name), 'method' => 'onTransition']); - $listener->addTag('kernel.event_listener', ['event' => sprintf('workflow.%s.enter', $name), 'method' => 'onEnter']); + $listener->addTag('kernel.event_listener', ['event' => \sprintf('workflow.%s.leave', $name), 'method' => 'onLeave']); + $listener->addTag('kernel.event_listener', ['event' => \sprintf('workflow.%s.transition', $name), 'method' => 'onTransition']); + $listener->addTag('kernel.event_listener', ['event' => \sprintf('workflow.%s.enter', $name), 'method' => 'onEnter']); $listener->addArgument(new Reference('logger')); - $container->setDefinition(sprintf('.%s.listener.audit_trail', $workflowId), $listener); + $container->setDefinition(\sprintf('.%s.listener.audit_trail', $workflowId), $listener); } // Add Guard Listener @@ -1105,7 +1105,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $guard->addTag('kernel.event_listener', ['event' => $eventName, 'method' => 'onTransition']); } - $container->setDefinition(sprintf('.%s.listener.guard', $workflowId), $guard); + $container->setDefinition(\sprintf('.%s.listener.guard', $workflowId), $guard); $container->setParameter('workflow.has_guard_listeners', true); } } @@ -1307,7 +1307,7 @@ private function registerAssetMapperConfiguration(array $config, ContainerBuilde $paths = $config['paths']; foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) { if ($container->fileExists($dir = $bundle['path'].'/Resources/public') || $container->fileExists($dir = $bundle['path'].'/public')) { - $paths[$dir] = sprintf('bundles/%s', preg_replace('/bundle$/', '', strtolower($name))); + $paths[$dir] = \sprintf('bundles/%s', preg_replace('/bundle$/', '', strtolower($name))); } } $excludedPathPatterns = []; @@ -1484,7 +1484,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder if ($container->fileExists($dir)) { $dirs[] = $transPaths[] = $dir; } else { - throw new \UnexpectedValueException(sprintf('"%s" defined in translator.paths does not exist or is not a directory.', $dir)); + throw new \UnexpectedValueException(\sprintf('"%s" defined in translator.paths does not exist or is not a directory.', $dir)); } } @@ -1568,7 +1568,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder foreach ($classToServices as $class => $service) { $package = substr($service, \strlen('translation.provider_factory.')); - if (!$container->hasDefinition('http_client') || !ContainerBuilder::willBeAvailable(sprintf('symfony/%s-translation-provider', $package), $class, $parentPackages)) { + if (!$container->hasDefinition('http_client') || !ContainerBuilder::willBeAvailable(\sprintf('symfony/%s-translation-provider', $package), $class, $parentPackages)) { $container->removeDefinition($service); } } @@ -1726,11 +1726,11 @@ private function registerMappingFilesFromConfig(ContainerBuilder $container, arr $container->addResource(new DirectoryResource($path, '/^$/')); } elseif ($container->fileExists($path, false)) { if (!preg_match('/\.(xml|ya?ml)$/', $path, $matches)) { - throw new \RuntimeException(sprintf('Unsupported mapping type in "%s", supported types are XML & Yaml.', $path)); + throw new \RuntimeException(\sprintf('Unsupported mapping type in "%s", supported types are XML & Yaml.', $path)); } $fileRecorder($matches[1], $path); } else { - throw new \RuntimeException(sprintf('Could not open file or directory "%s".', $path)); + throw new \RuntimeException(\sprintf('Could not open file or directory "%s".', $path)); } } } @@ -1790,7 +1790,7 @@ private function registerSecretsConfiguration(array $config, ContainerBuilder $c if ($config['decryption_env_var']) { if (!preg_match('/^(?:[-.\w\\\\]*+:)*+\w++$/', $config['decryption_env_var'])) { - throw new InvalidArgumentException(sprintf('Invalid value "%s" set as "decryption_env_var": only "word" characters are allowed.', $config['decryption_env_var'])); + throw new InvalidArgumentException(\sprintf('Invalid value "%s" set as "decryption_env_var": only "word" characters are allowed.', $config['decryption_env_var'])); } if (ContainerBuilder::willBeAvailable('symfony/string', LazyString::class, ['symfony/framework-bundle'])) { @@ -2173,7 +2173,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder $failureTransports = []; if ($config['failure_transport']) { if (!isset($config['transports'][$config['failure_transport']])) { - throw new LogicException(sprintf('Invalid Messenger configuration: the failure transport "%s" is not a valid transport or service id.', $config['failure_transport'])); + throw new LogicException(\sprintf('Invalid Messenger configuration: the failure transport "%s" is not a valid transport or service id.', $config['failure_transport'])); } $container->setAlias('messenger.failure_transports.default', 'messenger.transport.'.$config['failure_transport']); @@ -2209,7 +2209,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder if (null !== $transport['retry_strategy']['service']) { $transportRetryReferences[$name] = new Reference($transport['retry_strategy']['service']); } else { - $retryServiceId = sprintf('messenger.retry.multiplier_retry_strategy.%s', $name); + $retryServiceId = \sprintf('messenger.retry.multiplier_retry_strategy.%s', $name); $retryDefinition = new ChildDefinition('messenger.retry.abstract_multiplier_retry_strategy'); $retryDefinition ->replaceArgument(0, $transport['retry_strategy']['max_retries']) @@ -2244,7 +2244,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder foreach ($config['transports'] as $name => $transport) { if ($transport['failure_transport']) { if (!isset($senderReferences[$transport['failure_transport']])) { - throw new LogicException(sprintf('Invalid Messenger configuration: the failure transport "%s" is not a valid transport or service id.', $transport['failure_transport'])); + throw new LogicException(\sprintf('Invalid Messenger configuration: the failure transport "%s" is not a valid transport or service id.', $transport['failure_transport'])); } } } @@ -2255,16 +2255,16 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder foreach ($config['routing'] as $message => $messageConfiguration) { if ('*' !== $message && !class_exists($message) && !interface_exists($message, false) && !preg_match('/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+\\\\)++\*$/', $message)) { if (str_contains($message, '*')) { - throw new LogicException(sprintf('Invalid Messenger routing configuration: invalid namespace "%s" wildcard.', $message)); + throw new LogicException(\sprintf('Invalid Messenger routing configuration: invalid namespace "%s" wildcard.', $message)); } - throw new LogicException(sprintf('Invalid Messenger routing configuration: class or interface "%s" not found.', $message)); + throw new LogicException(\sprintf('Invalid Messenger routing configuration: class or interface "%s" not found.', $message)); } // make sure senderAliases contains all senders foreach ($messageConfiguration['senders'] as $sender) { if (!isset($senderReferences[$sender])) { - throw new LogicException(sprintf('Invalid Messenger routing configuration: the "%s" class is being routed to a sender called "%s". This is not a valid transport or service id.', $message, $sender)); + throw new LogicException(\sprintf('Invalid Messenger routing configuration: the "%s" class is being routed to a sender called "%s". This is not a valid transport or service id.', $message, $sender)); } } @@ -2471,7 +2471,7 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder foreach ($config['scoped_clients'] as $name => $scopeConfig) { if ($container->has($name)) { - throw new InvalidArgumentException(sprintf('Invalid scope name: "%s" is reserved.', $name)); + throw new InvalidArgumentException(\sprintf('Invalid scope name: "%s" is reserved.', $name)); } $scope = $scopeConfig['scope'] ?? null; @@ -2632,7 +2632,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co foreach ($classToServices as $class => $service) { $package = substr($service, \strlen('mailer.transport_factory.')); - if (!ContainerBuilder::willBeAvailable(sprintf('symfony/%s-mailer', 'gmail' === $package ? 'google' : $package), $class, ['symfony/framework-bundle', 'symfony/mailer'])) { + if (!ContainerBuilder::willBeAvailable(\sprintf('symfony/%s-mailer', 'gmail' === $package ? 'google' : $package), $class, ['symfony/framework-bundle', 'symfony/mailer'])) { $container->removeDefinition($service); } } @@ -2651,7 +2651,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co foreach ($webhookRequestParsers as $class => $service) { $package = substr($service, \strlen('mailer.webhook.request_parser.')); - if (!ContainerBuilder::willBeAvailable(sprintf('symfony/%s-mailer', 'gmail' === $package ? 'google' : $package), $class, ['symfony/framework-bundle', 'symfony/mailer'])) { + if (!ContainerBuilder::willBeAvailable(\sprintf('symfony/%s-mailer', 'gmail' === $package ? 'google' : $package), $class, ['symfony/framework-bundle', 'symfony/mailer'])) { $container->removeDefinition($service); } } @@ -2835,7 +2835,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ foreach ($classToServices as $class => $service) { $package = substr($service, \strlen('notifier.transport_factory.')); - if (!ContainerBuilder::willBeAvailable(sprintf('symfony/%s-notifier', $package), $class, $parentPackages)) { + if (!ContainerBuilder::willBeAvailable(\sprintf('symfony/%s-notifier', $package), $class, $parentPackages)) { $container->removeDefinition($service); } } @@ -2891,7 +2891,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ foreach ($webhookRequestParsers as $class => $service) { $package = substr($service, \strlen('notifier.webhook.request_parser.')); - if (!ContainerBuilder::willBeAvailable(sprintf('symfony/%s-notifier', $package), $class, ['symfony/framework-bundle', 'symfony/notifier'])) { + if (!ContainerBuilder::willBeAvailable(\sprintf('symfony/%s-notifier', $package), $class, ['symfony/framework-bundle', 'symfony/notifier'])) { $container->removeDefinition($service); } } @@ -2941,11 +2941,11 @@ private function registerRateLimiterConfiguration(array $config, ContainerBuilde if (null !== $limiterConfig['lock_factory']) { if (!interface_exists(LockInterface::class)) { - throw new LogicException(sprintf('Rate limiter "%s" requires the Lock component to be installed. Try running "composer require symfony/lock".', $name)); + throw new LogicException(\sprintf('Rate limiter "%s" requires the Lock component to be installed. Try running "composer require symfony/lock".', $name)); } if (!$this->isInitializedConfigEnabled('lock')) { - throw new LogicException(sprintf('Rate limiter "%s" requires the Lock component to be configured.', $name)); + throw new LogicException(\sprintf('Rate limiter "%s" requires the Lock component to be configured.', $name)); } $limiter->replaceArgument(2, new Reference($limiterConfig['lock_factory'])); @@ -3111,7 +3111,7 @@ private function isInitializedConfigEnabled(string $path): bool return $this->configsEnabled[$path]; } - throw new LogicException(sprintf('Can not read config enabled at "%s" because it has not been initialized.', $path)); + throw new LogicException(\sprintf('Can not read config enabled at "%s" because it has not been initialized.', $path)); } private function readConfigEnabled(string $path, ContainerBuilder $container, array $config): bool diff --git a/EventListener/ConsoleProfilerListener.php b/EventListener/ConsoleProfilerListener.php index 7bf23f04c..3df9c61b9 100644 --- a/EventListener/ConsoleProfilerListener.php +++ b/EventListener/ConsoleProfilerListener.php @@ -142,7 +142,7 @@ public function profile(ConsoleTerminateEvent $event): void if ($this->urlGenerator && $output) { $token = $p->getToken(); - $output->writeln(sprintf( + $output->writeln(\sprintf( 'See profile %s', $this->urlGenerator->generate('_profiler', ['token' => $token], UrlGeneratorInterface::ABSOLUTE_URL), $token diff --git a/EventListener/SuggestMissingPackageSubscriber.php b/EventListener/SuggestMissingPackageSubscriber.php index d7bdc8e66..a5a0d5d63 100644 --- a/EventListener/SuggestMissingPackageSubscriber.php +++ b/EventListener/SuggestMissingPackageSubscriber.php @@ -66,7 +66,7 @@ public function onConsoleError(ConsoleErrorEvent $event): void return; } - $message = sprintf("%s\n\nYou may be looking for a command provided by the \"%s\" which is currently not installed. Try running \"composer require %s\".", $error->getMessage(), $suggestion[0], $suggestion[1]); + $message = \sprintf("%s\n\nYou may be looking for a command provided by the \"%s\" which is currently not installed. Try running \"composer require %s\".", $error->getMessage(), $suggestion[0], $suggestion[1]); $event->setError(new CommandNotFoundException($message)); } diff --git a/KernelBrowser.php b/KernelBrowser.php index e06b9a056..6bc82b97c 100644 --- a/KernelBrowser.php +++ b/KernelBrowser.php @@ -103,11 +103,11 @@ public function enableReboot(): void public function loginUser(object $user, string $firewallContext = 'main', array $tokenAttributes = []): static { if (!interface_exists(UserInterface::class)) { - throw new \LogicException(sprintf('"%s" requires symfony/security-core to be installed. Try running "composer require symfony/security-core".', __METHOD__)); + throw new \LogicException(\sprintf('"%s" requires symfony/security-core to be installed. Try running "composer require symfony/security-core".', __METHOD__)); } if (!$user instanceof UserInterface) { - throw new \LogicException(sprintf('The first argument of "%s" must be instance of "%s", "%s" provided.', __METHOD__, UserInterface::class, get_debug_type($user))); + throw new \LogicException(\sprintf('The first argument of "%s" must be instance of "%s", "%s" provided.', __METHOD__, UserInterface::class, get_debug_type($user))); } $token = new TestBrowserToken($user->getRoles(), $user, $firewallContext); diff --git a/Resources/config/cache.php b/Resources/config/cache.php index eceb9bdfd..ad4dca42d 100644 --- a/Resources/config/cache.php +++ b/Resources/config/cache.php @@ -83,7 +83,7 @@ '', // namespace 0, // default lifetime abstract_arg('version'), - sprintf('%s/pools/system', param('kernel.cache_dir')), + \sprintf('%s/pools/system', param('kernel.cache_dir')), service('logger')->ignoreOnInvalid(), ]) ->tag('cache.pool', ['clearer' => 'cache.system_clearer', 'reset' => 'reset']) @@ -105,7 +105,7 @@ ->args([ '', // namespace 0, // default lifetime - sprintf('%s/pools/app', param('kernel.cache_dir')), + \sprintf('%s/pools/app', param('kernel.cache_dir')), service('cache.default_marshaller')->ignoreOnInvalid(), ]) ->call('setLogger', [service('logger')->ignoreOnInvalid()]) diff --git a/Resources/config/collectors.php b/Resources/config/collectors.php index aa6d4e33c..954ddeffa 100644 --- a/Resources/config/collectors.php +++ b/Resources/config/collectors.php @@ -56,7 +56,7 @@ ->set('data_collector.logger', LoggerDataCollector::class) ->args([ service('logger')->ignoreOnInvalid(), - sprintf('%s/%s', param('kernel.build_dir'), param('kernel.container_class')), + \sprintf('%s/%s', param('kernel.build_dir'), param('kernel.container_class')), service('.virtual_request_stack')->ignoreOnInvalid(), ]) ->tag('monolog.logger', ['channel' => 'profiler']) diff --git a/Resources/config/services.php b/Resources/config/services.php index c85ccf5d0..895d2ce34 100644 --- a/Resources/config/services.php +++ b/Resources/config/services.php @@ -129,7 +129,7 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : [] ->args([ tagged_iterator('kernel.cache_warmer'), param('kernel.debug'), - sprintf('%s/%sDeprecations.log', param('kernel.build_dir'), param('kernel.container_class')), + \sprintf('%s/%sDeprecations.log', param('kernel.build_dir'), param('kernel.container_class')), ]) ->tag('container.no_preload') diff --git a/Routing/Router.php b/Routing/Router.php index 4dfb71e74..ede88740b 100644 --- a/Routing/Router.php +++ b/Routing/Router.php @@ -55,7 +55,7 @@ public function __construct(ContainerInterface $container, mixed $resource, arra } elseif ($container instanceof SymfonyContainerInterface) { $this->paramFetcher = $container->getParameter(...); } else { - throw new \LogicException(sprintf('You should either pass a "%s" instance or provide the $parameters argument of the "%s" method.', SymfonyContainerInterface::class, __METHOD__)); + throw new \LogicException(\sprintf('You should either pass a "%s" instance or provide the $parameters argument of the "%s" method.', SymfonyContainerInterface::class, __METHOD__)); } $this->defaultLocale = $defaultLocale; @@ -168,7 +168,7 @@ private function resolve(mixed $value): mixed } if (preg_match('/^env\((?:\w++:)*+\w++\)$/', $match[1])) { - throw new RuntimeException(sprintf('Using "%%%s%%" is not allowed in routing configuration.', $match[1])); + throw new RuntimeException(\sprintf('Using "%%%s%%" is not allowed in routing configuration.', $match[1])); } $resolved = ($this->paramFetcher)($match[1]); @@ -185,7 +185,7 @@ private function resolve(mixed $value): mixed } } - throw new RuntimeException(sprintf('The container parameter "%s", used in the route configuration value "%s", must be a string or numeric, but it is of type "%s".', $match[1], $value, get_debug_type($resolved))); + throw new RuntimeException(\sprintf('The container parameter "%s", used in the route configuration value "%s", must be a string or numeric, but it is of type "%s".', $match[1], $value, get_debug_type($resolved))); }, $value); return str_replace('%%', '%', $escapedValue); diff --git a/Secrets/AbstractVault.php b/Secrets/AbstractVault.php index 374964a06..882ec7862 100644 --- a/Secrets/AbstractVault.php +++ b/Secrets/AbstractVault.php @@ -36,7 +36,7 @@ abstract public function list(bool $reveal = false): array; protected function validateName(string $name): void { if (!preg_match('/^\w++$/D', $name)) { - throw new \LogicException(sprintf('Invalid secret name "%s": only "word" characters are allowed.', $name)); + throw new \LogicException(\sprintf('Invalid secret name "%s": only "word" characters are allowed.', $name)); } } diff --git a/Secrets/DotenvVault.php b/Secrets/DotenvVault.php index 7bdd74c37..15952611a 100644 --- a/Secrets/DotenvVault.php +++ b/Secrets/DotenvVault.php @@ -44,7 +44,7 @@ public function seal(string $name, string $value): void file_put_contents($this->dotenvFile, $content); - $this->lastMessage = sprintf('Secret "%s" %s in "%s".', $name, $count ? 'added' : 'updated', $this->getPrettyPath($this->dotenvFile)); + $this->lastMessage = \sprintf('Secret "%s" %s in "%s".', $name, $count ? 'added' : 'updated', $this->getPrettyPath($this->dotenvFile)); } public function reveal(string $name): ?string @@ -54,7 +54,7 @@ public function reveal(string $name): ?string $v = $_ENV[$name] ?? (str_starts_with($name, 'HTTP_') ? null : ($_SERVER[$name] ?? null)); if ('' === ($v ?? '')) { - $this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath($this->dotenvFile)); + $this->lastMessage = \sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath($this->dotenvFile)); return null; } @@ -72,12 +72,12 @@ public function remove(string $name): bool if ($count) { file_put_contents($this->dotenvFile, $content); - $this->lastMessage = sprintf('Secret "%s" removed from file "%s".', $name, $this->getPrettyPath($this->dotenvFile)); + $this->lastMessage = \sprintf('Secret "%s" removed from file "%s".', $name, $this->getPrettyPath($this->dotenvFile)); return true; } - $this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath($this->dotenvFile)); + $this->lastMessage = \sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath($this->dotenvFile)); return false; } diff --git a/Secrets/SodiumVault.php b/Secrets/SodiumVault.php index f09c3b3af..74713e28c 100644 --- a/Secrets/SodiumVault.php +++ b/Secrets/SodiumVault.php @@ -61,7 +61,7 @@ public function generateKeys(bool $override = false): bool } if (!$override && null !== $this->encryptionKey) { - $this->lastMessage = sprintf('Sodium keys already exist at "%s*.{public,private}" and won\'t be overridden.', $this->getPrettyPath($this->pathPrefix)); + $this->lastMessage = \sprintf('Sodium keys already exist at "%s*.{public,private}" and won\'t be overridden.', $this->getPrettyPath($this->pathPrefix)); return false; } @@ -72,7 +72,7 @@ public function generateKeys(bool $override = false): bool $this->export('encrypt.public', $this->encryptionKey); $this->export('decrypt.private', $this->decryptionKey); - $this->lastMessage = sprintf('Sodium keys have been generated at "%s*.public/private.php".', $this->getPrettyPath($this->pathPrefix)); + $this->lastMessage = \sprintf('Sodium keys have been generated at "%s*.public/private.php".', $this->getPrettyPath($this->pathPrefix)); return true; } @@ -88,9 +88,9 @@ public function seal(string $name, string $value): void $list = $this->list(); $list[$name] = null; uksort($list, 'strnatcmp'); - file_put_contents($this->pathPrefix.'list.php', sprintf("pathPrefix.'list.php', \sprintf("lastMessage = sprintf('Secret "%s" encrypted in "%s"; you can commit it.', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); + $this->lastMessage = \sprintf('Secret "%s" encrypted in "%s"; you can commit it.', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); } public function reveal(string $name): ?string @@ -100,13 +100,13 @@ public function reveal(string $name): ?string $filename = $this->getFilename($name); if (!is_file($file = $this->pathPrefix.$filename.'.php')) { - $this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); + $this->lastMessage = \sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); return null; } if (!\function_exists('sodium_crypto_box_seal')) { - $this->lastMessage = sprintf('Secret "%s" cannot be revealed as the "sodium" PHP extension missing. Try running "composer require paragonie/sodium_compat" if you cannot enable the extension."', $name); + $this->lastMessage = \sprintf('Secret "%s" cannot be revealed as the "sodium" PHP extension missing. Try running "composer require paragonie/sodium_compat" if you cannot enable the extension."', $name); return null; } @@ -114,13 +114,13 @@ public function reveal(string $name): ?string $this->loadKeys(); if ('' === $this->decryptionKey) { - $this->lastMessage = sprintf('Secret "%s" cannot be revealed as no decryption key was found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); + $this->lastMessage = \sprintf('Secret "%s" cannot be revealed as no decryption key was found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); return null; } if (false === $value = sodium_crypto_box_seal_open(include $file, $this->decryptionKey)) { - $this->lastMessage = sprintf('Secret "%s" cannot be revealed as the wrong decryption key was provided for "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); + $this->lastMessage = \sprintf('Secret "%s" cannot be revealed as the wrong decryption key was provided for "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); return null; } @@ -135,16 +135,16 @@ public function remove(string $name): bool $filename = $this->getFilename($name); if (!is_file($file = $this->pathPrefix.$filename.'.php')) { - $this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); + $this->lastMessage = \sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); return false; } $list = $this->list(); unset($list[$name]); - file_put_contents($this->pathPrefix.'list.php', sprintf("pathPrefix.'list.php', \sprintf("lastMessage = sprintf('Secret "%s" removed from "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); + $this->lastMessage = \sprintf('Secret "%s" removed from "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); return @unlink($file) || !file_exists($file); } @@ -206,7 +206,7 @@ private function loadKeys(): void } elseif ('' !== $this->decryptionKey) { $this->encryptionKey = sodium_crypto_box_publickey($this->decryptionKey); } else { - throw new \RuntimeException(sprintf('Encryption key not found in "%s".', \dirname($this->pathPrefix))); + throw new \RuntimeException(\sprintf('Encryption key not found in "%s".', \dirname($this->pathPrefix))); } } @@ -215,7 +215,7 @@ private function export(string $filename, string $data): void $b64 = 'decrypt.private' === $filename ? '// SYMFONY_DECRYPTION_SECRET='.base64_encode($data)."\n" : ''; $name = basename($this->pathPrefix.$filename); $data = str_replace('%', '\x', rawurlencode($data)); - $data = sprintf("createSecretsDir(); @@ -228,7 +228,7 @@ private function export(string $filename, string $data): void private function createSecretsDir(): void { if ($this->secretsDir && !is_dir($this->secretsDir) && !@mkdir($this->secretsDir, 0777, true) && !is_dir($this->secretsDir)) { - throw new \RuntimeException(sprintf('Unable to create the secrets directory (%s).', $this->secretsDir)); + throw new \RuntimeException(\sprintf('Unable to create the secrets directory (%s).', $this->secretsDir)); } $this->secretsDir = null; diff --git a/Test/BrowserKitAssertionsTrait.php b/Test/BrowserKitAssertionsTrait.php index 7eea648cb..1b7437b77 100644 --- a/Test/BrowserKitAssertionsTrait.php +++ b/Test/BrowserKitAssertionsTrait.php @@ -171,7 +171,7 @@ protected static function getClient(?AbstractBrowser $newClient = null): ?Abstra } if (!$client instanceof AbstractBrowser) { - static::fail(sprintf('A client must be set to make assertions on it. Did you forget to call "%s::createClient()"?', __CLASS__)); + static::fail(\sprintf('A client must be set to make assertions on it. Did you forget to call "%s::createClient()"?', __CLASS__)); } return $client; diff --git a/Test/DomCrawlerAssertionsTrait.php b/Test/DomCrawlerAssertionsTrait.php index 024c78f75..ede359bcc 100644 --- a/Test/DomCrawlerAssertionsTrait.php +++ b/Test/DomCrawlerAssertionsTrait.php @@ -126,18 +126,18 @@ public static function assertCheckboxNotChecked(string $fieldName, string $messa public static function assertFormValue(string $formSelector, string $fieldName, string $value, string $message = ''): void { $node = self::getCrawler()->filter($formSelector); - self::assertNotEmpty($node, sprintf('Form "%s" not found.', $formSelector)); + self::assertNotEmpty($node, \sprintf('Form "%s" not found.', $formSelector)); $values = $node->form()->getValues(); - self::assertArrayHasKey($fieldName, $values, $message ?: sprintf('Field "%s" not found in form "%s".', $fieldName, $formSelector)); + self::assertArrayHasKey($fieldName, $values, $message ?: \sprintf('Field "%s" not found in form "%s".', $fieldName, $formSelector)); self::assertSame($value, $values[$fieldName]); } public static function assertNoFormValue(string $formSelector, string $fieldName, string $message = ''): void { $node = self::getCrawler()->filter($formSelector); - self::assertNotEmpty($node, sprintf('Form "%s" not found.', $formSelector)); + self::assertNotEmpty($node, \sprintf('Form "%s" not found.', $formSelector)); $values = $node->form()->getValues(); - self::assertArrayNotHasKey($fieldName, $values, $message ?: sprintf('Field "%s" has a value in form "%s".', $fieldName, $formSelector)); + self::assertArrayNotHasKey($fieldName, $values, $message ?: \sprintf('Field "%s" has a value in form "%s".', $fieldName, $formSelector)); } private static function getCrawler(): Crawler diff --git a/Test/HttpClientAssertionsTrait.php b/Test/HttpClientAssertionsTrait.php index 20c64608e..9d22a822f 100644 --- a/Test/HttpClientAssertionsTrait.php +++ b/Test/HttpClientAssertionsTrait.php @@ -34,7 +34,7 @@ public static function assertHttpClientRequest(string $expectedUrl, string $expe $expectedRequestHasBeenFound = false; if (!\array_key_exists($httpClientId, $httpClientDataCollector->getClients())) { - static::fail(sprintf('HttpClient "%s" is not registered.', $httpClientId)); + static::fail(\sprintf('HttpClient "%s" is not registered.', $httpClientId)); } foreach ($httpClientDataCollector->getClients()[$httpClientId]['traces'] as $trace) { @@ -102,7 +102,7 @@ public function assertNotHttpClientRequest(string $unexpectedUrl, string $expect $unexpectedUrlHasBeenFound = false; if (!\array_key_exists($httpClientId, $httpClientDataCollector->getClients())) { - static::fail(sprintf('HttpClient "%s" is not registered.', $httpClientId)); + static::fail(\sprintf('HttpClient "%s" is not registered.', $httpClientId)); } foreach ($httpClientDataCollector->getClients()[$httpClientId]['traces'] as $trace) { @@ -114,7 +114,7 @@ public function assertNotHttpClientRequest(string $unexpectedUrl, string $expect } } - self::assertFalse($unexpectedUrlHasBeenFound, sprintf('Unexpected URL called: "%s" - "%s"', $expectedMethod, $unexpectedUrl)); + self::assertFalse($unexpectedUrlHasBeenFound, \sprintf('Unexpected URL called: "%s" - "%s"', $expectedMethod, $unexpectedUrl)); } public static function assertHttpClientRequestCount(int $count, string $httpClientId = 'http_client'): void diff --git a/Test/KernelTestCase.php b/Test/KernelTestCase.php index 632b782ad..9fd13323b 100644 --- a/Test/KernelTestCase.php +++ b/Test/KernelTestCase.php @@ -46,11 +46,11 @@ protected function tearDown(): void protected static function getKernelClass(): string { if (!isset($_SERVER['KERNEL_CLASS']) && !isset($_ENV['KERNEL_CLASS'])) { - throw new \LogicException(sprintf('You must set the KERNEL_CLASS environment variable to the fully-qualified class name of your Kernel in phpunit.xml / phpunit.xml.dist or override the "%1$s::createKernel()" or "%1$s::getKernelClass()" method.', static::class)); + throw new \LogicException(\sprintf('You must set the KERNEL_CLASS environment variable to the fully-qualified class name of your Kernel in phpunit.xml / phpunit.xml.dist or override the "%1$s::createKernel()" or "%1$s::getKernelClass()" method.', static::class)); } if (!class_exists($class = $_ENV['KERNEL_CLASS'] ?? $_SERVER['KERNEL_CLASS'])) { - throw new \RuntimeException(sprintf('Class "%s" doesn\'t exist or cannot be autoloaded. Check that the KERNEL_CLASS value in phpunit.xml matches the fully-qualified class name of your Kernel or override the "%s::createKernel()" method.', $class, static::class)); + throw new \RuntimeException(\sprintf('Class "%s" doesn\'t exist or cannot be autoloaded. Check that the KERNEL_CLASS value in phpunit.xml matches the fully-qualified class name of your Kernel or override the "%s::createKernel()" method.', $class, static::class)); } return $class; diff --git a/Test/TestContainer.php b/Test/TestContainer.php index e1e7a8592..77135fa06 100644 --- a/Test/TestContainer.php +++ b/Test/TestContainer.php @@ -78,7 +78,7 @@ public function set(string $id, mixed $service): void throw $e; } if (isset($container->privates[$renamedId])) { - throw new InvalidArgumentException(sprintf('The "%s" service is already initialized, you cannot replace it.', $id)); + throw new InvalidArgumentException(\sprintf('The "%s" service is already initialized, you cannot replace it.', $id)); } $container->privates[$renamedId] = $service; } diff --git a/Test/WebTestCase.php b/Test/WebTestCase.php index de31d4ba9..9c6ee9c98 100644 --- a/Test/WebTestCase.php +++ b/Test/WebTestCase.php @@ -38,7 +38,7 @@ protected function tearDown(): void protected static function createClient(array $options = [], array $server = []): KernelBrowser { if (static::$booted) { - throw new \LogicException(sprintf('Booting the kernel before calling "%s()" is not supported, the kernel should only be booted once.', __METHOD__)); + throw new \LogicException(\sprintf('Booting the kernel before calling "%s()" is not supported, the kernel should only be booted once.', __METHOD__)); } $kernel = static::bootKernel($options); diff --git a/Tests/Command/CacheClearCommand/CacheClearCommandTest.php b/Tests/Command/CacheClearCommand/CacheClearCommandTest.php index b950b5fd9..753c39cc8 100644 --- a/Tests/Command/CacheClearCommand/CacheClearCommandTest.php +++ b/Tests/Command/CacheClearCommand/CacheClearCommandTest.php @@ -62,7 +62,7 @@ public function testCacheIsFreshAfterCacheClearedWithWarmup() $configCacheFactory->cache( substr($file, 0, -5), function () use ($file) { - $this->fail(sprintf('Meta file "%s" is not fresh', (string) $file)); + $this->fail(\sprintf('Meta file "%s" is not fresh', (string) $file)); } ); } @@ -92,7 +92,7 @@ function () use ($file) { $containerRef->getFileName() ); $this->assertMatchesRegularExpression( - sprintf('/\'kernel.container_class\'\s*=>\s*\'%s\'/', $containerClass), + \sprintf('/\'kernel.container_class\'\s*=>\s*\'%s\'/', $containerClass), $this->fs->readFile($containerFile), 'kernel.container_class is properly set on the dumped container' ); diff --git a/Tests/Console/Descriptor/AbstractDescriptorTestCase.php b/Tests/Console/Descriptor/AbstractDescriptorTestCase.php index cc6b08fd2..dde1f000b 100644 --- a/Tests/Console/Descriptor/AbstractDescriptorTestCase.php +++ b/Tests/Console/Descriptor/AbstractDescriptorTestCase.php @@ -292,7 +292,7 @@ private static function getDescriptionTestData(iterable $objects): array { $data = []; foreach ($objects as $name => $object) { - $file = sprintf('%s.%s', trim($name, '.'), static::getFormat()); + $file = \sprintf('%s.%s', trim($name, '.'), static::getFormat()); $description = file_get_contents(__DIR__.'/../../Fixtures/Descriptor/'.$file); $data[] = [$object, $description, $file]; } @@ -313,7 +313,7 @@ private static function getContainerBuilderDescriptionTestData(array $objects): $data = []; foreach ($objects as $name => $object) { foreach ($variations as $suffix => $options) { - $file = sprintf('%s_%s.%s', trim($name, '.'), $suffix, static::getFormat()); + $file = \sprintf('%s_%s.%s', trim($name, '.'), $suffix, static::getFormat()); $description = file_get_contents(__DIR__.'/../../Fixtures/Descriptor/'.$file); $data[] = [$object, $description, $options, $file]; } @@ -332,7 +332,7 @@ private static function getEventDispatcherDescriptionTestData(array $objects): a $data = []; foreach ($objects as $name => $object) { foreach ($variations as $suffix => $options) { - $file = sprintf('%s_%s.%s', trim($name, '.'), $suffix, static::getFormat()); + $file = \sprintf('%s_%s.%s', trim($name, '.'), $suffix, static::getFormat()); $description = file_get_contents(__DIR__.'/../../Fixtures/Descriptor/'.$file); $data[] = [$object, $description, $options, $file]; } @@ -353,7 +353,7 @@ public static function getDescribeContainerBuilderWithPriorityTagsTestData(): ar $data = []; foreach (ObjectsProvider::getContainerBuildersWithPriorityTags() as $name => $object) { foreach ($variations as $suffix => $options) { - $file = sprintf('%s_%s.%s', trim($name, '.'), $suffix, static::getFormat()); + $file = \sprintf('%s_%s.%s', trim($name, '.'), $suffix, static::getFormat()); $description = file_get_contents(__DIR__.'/../../Fixtures/Descriptor/'.$file); $data[] = [$object, $description, $options]; } diff --git a/Tests/Console/Descriptor/TextDescriptorTest.php b/Tests/Console/Descriptor/TextDescriptorTest.php index 2404706d0..34e16f5e4 100644 --- a/Tests/Console/Descriptor/TextDescriptorTest.php +++ b/Tests/Console/Descriptor/TextDescriptorTest.php @@ -35,7 +35,7 @@ public static function getDescribeRouteWithControllerLinkTestData() foreach ($getDescribeData as $key => &$data) { $routeStub = $data[0]; - $routeStub->setDefault('_controller', sprintf('%s::%s', MyController::class, '__invoke')); + $routeStub->setDefault('_controller', \sprintf('%s::%s', MyController::class, '__invoke')); $file = $data[2]; $file = preg_replace('#(\..*?)$#', '_link$1', $file); $data = file_get_contents(__DIR__.'/../../Fixtures/Descriptor/'.$file); diff --git a/Tests/Controller/TestAbstractController.php b/Tests/Controller/TestAbstractController.php index 18f3eabb7..7c13aedb5 100644 --- a/Tests/Controller/TestAbstractController.php +++ b/Tests/Controller/TestAbstractController.php @@ -41,11 +41,11 @@ public function setContainer(ContainerInterface $container): ?ContainerInterface continue; } if (!isset($expected[$id])) { - throw new \UnexpectedValueException(sprintf('Service "%s" is not expected, as declared by "%s::getSubscribedServices()".', $id, AbstractController::class)); + throw new \UnexpectedValueException(\sprintf('Service "%s" is not expected, as declared by "%s::getSubscribedServices()".', $id, AbstractController::class)); } $type = substr($expected[$id], 1); if (!$container->get($id) instanceof $type) { - throw new \UnexpectedValueException(sprintf('Service "%s" is expected to be an instance of "%s", as declared by "%s::getSubscribedServices()".', $id, $type, AbstractController::class)); + throw new \UnexpectedValueException(\sprintf('Service "%s" is expected to be an instance of "%s", as declared by "%s::getSubscribedServices()".', $id, $type, AbstractController::class)); } } diff --git a/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php b/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php index d9785f1dc..b6021fbdd 100644 --- a/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php +++ b/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php @@ -29,7 +29,7 @@ public function testProcess() $pass->process($container); - $this->assertSame([sprintf('%s: Tag "kenrel.event_subscriber" was defined on service(s) "foo", "bar", but was never used. Did you mean "kernel.event_subscriber"?', UnusedTagsPass::class)], $container->getCompiler()->getLog()); + $this->assertSame([\sprintf('%s: Tag "kenrel.event_subscriber" was defined on service(s) "foo", "bar", but was never used. Did you mean "kernel.event_subscriber"?', UnusedTagsPass::class)], $container->getCompiler()->getLog()); } public function testMissingKnownTags() diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index cb97f2a47..cc88fb154 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -1766,24 +1766,24 @@ public function testRedisTagAwareAdapter() 'cacheRedisTagAwareBaz2', ]; foreach ($argNames as $argumentName) { - $aliasesForArguments[] = sprintf('%s $%s', TagAwareCacheInterface::class, $argumentName); - $aliasesForArguments[] = sprintf('%s $%s', CacheInterface::class, $argumentName); - $aliasesForArguments[] = sprintf('%s $%s', CacheItemPoolInterface::class, $argumentName); + $aliasesForArguments[] = \sprintf('%s $%s', TagAwareCacheInterface::class, $argumentName); + $aliasesForArguments[] = \sprintf('%s $%s', CacheInterface::class, $argumentName); + $aliasesForArguments[] = \sprintf('%s $%s', CacheItemPoolInterface::class, $argumentName); } foreach ($aliasesForArguments as $aliasForArgumentStr) { $aliasForArgument = $container->getAlias($aliasForArgumentStr); - $this->assertNotNull($aliasForArgument, sprintf("No alias found for '%s'", $aliasForArgumentStr)); + $this->assertNotNull($aliasForArgument, \sprintf("No alias found for '%s'", $aliasForArgumentStr)); $def = $container->getDefinition((string) $aliasForArgument); - $this->assertInstanceOf(ChildDefinition::class, $def, sprintf("No definition found for '%s'", $aliasForArgumentStr)); + $this->assertInstanceOf(ChildDefinition::class, $def, \sprintf("No definition found for '%s'", $aliasForArgumentStr)); $defParent = $container->getDefinition($def->getParent()); if ($defParent instanceof ChildDefinition) { $defParent = $container->getDefinition($defParent->getParent()); } - $this->assertSame(RedisTagAwareAdapter::class, $defParent->getClass(), sprintf("'%s' is not %s", $aliasForArgumentStr, RedisTagAwareAdapter::class)); + $this->assertSame(RedisTagAwareAdapter::class, $defParent->getClass(), \sprintf("'%s' is not %s", $aliasForArgumentStr, RedisTagAwareAdapter::class)); } } @@ -2191,7 +2191,7 @@ public function testIfNotifierTransportsAreKnownByFrameworkExtension() foreach ((new Finder())->in(\dirname(__DIR__, 4).'/Component/Notifier/Bridge')->directories()->depth(0)->exclude('Mercure') as $bridgeDirectory) { $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 "%s" TransportFactory to the $classToServices array in FrameworkExtension?', $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())); } } @@ -2464,14 +2464,14 @@ private function assertVersionStrategy(ContainerBuilder $container, Reference $r private function assertCachePoolServiceDefinitionIsCreated(ContainerBuilder $container, $id, $adapter, $defaultLifetime) { - $this->assertTrue($container->has($id), sprintf('Service definition "%s" for cache pool of type "%s" is registered', $id, $adapter)); + $this->assertTrue($container->has($id), \sprintf('Service definition "%s" for cache pool of type "%s" is registered', $id, $adapter)); $poolDefinition = $container->getDefinition($id); - $this->assertInstanceOf(ChildDefinition::class, $poolDefinition, sprintf('Cache pool "%s" is based on an abstract cache pool.', $id)); + $this->assertInstanceOf(ChildDefinition::class, $poolDefinition, \sprintf('Cache pool "%s" is based on an abstract cache pool.', $id)); - $this->assertTrue($poolDefinition->hasTag('cache.pool'), sprintf('Service definition "%s" is tagged with the "cache.pool" tag.', $id)); - $this->assertFalse($poolDefinition->isAbstract(), sprintf('Service definition "%s" is not abstract.', $id)); + $this->assertTrue($poolDefinition->hasTag('cache.pool'), \sprintf('Service definition "%s" is tagged with the "cache.pool" tag.', $id)); + $this->assertFalse($poolDefinition->isAbstract(), \sprintf('Service definition "%s" is not abstract.', $id)); $tag = $poolDefinition->getTag('cache.pool'); $this->assertArrayHasKey('default_lifetime', $tag[0], 'The default lifetime is stored as an attribute of the "cache.pool" tag.'); diff --git a/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php b/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php index b0d303128..989684bee 100644 --- a/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php +++ b/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php @@ -35,13 +35,13 @@ public function welcomeAction(Request $request, $name = null) // remember name $session->set('name', $name); - return new Response(sprintf('Hello %s, nice to meet you.', $name)); + return new Response(\sprintf('Hello %s, nice to meet you.', $name)); } // existing session $name = $session->get('name'); - return new Response(sprintf('Welcome back %s, nice to meet you.', $name)); + return new Response(\sprintf('Welcome back %s, nice to meet you.', $name)); } public function cacheableAction() diff --git a/Tests/Functional/CacheAttributeListenerTest.php b/Tests/Functional/CacheAttributeListenerTest.php index 72b2c1226..e6eb93eba 100644 --- a/Tests/Functional/CacheAttributeListenerTest.php +++ b/Tests/Functional/CacheAttributeListenerTest.php @@ -25,7 +25,7 @@ public function testAnonimousUserWithEtag() { $client = self::createClient(['test_case' => 'CacheAttributeListener']); - $client->request('GET', '/', server: ['HTTP_IF_NONE_MATCH' => sprintf('"%s"', hash('sha256', '12345'))]); + $client->request('GET', '/', server: ['HTTP_IF_NONE_MATCH' => \sprintf('"%s"', hash('sha256', '12345'))]); self::assertTrue($client->getResponse()->isRedirect('http://localhost/login')); } @@ -44,7 +44,7 @@ public function testLoggedInUserWithEtag() $client = self::createClient(['test_case' => 'CacheAttributeListener']); $client->loginUser(new InMemoryUser('the-username', 'the-password', ['ROLE_USER'])); - $client->request('GET', '/', server: ['HTTP_IF_NONE_MATCH' => sprintf('"%s"', hash('sha256', '12345'))]); + $client->request('GET', '/', server: ['HTTP_IF_NONE_MATCH' => \sprintf('"%s"', hash('sha256', '12345'))]); $response = $client->getResponse(); diff --git a/Tests/Functional/ConfigDebugCommandTest.php b/Tests/Functional/ConfigDebugCommandTest.php index c9bfba234..bd1539636 100644 --- a/Tests/Functional/ConfigDebugCommandTest.php +++ b/Tests/Functional/ConfigDebugCommandTest.php @@ -155,7 +155,7 @@ public function testDefaultParameterValueIsResolvedIfConfigIsExisting(bool $debu $this->assertSame(0, $ret, 'Returns 0 in case of success'); $kernelCacheDir = self::$kernel->getContainer()->getParameter('kernel.cache_dir'); - $this->assertStringContainsString(sprintf("dsn: 'file:%s/profiler'", $kernelCacheDir), $tester->getDisplay()); + $this->assertStringContainsString(\sprintf("dsn: 'file:%s/profiler'", $kernelCacheDir), $tester->getDisplay()); } /** diff --git a/Tests/Functional/ContainerDebugCommandTest.php b/Tests/Functional/ContainerDebugCommandTest.php index efbc1f54a..bb80a4484 100644 --- a/Tests/Functional/ContainerDebugCommandTest.php +++ b/Tests/Functional/ContainerDebugCommandTest.php @@ -205,7 +205,7 @@ public function testDescribeEnvVar() public function testGetDeprecation() { static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => true]); - $path = sprintf('%s/%sDeprecations.log', static::$kernel->getContainer()->getParameter('kernel.build_dir'), static::$kernel->getContainer()->getParameter('kernel.container_class')); + $path = \sprintf('%s/%sDeprecations.log', static::$kernel->getContainer()->getParameter('kernel.build_dir'), static::$kernel->getContainer()->getParameter('kernel.container_class')); touch($path); file_put_contents($path, serialize([[ 'type' => 16384, @@ -235,7 +235,7 @@ public function testGetDeprecation() public function testGetDeprecationNone() { static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => true]); - $path = sprintf('%s/%sDeprecations.log', static::$kernel->getContainer()->getParameter('kernel.build_dir'), static::$kernel->getContainer()->getParameter('kernel.container_class')); + $path = \sprintf('%s/%sDeprecations.log', static::$kernel->getContainer()->getParameter('kernel.build_dir'), static::$kernel->getContainer()->getParameter('kernel.container_class')); touch($path); file_put_contents($path, serialize([])); @@ -254,7 +254,7 @@ public function testGetDeprecationNone() public function testGetDeprecationNoFile() { static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => true]); - $path = sprintf('%s/%sDeprecations.log', static::$kernel->getContainer()->getParameter('kernel.build_dir'), static::$kernel->getContainer()->getParameter('kernel.container_class')); + $path = \sprintf('%s/%sDeprecations.log', static::$kernel->getContainer()->getParameter('kernel.build_dir'), static::$kernel->getContainer()->getParameter('kernel.container_class')); @unlink($path); $application = new Application(static::$kernel); diff --git a/Tests/Functional/app/AppKernel.php b/Tests/Functional/app/AppKernel.php index 06dc2d637..d4d9c99bc 100644 --- a/Tests/Functional/app/AppKernel.php +++ b/Tests/Functional/app/AppKernel.php @@ -35,14 +35,14 @@ class AppKernel extends Kernel implements ExtensionInterface, ConfigurationInter public function __construct($varDir, $testCase, $rootConfig, $environment, $debug) { if (!is_dir(__DIR__.'/'.$testCase)) { - throw new \InvalidArgumentException(sprintf('The test case "%s" does not exist.', $testCase)); + throw new \InvalidArgumentException(\sprintf('The test case "%s" does not exist.', $testCase)); } $this->varDir = $varDir; $this->testCase = $testCase; $fs = new Filesystem(); if (!$fs->isAbsolutePath($rootConfig) && !file_exists($rootConfig = __DIR__.'/'.$testCase.'/'.$rootConfig)) { - throw new \InvalidArgumentException(sprintf('The root config "%s" does not exist.', $rootConfig)); + throw new \InvalidArgumentException(\sprintf('The root config "%s" does not exist.', $rootConfig)); } $this->rootConfig = $rootConfig; @@ -52,7 +52,7 @@ public function __construct($varDir, $testCase, $rootConfig, $environment, $debu public function registerBundles(): iterable { if (!file_exists($filename = $this->getProjectDir().'/'.$this->testCase.'/bundles.php')) { - throw new \RuntimeException(sprintf('The bundles file "%s" does not exist.', $filename)); + throw new \RuntimeException(\sprintf('The bundles file "%s" does not exist.', $filename)); } return include $filename; diff --git a/Translation/Translator.php b/Translation/Translator.php index 870d69ae1..84aa0c7fd 100644 --- a/Translation/Translator.php +++ b/Translation/Translator.php @@ -81,7 +81,7 @@ public function __construct( ) { // check option names if ($diff = array_diff(array_keys($options), array_keys($this->options))) { - throw new InvalidArgumentException(sprintf('The Translator does not support the following options: \'%s\'.', implode('\', \'', $diff))); + throw new InvalidArgumentException(\sprintf('The Translator does not support the following options: \'%s\'.', implode('\', \'', $diff))); } $this->options = array_merge($this->options, $options); From 4736b77df25094bfcf7e116c92a91c05c61a3de2 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sun, 16 Jun 2024 17:17:26 +0200 Subject: [PATCH 090/281] chore: CS fixes --- Tests/DependencyInjection/ConfigurationTest.php | 14 +++++++------- .../Routing/RedirectableCompiledUrlMatcherTest.php | 6 ++++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index b32d8681b..6f2a51993 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -224,13 +224,13 @@ public function testInvalidAssetsConfiguration(array $assetConfig, $expectedMess $this->expectExceptionMessage($expectedMessage); $processor->processConfiguration($configuration, [ - [ - 'http_method_override' => false, - 'handle_all_throwables' => true, - 'php_errors' => ['log' => true], - 'assets' => $assetConfig, - ], - ]); + [ + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'assets' => $assetConfig, + ], + ]); } public static function provideInvalidAssetConfigurationTests(): iterable diff --git a/Tests/Routing/RedirectableCompiledUrlMatcherTest.php b/Tests/Routing/RedirectableCompiledUrlMatcherTest.php index 29126e130..9a96313d0 100644 --- a/Tests/Routing/RedirectableCompiledUrlMatcherTest.php +++ b/Tests/Routing/RedirectableCompiledUrlMatcherTest.php @@ -27,7 +27,8 @@ public function testRedirectWhenNoSlash() $matcher = $this->getMatcher($routes, $context = new RequestContext()); - $this->assertEquals([ + $this->assertEquals( + [ '_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', 'path' => '/foo/', 'permanent' => true, @@ -47,7 +48,8 @@ public function testSchemeRedirect() $matcher = $this->getMatcher($routes, $context = new RequestContext()); - $this->assertEquals([ + $this->assertEquals( + [ '_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', 'path' => '/foo', 'permanent' => true, From 900b8add71b63351d9d87907e3b3b7ffcabb5adc Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 25 Jun 2024 08:08:32 +0200 Subject: [PATCH 091/281] Unify how --format is handle by commands --- Command/ConfigDebugCommand.php | 7 ++----- Command/ConfigDumpReferenceCommand.php | 9 +++------ Command/ContainerDebugCommand.php | 4 ++++ Command/EventDispatcherDebugCommand.php | 5 +++++ Command/RouterDebugCommand.php | 4 ++++ 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Command/ConfigDebugCommand.php b/Command/ConfigDebugCommand.php index fb79b39bf..55c101e9c 100644 --- a/Command/ConfigDebugCommand.php +++ b/Command/ConfigDebugCommand.php @@ -41,9 +41,6 @@ class ConfigDebugCommand extends AbstractConfigCommand { protected function configure(): void { - $commentedHelpFormats = array_map(fn ($format) => \sprintf('%s', $format), $this->getAvailableFormatOptions()); - $helpFormats = implode('", "', $commentedHelpFormats); - $this ->setDefinition([ new InputArgument('name', InputArgument::OPTIONAL, 'The bundle name or the extension alias'), @@ -60,8 +57,7 @@ protected function configure(): void php %command.full_name% framework php %command.full_name% FrameworkBundle -The --format option specifies the format of the configuration, -these are "{$helpFormats}". +The --format option specifies the format of the command output: php %command.full_name% framework --format=json @@ -268,6 +264,7 @@ private static function buildPathsCompletion(array $paths, string $prefix = ''): return $completionPaths; } + /** @return string[] */ private function getAvailableFormatOptions(): array { return ['txt', 'yaml', 'json']; diff --git a/Command/ConfigDumpReferenceCommand.php b/Command/ConfigDumpReferenceCommand.php index 27dc01b11..7e5cd765f 100644 --- a/Command/ConfigDumpReferenceCommand.php +++ b/Command/ConfigDumpReferenceCommand.php @@ -39,9 +39,6 @@ class ConfigDumpReferenceCommand extends AbstractConfigCommand { protected function configure(): void { - $commentedHelpFormats = array_map(fn ($format) => \sprintf('%s', $format), $this->getAvailableFormatOptions()); - $helpFormats = implode('", "', $commentedHelpFormats); - $this ->setDefinition([ new InputArgument('name', InputArgument::OPTIONAL, 'The Bundle name or the extension alias'), @@ -57,10 +54,9 @@ protected function configure(): void php %command.full_name% framework php %command.full_name% FrameworkBundle -The --format option specifies the format of the configuration, -these are "{$helpFormats}". +The --format option specifies the format of the command output: - php %command.full_name% FrameworkBundle --format=xml + php %command.full_name% FrameworkBundle --format=json For dumping a specific option, add its path as second argument (only available for the yaml format): @@ -181,6 +177,7 @@ private function getAvailableBundles(): array return $bundles; } + /** @return string[] */ private function getAvailableFormatOptions(): array { return ['yaml', 'xml']; diff --git a/Command/ContainerDebugCommand.php b/Command/ContainerDebugCommand.php index 9619d1972..46cdca9ab 100644 --- a/Command/ContainerDebugCommand.php +++ b/Command/ContainerDebugCommand.php @@ -106,6 +106,9 @@ protected function configure(): void php %command.full_name% --show-hidden +The --format option specifies the format of the command output: + + php %command.full_name% --format=json EOF ) ; @@ -358,6 +361,7 @@ public function filterToServiceTypes(string $serviceId): bool return class_exists($serviceId) || interface_exists($serviceId, false); } + /** @return string[] */ private function getAvailableFormatOptions(): array { return (new DescriptorHelper())->getFormats(); diff --git a/Command/EventDispatcherDebugCommand.php b/Command/EventDispatcherDebugCommand.php index a3531d800..3c51cb1b7 100644 --- a/Command/EventDispatcherDebugCommand.php +++ b/Command/EventDispatcherDebugCommand.php @@ -60,6 +60,10 @@ protected function configure(): void To get specific listeners for an event, specify its name: php %command.full_name% kernel.request + +The --format option specifies the format of the command output: + + php %command.full_name% --format=json EOF ) ; @@ -153,6 +157,7 @@ private function searchForEvent(EventDispatcherInterface $dispatcher, string $ne return $output; } + /** @return string[] */ private function getAvailableFormatOptions(): array { return (new DescriptorHelper())->getFormats(); diff --git a/Command/RouterDebugCommand.php b/Command/RouterDebugCommand.php index d57c0623f..13a6f75d0 100644 --- a/Command/RouterDebugCommand.php +++ b/Command/RouterDebugCommand.php @@ -61,6 +61,9 @@ protected function configure(): void php %command.full_name% +The --format option specifies the format of the command output: + + php %command.full_name% --format=json EOF ) ; @@ -164,6 +167,7 @@ private function findRouteContaining(string $name, RouteCollection $routes): Rou return $foundRoutes; } + /** @return string[] */ private function getAvailableFormatOptions(): array { return (new DescriptorHelper())->getFormats(); From ab5d785db2e90f3508eb58957aebe756c8bef410 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Thu, 20 Jun 2024 08:01:34 -0400 Subject: [PATCH 092/281] Lazy kernel.secret parameter resolving --- Resources/config/services.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Resources/config/services.php b/Resources/config/services.php index 895d2ce34..4095af666 100644 --- a/Resources/config/services.php +++ b/Resources/config/services.php @@ -23,6 +23,7 @@ use Symfony\Component\Console\ConsoleEvents; use Symfony\Component\DependencyInjection\Config\ContainerParametersResourceChecker; use Symfony\Component\DependencyInjection\EnvVarProcessor; +use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\ParameterBag\ContainerBag; use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; @@ -154,7 +155,7 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : [] ->set('uri_signer', UriSigner::class) ->args([ - param('kernel.secret'), + new Parameter('kernel.secret') ]) ->alias(UriSigner::class, 'uri_signer') From 35d085dc18b494d20b75728ecb222543e760c515 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 27 Jun 2024 14:04:56 +0200 Subject: [PATCH 093/281] [FrameworkBundle] Fix warming up routes --- Routing/Router.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Routing/Router.php b/Routing/Router.php index 4dfb71e74..69428a1b7 100644 --- a/Routing/Router.php +++ b/Routing/Router.php @@ -69,7 +69,7 @@ public function getRouteCollection(): RouteCollection $this->collection->addResource(new ContainerParametersResource($this->collectedParameters)); try { - $containerFile = ($this->paramFetcher)('kernel.cache_dir').'/'.($this->paramFetcher)('kernel.container_class').'.php'; + $containerFile = ($this->paramFetcher)('kernel.build_dir').'/'.($this->paramFetcher)('kernel.container_class').'.php'; if (file_exists($containerFile)) { $this->collection->addResource(new FileResource($containerFile)); } else { @@ -84,14 +84,12 @@ public function getRouteCollection(): RouteCollection public function warmUp(string $cacheDir, ?string $buildDir = null): array { - if (!$buildDir) { - return []; + if (null === $currentDir = $this->getOption('cache_dir')) { + return []; // skip warmUp when router doesn't use cache } - $currentDir = $this->getOption('cache_dir'); - - // force cache generation in build_dir - $this->setOption('cache_dir', $buildDir); + // force cache generation + $this->setOption('cache_dir', $buildDir ?? $cacheDir); $this->getMatcher(); $this->getGenerator(); From 8e79d91740601e27db4d9ab78d40bd697c20379f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 1 Jul 2024 02:16:34 +0200 Subject: [PATCH 094/281] Remove useless uniqid in tempnam calls --- Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php | 3 ++- Tests/Command/TranslationDebugCommandTest.php | 6 ++++-- .../TranslationUpdateCommandCompletionTest.php | 3 ++- Tests/Command/TranslationUpdateCommandTest.php | 13 ++----------- Tests/Functional/BundlePathsTest.php | 3 ++- Tests/Routing/RouterTest.php | 4 +++- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php b/Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php index 294a3cd25..cf5f88c15 100644 --- a/Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php +++ b/Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php @@ -37,8 +37,9 @@ class ConfigBuilderCacheWarmerTest extends TestCase protected function setUp(): void { - $this->varDir = sys_get_temp_dir().'/'.uniqid(); $fs = new Filesystem(); + $this->varDir = tempnam(sys_get_temp_dir(), 'sf_var_'); + $fs->remove($this->varDir); $fs->mkdir($this->varDir); } diff --git a/Tests/Command/TranslationDebugCommandTest.php b/Tests/Command/TranslationDebugCommandTest.php index dcff845a3..c6c91a857 100644 --- a/Tests/Command/TranslationDebugCommandTest.php +++ b/Tests/Command/TranslationDebugCommandTest.php @@ -82,7 +82,8 @@ public function testDebugDefaultRootDirectory() { $this->fs->remove($this->translationDir); $this->fs = new Filesystem(); - $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf_translation', true); + $this->translationDir = tempnam(sys_get_temp_dir(), 'sf_translation_'); + $this->fs->remove($this->translationDir); $this->fs->mkdir($this->translationDir.'/translations'); $this->fs->mkdir($this->translationDir.'/templates'); @@ -150,7 +151,8 @@ public function testNoErrorWithOnlyUnusedOptionAndNoResults() protected function setUp(): void { $this->fs = new Filesystem(); - $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf_translation', true); + $this->translationDir = tempnam(sys_get_temp_dir(), 'sf_translation_'); + $this->fs->remove($this->translationDir); $this->fs->mkdir($this->translationDir.'/translations'); $this->fs->mkdir($this->translationDir.'/templates'); } diff --git a/Tests/Command/TranslationUpdateCommandCompletionTest.php b/Tests/Command/TranslationUpdateCommandCompletionTest.php index 1b11a6111..4627508cb 100644 --- a/Tests/Command/TranslationUpdateCommandCompletionTest.php +++ b/Tests/Command/TranslationUpdateCommandCompletionTest.php @@ -57,7 +57,8 @@ public static function provideCompletionSuggestions(): iterable protected function setUp(): void { $this->fs = new Filesystem(); - $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf_translation', true); + $this->translationDir = tempnam(sys_get_temp_dir(), 'sf_translation_'); + $this->fs->remove($this->translationDir); $this->fs->mkdir($this->translationDir.'/translations'); $this->fs->mkdir($this->translationDir.'/templates'); } diff --git a/Tests/Command/TranslationUpdateCommandTest.php b/Tests/Command/TranslationUpdateCommandTest.php index d9f142e8f..def589f58 100644 --- a/Tests/Command/TranslationUpdateCommandTest.php +++ b/Tests/Command/TranslationUpdateCommandTest.php @@ -78,11 +78,6 @@ public function testDumpWrongSortAndClean() public function testDumpMessagesAndCleanInRootDirectory() { - $this->fs->remove($this->translationDir); - $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf_translation', true); - $this->fs->mkdir($this->translationDir.'/translations'); - $this->fs->mkdir($this->translationDir.'/templates'); - $tester = $this->createCommandTester(['messages' => ['foo' => 'foo']], [], null, [$this->translationDir.'/trans'], [$this->translationDir.'/views']); $tester->execute(['command' => 'translation:extract', 'locale' => 'en', '--dump-messages' => true, '--clean' => true]); $this->assertMatchesRegularExpression('/foo/', $tester->getDisplay()); @@ -115,11 +110,6 @@ public function testWriteMessages() public function testWriteMessagesInRootDirectory() { - $this->fs->remove($this->translationDir); - $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf_translation', true); - $this->fs->mkdir($this->translationDir.'/translations'); - $this->fs->mkdir($this->translationDir.'/templates'); - $tester = $this->createCommandTester(['messages' => ['foo' => 'foo']]); $tester->execute(['command' => 'translation:extract', 'locale' => 'en', '--force' => true]); $this->assertMatchesRegularExpression('/Translation files were successfully updated./', $tester->getDisplay()); @@ -174,7 +164,8 @@ public function testFilterDuplicateTransPaths() protected function setUp(): void { $this->fs = new Filesystem(); - $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf_translation', true); + $this->translationDir = tempnam(sys_get_temp_dir(), 'sf_translation_'); + $this->fs->remove($this->translationDir); $this->fs->mkdir($this->translationDir.'/translations'); $this->fs->mkdir($this->translationDir.'/templates'); } diff --git a/Tests/Functional/BundlePathsTest.php b/Tests/Functional/BundlePathsTest.php index a079837c9..a06803434 100644 --- a/Tests/Functional/BundlePathsTest.php +++ b/Tests/Functional/BundlePathsTest.php @@ -23,9 +23,10 @@ class BundlePathsTest extends AbstractWebTestCase public function testBundlePublicDir() { $kernel = static::bootKernel(['test_case' => 'BundlePaths']); - $projectDir = sys_get_temp_dir().'/'.uniqid('sf_bundle_paths', true); + $projectDir = tempnam(sys_get_temp_dir(), 'sf_bundle_paths_'); $fs = new Filesystem(); + $fs->remove($projectDir); $fs->mkdir($projectDir.'/public'); $command = (new Application($kernel))->add(new AssetsInstallCommand($fs, $projectDir)); $exitCode = (new CommandTester($command))->execute(['target' => $projectDir.'/public']); diff --git a/Tests/Routing/RouterTest.php b/Tests/Routing/RouterTest.php index 11c0dc7e6..d2c021563 100644 --- a/Tests/Routing/RouterTest.php +++ b/Tests/Routing/RouterTest.php @@ -530,7 +530,9 @@ public static function getNonStringValues() */ public function testCacheValidityWithContainerParameters($parameter) { - $cacheDir = sys_get_temp_dir().\DIRECTORY_SEPARATOR.uniqid('router_', true); + $cacheDir = tempnam(sys_get_temp_dir(), 'sf_router_'); + unlink($cacheDir); + mkdir($cacheDir); try { $container = new Container(); From 213321d07b71574142ec950dacf84867f131d458 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Mon, 1 Jul 2024 16:00:19 +0200 Subject: [PATCH 095/281] [TypeInfo] Add PhpDocAwareReflectionTypeResolver --- DependencyInjection/FrameworkExtension.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 7cc67725e..04d45f59f 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -14,6 +14,7 @@ use Composer\InstalledVersions; use Http\Client\HttpAsyncClient; use Http\Client\HttpClient; +use Symfony\Component\TypeInfo\TypeResolver\PhpDocAwareReflectionTypeResolver; use phpDocumentor\Reflection\DocBlockFactoryInterface; use phpDocumentor\Reflection\Types\ContextFactory; use PhpParser\Parser; @@ -1974,11 +1975,21 @@ private function registerTypeInfoConfiguration(ContainerBuilder $container, PhpF if (ContainerBuilder::willBeAvailable('phpstan/phpdoc-parser', PhpDocParser::class, ['symfony/framework-bundle', 'symfony/type-info'])) { $container->register('type_info.resolver.string', StringTypeResolver::class); + $container->register('type_info.resolver.reflection_parameter.phpdoc_aware', PhpDocAwareReflectionTypeResolver::class) + ->setArguments([new Reference('type_info.resolver.reflection_parameter'), new Reference('type_info.resolver.string'), new Reference('type_info.type_context_factory')]); + $container->register('type_info.resolver.reflection_property.phpdoc_aware', PhpDocAwareReflectionTypeResolver::class) + ->setArguments([new Reference('type_info.resolver.reflection_property'), new Reference('type_info.resolver.string'), new Reference('type_info.type_context_factory')]); + $container->register('type_info.resolver.reflection_return.phpdoc_aware', PhpDocAwareReflectionTypeResolver::class) + ->setArguments([new Reference('type_info.resolver.reflection_return'), new Reference('type_info.resolver.string'), new Reference('type_info.type_context_factory')]); + /** @var ServiceLocatorArgument $resolversLocator */ $resolversLocator = $container->getDefinition('type_info.resolver')->getArgument(0); - $resolversLocator->setValues($resolversLocator->getValues() + [ + $resolversLocator->setValues([ 'string' => new Reference('type_info.resolver.string'), - ]); + \ReflectionParameter::class => new Reference('type_info.resolver.reflection_parameter.phpdoc_aware'), + \ReflectionProperty::class => new Reference('type_info.resolver.reflection_property.phpdoc_aware'), + \ReflectionFunctionAbstract::class => new Reference('type_info.resolver.reflection_return.phpdoc_aware'), + ] + $resolversLocator->getValues()); } } From eeac11247208e54653d91a5f426038fe71e0e612 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 5 Jul 2024 08:25:30 +0200 Subject: [PATCH 096/281] [Notifier] Remove the Gitter bridge --- DependencyInjection/FrameworkExtension.php | 1 - Resources/config/notifier_transports.php | 1 - 2 files changed, 2 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 7cc67725e..7e5f88fde 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2779,7 +2779,6 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\FortySixElks\FortySixElksTransportFactory::class => 'notifier.transport_factory.forty-six-elks', NotifierBridge\FreeMobile\FreeMobileTransportFactory::class => 'notifier.transport_factory.free-mobile', NotifierBridge\GatewayApi\GatewayApiTransportFactory::class => 'notifier.transport_factory.gateway-api', - NotifierBridge\Gitter\GitterTransportFactory::class => 'notifier.transport_factory.gitter', NotifierBridge\GoIp\GoIpTransportFactory::class => 'notifier.transport_factory.go-ip', NotifierBridge\GoogleChat\GoogleChatTransportFactory::class => 'notifier.transport_factory.google-chat', NotifierBridge\Infobip\InfobipTransportFactory::class => 'notifier.transport_factory.infobip', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index 4acfcf783..e34781e9c 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -31,7 +31,6 @@ 'discord' => Bridge\Discord\DiscordTransportFactory::class, 'fake-chat' => Bridge\FakeChat\FakeChatTransportFactory::class, 'firebase' => Bridge\Firebase\FirebaseTransportFactory::class, - 'gitter' => Bridge\Gitter\GitterTransportFactory::class, 'google-chat' => Bridge\GoogleChat\GoogleChatTransportFactory::class, 'line-notify' => Bridge\LineNotify\LineNotifyTransportFactory::class, 'linked-in' => Bridge\LinkedIn\LinkedInTransportFactory::class, From e81f2c33de7786829621d7d10a5fa24dee8d5dbe Mon Sep 17 00:00:00 2001 From: Piotr Zajac Date: Tue, 28 May 2024 13:26:56 +0200 Subject: [PATCH 097/281] validate empty request MapQueryString/MapRequestPayload skips validation when empty request is sent resolves #54617 --- Tests/Functional/ApiAttributesTest.php | 846 ++++++++++++++++-- .../app/ApiAttributesTest/routing.yml | 28 +- 2 files changed, 799 insertions(+), 75 deletions(-) diff --git a/Tests/Functional/ApiAttributesTest.php b/Tests/Functional/ApiAttributesTest.php index 96b6d0ee9..a64da93b3 100644 --- a/Tests/Functional/ApiAttributesTest.php +++ b/Tests/Functional/ApiAttributesTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; +use Composer\InstalledVersions; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -23,13 +24,14 @@ class ApiAttributesTest extends AbstractWebTestCase /** * @dataProvider mapQueryStringProvider */ - public function testMapQueryString(array $query, string $expectedResponse, int $expectedStatusCode) + public function testMapQueryString(string $uri, array $query, string $expectedResponse, int $expectedStatusCode) { $client = self::createClient(['test_case' => 'ApiAttributesTest']); - $client->request('GET', '/map-query-string.json', $query); + $client->request('GET', $uri, $query); $response = $client->getResponse(); + if ($expectedResponse) { self::assertJsonStringEqualsJsonString($expectedResponse, $response->getContent()); } else { @@ -40,13 +42,70 @@ public function testMapQueryString(array $query, string $expectedResponse, int $ public static function mapQueryStringProvider(): iterable { - yield 'empty' => [ + yield 'empty query string mapping nullable attribute' => [ + 'uri' => '/map-query-string-to-nullable-attribute.json', 'query' => [], 'expectedResponse' => '', 'expectedStatusCode' => 204, ]; - yield 'valid' => [ + yield 'valid query string mapping nullable attribute' => [ + 'uri' => '/map-query-string-to-nullable-attribute.json', + 'query' => ['filter' => ['status' => 'approved', 'quantity' => '4']], + 'expectedResponse' => <<<'JSON' + { + "filter": { + "status": "approved", + "quantity": 4 + } + } + JSON, + 'expectedStatusCode' => 200, + ]; + + yield 'invalid query string mapping nullable attribute' => [ + 'uri' => '/map-query-string-to-nullable-attribute.json', + 'query' => ['filter' => ['status' => 'approved', 'quantity' => '200']], + 'expectedResponse' => <<<'JSON' + { + "type": "https:\/\/symfony.com\/errors\/validation", + "title": "Validation Failed", + "status": 404, + "detail": "filter.quantity: This value should be less than 10.", + "violations": [ + { + "propertyPath": "filter.quantity", + "title": "This value should be less than 10.", + "template": "This value should be less than {{ compared_value }}.", + "parameters": { + "{{ value }}": "200", + "{{ compared_value }}": "10", + "{{ compared_value_type }}": "int" + }, + "type": "urn:uuid:079d7420-2d13-460c-8756-de810eeb37d2" + } + ] + } + JSON, + 'expectedStatusCode' => 404, + ]; + + yield 'empty query string mapping attribute with default value' => [ + 'uri' => '/map-query-string-to-attribute-with-default-value.json', + 'query' => [], + 'expectedResponse' => <<<'JSON' + { + "filter": { + "status": "approved", + "quantity": 5 + } + } + JSON, + 'expectedStatusCode' => 200, + ]; + + yield 'valid query string mapping attribute with default value' => [ + 'uri' => '/map-query-string-to-attribute-with-default-value.json', 'query' => ['filter' => ['status' => 'approved', 'quantity' => '4']], 'expectedResponse' => <<<'JSON' { @@ -59,7 +118,8 @@ public static function mapQueryStringProvider(): iterable 'expectedStatusCode' => 200, ]; - yield 'invalid' => [ + yield 'invalid query string mapping attribute with default value' => [ + 'uri' => '/map-query-string-to-attribute-with-default-value.json', 'query' => ['filter' => ['status' => 'approved', 'quantity' => '200']], 'expectedResponse' => <<<'JSON' { @@ -84,12 +144,92 @@ public static function mapQueryStringProvider(): iterable JSON, 'expectedStatusCode' => 404, ]; + + $expectedResponse = <<<'JSON' + { + "type": "https:\/\/symfony.com\/errors\/validation", + "title": "Validation Failed", + "status": 404, + "detail": "filter: This value should be of type Symfony\\Bundle\\FrameworkBundle\\Tests\\Functional\\Filter.", + "violations": [ + { + "parameters": { + "hint": "Failed to create object because the class misses the \"filter\" property.", + "{{ type }}": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Functional\\Filter" + }, + "propertyPath": "filter", + "template": "This value should be of type {{ type }}.", + "title": "This value should be of type Symfony\\Bundle\\FrameworkBundle\\Tests\\Functional\\Filter." + } + ] + } + JSON; + + $httpKernelVersion = InstalledVersions::getVersion('symfony/http-kernel'); + if ($httpKernelVersion && version_compare($httpKernelVersion, '7.2.0', '<')) { + $expectedResponse = <<<'JSON' + { + "type": "https:\/\/tools.ietf.org\/html\/rfc2616#section-10", + "title": "An error occurred", + "status": 404, + "detail": "Not Found" + } + JSON; + } + + yield 'empty query string mapping non-nullable attribute without default value' => [ + 'uri' => '/map-query-string-to-non-nullable-attribute-without-default-value.json', + 'query' => [], + 'expectedResponse' => $expectedResponse, + 'expectedStatusCode' => 404, + ]; + + yield 'valid query string mapping non-nullable attribute without default value' => [ + 'uri' => '/map-query-string-to-non-nullable-attribute-without-default-value.json', + 'query' => ['filter' => ['status' => 'approved', 'quantity' => '4']], + 'expectedResponse' => <<<'JSON' + { + "filter": { + "status": "approved", + "quantity": 4 + } + } + JSON, + 'expectedStatusCode' => 200, + ]; + + yield 'invalid query string mapping non-nullable attribute without default value' => [ + 'uri' => '/map-query-string-to-non-nullable-attribute-without-default-value.json', + 'query' => ['filter' => ['status' => 'approved', 'quantity' => '11']], + 'expectedResponse' => <<<'JSON' + { + "type": "https:\/\/symfony.com\/errors\/validation", + "title": "Validation Failed", + "status": 404, + "detail": "filter.quantity: This value should be less than 10.", + "violations": [ + { + "propertyPath": "filter.quantity", + "title": "This value should be less than 10.", + "template": "This value should be less than {{ compared_value }}.", + "parameters": { + "{{ value }}": "11", + "{{ compared_value }}": "10", + "{{ compared_value_type }}": "int" + }, + "type": "urn:uuid:079d7420-2d13-460c-8756-de810eeb37d2" + } + ] + } + JSON, + 'expectedStatusCode' => 404, + ]; } /** * @dataProvider mapRequestPayloadProvider */ - public function testMapRequestPayload(string $format, array $parameters, ?string $content, string $expectedResponse, int $expectedStatusCode) + public function testMapRequestPayload(string $uri, string $format, array $parameters, ?string $content, string $expectedResponse, int $expectedStatusCode) { $client = self::createClient(['test_case' => 'ApiAttributesTest']); @@ -102,7 +242,7 @@ public function testMapRequestPayload(string $format, array $parameters, ?string $client->request( 'POST', - '/map-request-body.'.$format, + $uri, $parameters, [], ['HTTP_ACCEPT' => $acceptHeader, 'CONTENT_TYPE' => $acceptHeader], @@ -123,7 +263,8 @@ public function testMapRequestPayload(string $format, array $parameters, ?string public static function mapRequestPayloadProvider(): iterable { - yield 'empty' => [ + yield 'empty request mapping nullable attribute' => [ + 'uri' => '/map-request-to-nullable-attribute.json', 'format' => 'json', 'parameters' => [], 'content' => '', @@ -131,7 +272,8 @@ public static function mapRequestPayloadProvider(): iterable 'expectedStatusCode' => 204, ]; - yield 'valid json' => [ + yield 'valid request with json content mapping nullable attribute' => [ + 'uri' => '/map-request-to-nullable-attribute.json', 'format' => 'json', 'parameters' => [], 'content' => <<<'JSON' @@ -149,7 +291,41 @@ public static function mapRequestPayloadProvider(): iterable 'expectedStatusCode' => 200, ]; - yield 'malformed json' => [ + yield 'valid request with xml content mapping nullable attribute' => [ + 'uri' => '/map-request-to-nullable-attribute.xml', + 'format' => 'xml', + 'parameters' => [], + 'content' => <<<'XML' + + Hello everyone! + true + + XML, + 'expectedResponse' => <<<'XML' + + Hello everyone! + 1 + + XML, + 'expectedStatusCode' => 200, + ]; + + yield 'valid request mapping nullable attribute' => [ + 'uri' => '/map-request-to-nullable-attribute.json', + 'format' => 'json', + 'parameters' => ['comment' => 'Hello everyone!', 'approved' => '0'], + 'content' => null, + 'expectedResponse' => <<<'JSON' + { + "comment": "Hello everyone!", + "approved": false + } + JSON, + 'expectedStatusCode' => 200, + ]; + + yield 'malformed json request mapping nullable attribute' => [ + 'uri' => '/map-request-to-nullable-attribute.json', 'format' => 'json', 'parameters' => [], 'content' => <<<'JSON' @@ -169,7 +345,8 @@ public static function mapRequestPayloadProvider(): iterable 'expectedStatusCode' => 400, ]; - yield 'unsupported format' => [ + yield 'request with unsupported format mapping nullable attribute' => [ + 'uri' => '/map-request-to-nullable-attribute.dummy', 'format' => 'dummy', 'parameters' => [], 'content' => 'Hello', @@ -177,25 +354,8 @@ public static function mapRequestPayloadProvider(): iterable 'expectedStatusCode' => 415, ]; - yield 'valid xml' => [ - 'format' => 'xml', - 'parameters' => [], - 'content' => <<<'XML' - - Hello everyone! - true - - XML, - 'expectedResponse' => <<<'XML' - - Hello everyone! - 1 - - XML, - 'expectedStatusCode' => 200, - ]; - - yield 'invalid type' => [ + yield 'request with invalid type mapping nullable attribute' => [ + 'uri' => '/map-request-to-nullable-attribute.json', 'format' => 'json', 'parameters' => [], 'content' => <<<'JSON' @@ -225,7 +385,8 @@ public static function mapRequestPayloadProvider(): iterable 'expectedStatusCode' => 422, ]; - yield 'validation error json' => [ + yield 'invalid request with json content mapping nullable attribute' => [ + 'uri' => '/map-request-to-nullable-attribute.json', 'format' => 'json', 'parameters' => [], 'content' => <<<'JSON' @@ -267,7 +428,8 @@ public static function mapRequestPayloadProvider(): iterable 'expectedStatusCode' => 422, ]; - yield 'validation error xml' => [ + yield 'invalid request with xml content mapping nullable attribute' => [ + 'uri' => '/map-request-to-nullable-attribute.xml', 'format' => 'xml', 'parameters' => [], 'content' => <<<'XML' @@ -299,22 +461,10 @@ public static function mapRequestPayloadProvider(): iterable 'expectedStatusCode' => 422, ]; - yield 'valid input' => [ - 'format' => 'json', - 'input' => ['comment' => 'Hello everyone!', 'approved' => '0'], - 'content' => null, - 'expectedResponse' => <<<'JSON' - { - "comment": "Hello everyone!", - "approved": false - } - JSON, - 'expectedStatusCode' => 200, - ]; - - yield 'validation error input' => [ + yield 'invalid request mapping nullable attribute' => [ + 'uri' => '/map-request-to-nullable-attribute.json', 'format' => 'json', - 'input' => ['comment' => '', 'approved' => '1'], + 'parameters' => ['comment' => '', 'approved' => '1'], 'content' => null, 'expectedResponse' => <<<'JSON' { @@ -348,32 +498,590 @@ public static function mapRequestPayloadProvider(): iterable JSON, 'expectedStatusCode' => 422, ]; - } -} -class WithMapQueryStringController -{ - public function __invoke(#[MapQueryString] ?QueryString $query): Response - { - if (!$query) { - return new Response('', Response::HTTP_NO_CONTENT); - } + yield 'empty request mapping attribute with default value' => [ + 'uri' => '/map-request-to-attribute-with-default-value.json', + 'format' => 'json', + 'parameters' => [], + 'content' => '', + 'expectedResponse' => <<<'JSON' + { + "comment": "Hello everyone!", + "approved": false + } + JSON, + 'expectedStatusCode' => 200, + ]; - return new JsonResponse( - ['filter' => ['status' => $query->filter->status, 'quantity' => $query->filter->quantity]], - ); - } -} + yield 'valid request with json content mapping attribute with default value' => [ + 'uri' => '/map-request-to-attribute-with-default-value.json', + 'format' => 'json', + 'parameters' => [], + 'content' => <<<'JSON' + { + "comment": "Hello everyone!", + "approved": false + } + JSON, + 'expectedResponse' => <<<'JSON' + { + "comment": "Hello everyone!", + "approved": false + } + JSON, + 'expectedStatusCode' => 200, + ]; -class WithMapRequestPayloadController -{ - public function __invoke(#[MapRequestPayload] ?RequestBody $body, Request $request): Response - { - if ('json' === $request->getPreferredFormat('json')) { - if (!$body) { - return new Response('', Response::HTTP_NO_CONTENT); - } + yield 'valid request with xml content mapping attribute with default value' => [ + 'uri' => '/map-request-to-attribute-with-default-value.xml', + 'format' => 'xml', + 'parameters' => [], + 'content' => <<<'XML' + + Hello everyone! + true + + XML, + 'expectedResponse' => <<<'XML' + + Hello everyone! + 1 + + XML, + 'expectedStatusCode' => 200, + ]; + + yield 'valid request mapping attribute with default value' => [ + 'uri' => '/map-request-to-attribute-with-default-value.json', + 'format' => 'json', + 'parameters' => ['comment' => 'Hello everyone!', 'approved' => '0'], + 'content' => null, + 'expectedResponse' => <<<'JSON' + { + "comment": "Hello everyone!", + "approved": false + } + JSON, + 'expectedStatusCode' => 200, + ]; + yield 'malformed json request mapping attribute with default value' => [ + 'uri' => '/map-request-to-attribute-with-default-value.json', + 'format' => 'json', + 'parameters' => [], + 'content' => <<<'JSON' + { + "comment": "Hello everyone!", + "approved": false, + } + JSON, + 'expectedResponse' => <<<'JSON' + { + "type": "https:\/\/tools.ietf.org\/html\/rfc2616#section-10", + "title": "An error occurred", + "status": 400, + "detail": "Bad Request" + } + JSON, + 'expectedStatusCode' => 400, + ]; + + yield 'request with unsupported format mapping attribute with default value' => [ + 'uri' => '/map-request-to-attribute-with-default-value.dummy', + 'format' => 'dummy', + 'parameters' => [], + 'content' => 'Hello', + 'expectedResponse' => '415 Unsupported Media Type', + 'expectedStatusCode' => 415, + ]; + + yield 'request with invalid type mapping attribute with default value' => [ + 'uri' => '/map-request-to-attribute-with-default-value.json', + 'format' => 'json', + 'parameters' => [], + 'content' => <<<'JSON' + { + "comment": "Hello everyone!", + "approved": "string instead of bool" + } + JSON, + 'expectedResponse' => <<<'JSON' + { + "type": "https:\/\/symfony.com\/errors\/validation", + "title": "Validation Failed", + "status": 422, + "detail": "approved: This value should be of type bool.", + "violations": [ + { + "propertyPath": "approved", + "title": "This value should be of type bool.", + "template": "This value should be of type {{ type }}.", + "parameters": { + "{{ type }}": "bool" + } + } + ] + } + JSON, + 'expectedStatusCode' => 422, + ]; + + yield 'invalid request with json content mapping attribute with default value' => [ + 'uri' => '/map-request-to-attribute-with-default-value.json', + 'format' => 'json', + 'parameters' => [], + 'content' => <<<'JSON' + { + "comment": "", + "approved": true + } + JSON, + 'expectedResponse' => <<<'JSON' + { + "type": "https:\/\/symfony.com\/errors\/validation", + "title": "Validation Failed", + "status": 422, + "detail": "comment: This value should not be blank.\ncomment: This value is too short. It should have 10 characters or more.", + "violations": [ + { + "propertyPath": "comment", + "title": "This value should not be blank.", + "template": "This value should not be blank.", + "parameters": { + "{{ value }}": "\"\"" + }, + "type": "urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3" + }, + { + "propertyPath": "comment", + "title": "This value is too short. It should have 10 characters or more.", + "template": "This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.", + "parameters": { + "{{ value }}": "\"\"", + "{{ limit }}": "10", + "{{ value_length }}": "0" + }, + "type": "urn:uuid:9ff3fdc4-b214-49db-8718-39c315e33d45" + } + ] + } + JSON, + 'expectedStatusCode' => 422, + ]; + + yield 'invalid request with xml content mapping attribute with default value' => [ + 'uri' => '/map-request-to-attribute-with-default-value.xml', + 'format' => 'xml', + 'parameters' => [], + 'content' => <<<'XML' + + H + false + + XML, + 'expectedResponse' => <<<'XML' + + + https://symfony.com/errors/validation + Codestin Search App + 422 + comment: This value is too short. It should have 10 characters or more. + + comment + Codestin Search App + + + "H" + 10 + 1 + + urn:uuid:9ff3fdc4-b214-49db-8718-39c315e33d45 + + + XML, + 'expectedStatusCode' => 422, + ]; + + yield 'invalid request mapping attribute with default value' => [ + 'uri' => '/map-request-to-attribute-with-default-value.json', + 'format' => 'json', + 'input' => ['comment' => '', 'approved' => '1'], + 'content' => null, + 'expectedResponse' => <<<'JSON' + { + "type": "https:\/\/symfony.com\/errors\/validation", + "title": "Validation Failed", + "status": 422, + "detail": "comment: This value should not be blank.\ncomment: This value is too short. It should have 10 characters or more.", + "violations": [ + { + "propertyPath": "comment", + "title": "This value should not be blank.", + "template": "This value should not be blank.", + "parameters": { + "{{ value }}": "\"\"" + }, + "type": "urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3" + }, + { + "propertyPath": "comment", + "title": "This value is too short. It should have 10 characters or more.", + "template": "This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.", + "parameters": { + "{{ value }}": "\"\"", + "{{ limit }}": "10", + "{{ value_length }}": "0" + }, + "type": "urn:uuid:9ff3fdc4-b214-49db-8718-39c315e33d45" + } + ] + } + JSON, + 'expectedStatusCode' => 422, + ]; + + $expectedStatusCode = 400; + $expectedResponse = <<<'JSON' + { + "type":"https:\/\/tools.ietf.org\/html\/rfc2616#section-10", + "title":"An error occurred", + "status":400, + "detail":"Bad Request" + } + JSON; + + $httpKernelVersion = InstalledVersions::getVersion('symfony/http-kernel'); + if ($httpKernelVersion && version_compare($httpKernelVersion, '7.2.0', '<')) { + $expectedStatusCode = 422; + $expectedResponse = <<<'JSON' + { + "type": "https:\/\/tools.ietf.org\/html\/rfc2616#section-10", + "title": "An error occurred", + "status": 422, + "detail": "Unprocessable Content" + } + JSON; + } + + yield 'empty request mapping non-nullable attribute without default value' => [ + 'uri' => '/map-request-to-non-nullable-attribute-without-default-value.json', + 'format' => 'json', + 'parameters' => [], + 'content' => '', + 'expectedResponse' => $expectedResponse, + 'expectedStatusCode' => $expectedStatusCode, + ]; + + yield 'valid request with json content mapping non-nullable attribute without default value' => [ + 'uri' => '/map-request-to-non-nullable-attribute-without-default-value.json', + 'format' => 'json', + 'parameters' => [], + 'content' => <<<'JSON' + { + "comment": "Hello everyone!", + "approved": false + } + JSON, + 'expectedResponse' => <<<'JSON' + { + "comment": "Hello everyone!", + "approved": false + } + JSON, + 'expectedStatusCode' => 200, + ]; + + yield 'valid request with xml content mapping non-nullable attribute without default value' => [ + 'uri' => '/map-request-to-non-nullable-attribute-without-default-value.xml', + 'format' => 'xml', + 'parameters' => [], + 'content' => <<<'XML' + + Hello everyone! + true + + XML, + 'expectedResponse' => <<<'XML' + + Hello everyone! + 1 + + XML, + 'expectedStatusCode' => 200, + ]; + + yield 'valid request mapping non-nullable attribute without default value' => [ + 'uri' => '/map-request-to-non-nullable-attribute-without-default-value.json', + 'format' => 'json', + 'parameters' => ['comment' => 'Hello everyone!', 'approved' => '0'], + 'content' => null, + 'expectedResponse' => <<<'JSON' + { + "comment": "Hello everyone!", + "approved": false + } + JSON, + 'expectedStatusCode' => 200, + ]; + + yield 'malformed json request mapping non-nullable attribute without default value' => [ + 'uri' => '/map-request-to-non-nullable-attribute-without-default-value.json', + 'format' => 'json', + 'parameters' => [], + 'content' => <<<'JSON' + { + "comment": "Hello everyone!", + "approved": false, + } + JSON, + 'expectedResponse' => <<<'JSON' + { + "type": "https:\/\/tools.ietf.org\/html\/rfc2616#section-10", + "title": "An error occurred", + "status": 400, + "detail": "Bad Request" + } + JSON, + 'expectedStatusCode' => 400, + ]; + + yield 'request with unsupported format mapping non-nullable attribute without default value' => [ + 'uri' => '/map-request-to-non-nullable-attribute-without-default-value.dummy', + 'format' => 'dummy', + 'parameters' => [], + 'content' => 'Hello', + 'expectedResponse' => '415 Unsupported Media Type', + 'expectedStatusCode' => 415, + ]; + + yield 'request with invalid type mapping non-nullable attribute without default value' => [ + 'uri' => '/map-request-to-non-nullable-attribute-without-default-value.json', + 'format' => 'json', + 'parameters' => [], + 'content' => <<<'JSON' + { + "comment": "Hello everyone!", + "approved": "string instead of bool" + } + JSON, + 'expectedResponse' => <<<'JSON' + { + "type": "https:\/\/symfony.com\/errors\/validation", + "title": "Validation Failed", + "status": 422, + "detail": "approved: This value should be of type bool.", + "violations": [ + { + "propertyPath": "approved", + "title": "This value should be of type bool.", + "template": "This value should be of type {{ type }}.", + "parameters": { + "{{ type }}": "bool" + } + } + ] + } + JSON, + 'expectedStatusCode' => 422, + ]; + + yield 'invalid request with json content mapping non-nullable attribute without default value' => [ + 'uri' => '/map-request-to-non-nullable-attribute-without-default-value.json', + 'format' => 'json', + 'parameters' => [], + 'content' => <<<'JSON' + { + "comment": "", + "approved": true + } + JSON, + 'expectedResponse' => <<<'JSON' + { + "type": "https:\/\/symfony.com\/errors\/validation", + "title": "Validation Failed", + "status": 422, + "detail": "comment: This value should not be blank.\ncomment: This value is too short. It should have 10 characters or more.", + "violations": [ + { + "propertyPath": "comment", + "title": "This value should not be blank.", + "template": "This value should not be blank.", + "parameters": { + "{{ value }}": "\"\"" + }, + "type": "urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3" + }, + { + "propertyPath": "comment", + "title": "This value is too short. It should have 10 characters or more.", + "template": "This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.", + "parameters": { + "{{ value }}": "\"\"", + "{{ limit }}": "10", + "{{ value_length }}": "0" + }, + "type": "urn:uuid:9ff3fdc4-b214-49db-8718-39c315e33d45" + } + ] + } + JSON, + 'expectedStatusCode' => 422, + ]; + + yield 'invalid request with xml content mapping non-nullable attribute without default value' => [ + 'uri' => '/map-request-to-non-nullable-attribute-without-default-value.xml', + 'format' => 'xml', + 'parameters' => [], + 'content' => <<<'XML' + + H + false + + XML, + 'expectedResponse' => <<<'XML' + + + https://symfony.com/errors/validation + Codestin Search App + 422 + comment: This value is too short. It should have 10 characters or more. + + comment + Codestin Search App + + + "H" + 10 + 1 + + urn:uuid:9ff3fdc4-b214-49db-8718-39c315e33d45 + + + XML, + 'expectedStatusCode' => 422, + ]; + + yield 'invalid request mapping non-nullable attribute without default value' => [ + 'uri' => '/map-request-to-non-nullable-attribute-without-default-value.json', + 'format' => 'json', + 'input' => ['comment' => '', 'approved' => '1'], + 'content' => null, + 'expectedResponse' => <<<'JSON' + { + "type": "https:\/\/symfony.com\/errors\/validation", + "title": "Validation Failed", + "status": 422, + "detail": "comment: This value should not be blank.\ncomment: This value is too short. It should have 10 characters or more.", + "violations": [ + { + "propertyPath": "comment", + "title": "This value should not be blank.", + "template": "This value should not be blank.", + "parameters": { + "{{ value }}": "\"\"" + }, + "type": "urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3" + }, + { + "propertyPath": "comment", + "title": "This value is too short. It should have 10 characters or more.", + "template": "This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.", + "parameters": { + "{{ value }}": "\"\"", + "{{ limit }}": "10", + "{{ value_length }}": "0" + }, + "type": "urn:uuid:9ff3fdc4-b214-49db-8718-39c315e33d45" + } + ] + } + JSON, + 'expectedStatusCode' => 422, + ]; + } +} + +class WithMapQueryStringToNullableAttributeController +{ + public function __invoke(#[MapQueryString] ?QueryString $query): Response + { + if (!$query) { + return new Response('', Response::HTTP_NO_CONTENT); + } + + return new JsonResponse( + ['filter' => ['status' => $query->filter->status, 'quantity' => $query->filter->quantity]], + ); + } +} + +class WithMapQueryStringToAttributeWithDefaultValueController +{ + public function __invoke(#[MapQueryString] QueryString $query = new QueryString(new Filter('approved', 5))): Response + { + return new JsonResponse( + ['filter' => ['status' => $query->filter->status, 'quantity' => $query->filter->quantity]], + ); + } +} + +class WithMapQueryStringToNonNullableAttributeWithoutDefaultValueController +{ + public function __invoke(#[MapQueryString] QueryString $query): Response + { + return new JsonResponse( + ['filter' => ['status' => $query->filter->status, 'quantity' => $query->filter->quantity]], + ); + } +} + +class WithMapRequestToNullableAttributeController +{ + public function __invoke(#[MapRequestPayload] ?RequestBody $body, Request $request): Response + { + if ('json' === $request->getPreferredFormat('json')) { + if (!$body) { + return new Response('', Response::HTTP_NO_CONTENT); + } + + return new JsonResponse(['comment' => $body->comment, 'approved' => $body->approved]); + } + + return new Response( + << + {$body->comment} + {$body->approved} + + XML + ); + } +} + +class WithMapRequestToAttributeWithDefaultValueController +{ + public function __invoke(Request $request, #[MapRequestPayload] RequestBody $body = new RequestBody('Hello everyone!', false)): Response + { + if ('json' === $request->getPreferredFormat('json')) { + return new JsonResponse(['comment' => $body->comment, 'approved' => $body->approved]); + } + + return new Response( + << + {$body->comment} + {$body->approved} + + XML + ); + } +} + +class WithMapRequestToNonNullableAttributeWithoutDefaultValueController +{ + public function __invoke(Request $request, #[MapRequestPayload] RequestBody $body): Response + { + if ('json' === $request->getPreferredFormat('json')) { return new JsonResponse(['comment' => $body->comment, 'approved' => $body->approved]); } diff --git a/Tests/Functional/app/ApiAttributesTest/routing.yml b/Tests/Functional/app/ApiAttributesTest/routing.yml index 9ec40e170..a2827eb3d 100644 --- a/Tests/Functional/app/ApiAttributesTest/routing.yml +++ b/Tests/Functional/app/ApiAttributesTest/routing.yml @@ -1,7 +1,23 @@ -map_query_string: - path: /map-query-string.{_format} - controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\WithMapQueryStringController +map_query_string_to_nullable_attribute: + path: /map-query-string-to-nullable-attribute.{_format} + controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\WithMapQueryStringToNullableAttributeController -map_request_body: - path: /map-request-body.{_format} - controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\WithMapRequestPayloadController +map_query_string_to_attribute_with_default_value: + path: /map-query-string-to-attribute-with-default-value.{_format} + controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\WithMapQueryStringToAttributeWithDefaultValueController + +map_query_string_to_non_nullable_attribute_without_default_value: + path: /map-query-string-to-non-nullable-attribute-without-default-value.{_format} + controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\WithMapQueryStringToNonNullableAttributeWithoutDefaultValueController + +map_request_to_nullable_attribute: + path: /map-request-to-nullable-attribute.{_format} + controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\WithMapRequestToNullableAttributeController + +map_request_to_attribute_with_default_value: + path: /map-request-to-attribute-with-default-value.{_format} + controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\WithMapRequestToAttributeWithDefaultValueController + +map_request_to_non_nullable_attribute_without_default_value: + path: /map-request-to-non-nullable-attribute-without-default-value.{_format} + controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\WithMapRequestToNonNullableAttributeWithoutDefaultValueController From 5fae5196cdc2a00726300a831b0c274df44e89a9 Mon Sep 17 00:00:00 2001 From: Lukas Kaltenbach Date: Tue, 2 Jul 2024 20:45:16 +0200 Subject: [PATCH 098/281] [Notifier] Add Sipgate bridge --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_transports.php | 1 + 2 files changed, 2 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 1114246cc..46ba61aa6 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2801,6 +2801,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\RingCentral\RingCentralTransportFactory::class => 'notifier.transport_factory.ring-central', NotifierBridge\RocketChat\RocketChatTransportFactory::class => 'notifier.transport_factory.rocket-chat', NotifierBridge\Sendberry\SendberryTransportFactory::class => 'notifier.transport_factory.sendberry', + NotifierBridge\Sipgate\SipgateTransportFactory::class => 'notifier.transport_factory.sipgate', NotifierBridge\SimpleTextin\SimpleTextinTransportFactory::class => 'notifier.transport_factory.simple-textin', NotifierBridge\Sevenio\SevenIoTransportFactory::class => 'notifier.transport_factory.sevenio', NotifierBridge\Sinch\SinchTransportFactory::class => 'notifier.transport_factory.sinch', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index 5ddc9ae24..98ff2d50c 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -93,6 +93,7 @@ 'ring-central' => Bridge\RingCentral\RingCentralTransportFactory::class, 'sendberry' => Bridge\Sendberry\SendberryTransportFactory::class, 'sevenio' => Bridge\Sevenio\SevenIoTransportFactory::class, + 'sipgate' => Bridge\Sipgate\SipgateTransportFactory::class, 'simple-textin' => Bridge\SimpleTextin\SimpleTextinTransportFactory::class, 'sinch' => Bridge\Sinch\SinchTransportFactory::class, 'sms-biuras' => Bridge\SmsBiuras\SmsBiurasTransportFactory::class, From f497f34253377b8f85644a7380d6f9299649c66d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 6 Jul 2024 09:57:16 +0200 Subject: [PATCH 099/281] Update .gitattributes --- .gitattributes | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 84c7add05..14c3c3594 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,3 @@ /Tests export-ignore /phpunit.xml.dist export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore +/.git* export-ignore From a6929df8c5b9862552f16dbb9d37f815ef58307f Mon Sep 17 00:00:00 2001 From: Petrisor Ciprian Daniel Date: Sat, 6 Jul 2024 22:13:31 +0300 Subject: [PATCH 100/281] [FrameworkBundle] Add `exit` option to `secrets:decrypt-to-local` command --- CHANGELOG.md | 1 + Command/SecretsDecryptToLocalCommand.php | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2470d542..2db5b9674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG * Derivate `kernel.secret` from the decryption secret when its env var is not defined * Make the `config/` directory optional in `MicroKernelTrait`, add support for service arguments in the invokable Kernel class, and register `FrameworkBundle` by default when the `bundles.php` file is missing + * Add `exit` option for `secrets:decrypt-to-local` command 7.1 --- diff --git a/Command/SecretsDecryptToLocalCommand.php b/Command/SecretsDecryptToLocalCommand.php index b078769d8..e98a6d54d 100644 --- a/Command/SecretsDecryptToLocalCommand.php +++ b/Command/SecretsDecryptToLocalCommand.php @@ -38,15 +38,20 @@ public function __construct( protected function configure(): void { $this + ->addOption('exit', null, InputOption::VALUE_NONE, 'Returns a non-zero exit code if any errors are encountered') ->addOption('force', 'f', InputOption::VALUE_NONE, 'Force overriding of secrets that already exist in the local vault') ->setHelp(<<<'EOF' The %command.name% command decrypts all secrets and copies them in the local vault. %command.full_name% -When the option --force is provided, secrets that already exist in the local vault are overriden. +When the --force option is provided, secrets that already exist in the local vault are overriden. %command.full_name% --force + +When the --exit option is provided, the command will return a non-zero exit code if any errors are encountered. + + %command.full_name% --exit EOF ) ; @@ -83,9 +88,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int ]); } + $hadErrors = false; foreach ($secrets as $k => $v) { if (null === $v) { $io->error($this->vault->getLastMessage() ?? \sprintf('Secret "%s" has been skipped as there was an error reading it.', $k)); + $hadErrors = true; continue; } @@ -93,6 +100,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->note($this->localVault->getLastMessage()); } + if ($hadErrors && $input->getOption('exit')) { + return 1; + } + return 0; } } From 4495b7fe12f3f5f013a98ceb9b0511ac6a015217 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 15 Jul 2024 14:43:16 +0200 Subject: [PATCH 101/281] Use `createMock` --- Tests/Controller/AbstractControllerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Controller/AbstractControllerTest.php b/Tests/Controller/AbstractControllerTest.php index 0dc06f30e..55a363984 100644 --- a/Tests/Controller/AbstractControllerTest.php +++ b/Tests/Controller/AbstractControllerTest.php @@ -431,7 +431,7 @@ public function testRenderViewWithForm() { $formView = new FormView(); - $form = $this->getMockBuilder(FormInterface::class)->getMock(); + $form = $this->createMock(FormInterface::class); $form->expects($this->once())->method('createView')->willReturn($formView); $twig = $this->getMockBuilder(Environment::class)->disableOriginalConstructor()->getMock(); @@ -452,7 +452,7 @@ public function testRenderWithFormSubmittedAndInvalid() { $formView = new FormView(); - $form = $this->getMockBuilder(FormInterface::class)->getMock(); + $form = $this->createMock(FormInterface::class); $form->expects($this->once())->method('createView')->willReturn($formView); $form->expects($this->once())->method('isSubmitted')->willReturn(true); $form->expects($this->once())->method('isValid')->willReturn(false); From 0931874388879f3c3b7219228755a9f53bd880a4 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 17 Jul 2024 10:14:50 +0200 Subject: [PATCH 102/281] do not use uniqid() for generating dev tool tokens --- EventListener/ConsoleProfilerListener.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EventListener/ConsoleProfilerListener.php b/EventListener/ConsoleProfilerListener.php index 3df9c61b9..4a8c9015e 100644 --- a/EventListener/ConsoleProfilerListener.php +++ b/EventListener/ConsoleProfilerListener.php @@ -77,7 +77,7 @@ public function initialize(ConsoleCommandEvent $event): void return; } - $request->attributes->set('_stopwatch_token', substr(hash('xxh128', uniqid(mt_rand(), true)), 0, 6)); + $request->attributes->set('_stopwatch_token', bin2hex(random_bytes(3))); $this->stopwatch->openSection(); } From 3a8d803cbea3badde4ac41a069adc93a883e4eb1 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 19 Jul 2024 10:48:20 +0200 Subject: [PATCH 103/281] [HttpFoundation][HttpKernel] Remove dead code and useless casts --- CacheWarmer/RouterCacheWarmer.php | 2 +- CacheWarmer/TranslationsCacheWarmer.php | 2 +- Tests/CacheWarmer/RouterCacheWarmerTest.php | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CacheWarmer/RouterCacheWarmer.php b/CacheWarmer/RouterCacheWarmer.php index 149d20ce3..052bfb39d 100644 --- a/CacheWarmer/RouterCacheWarmer.php +++ b/CacheWarmer/RouterCacheWarmer.php @@ -43,7 +43,7 @@ public function warmUp(string $cacheDir, ?string $buildDir = null): array $router = $this->container->get('router'); if ($router instanceof WarmableInterface) { - return (array) $router->warmUp($cacheDir, $buildDir); + return $router->warmUp($cacheDir, $buildDir); } throw new \LogicException(\sprintf('The router "%s" cannot be warmed up because it does not implement "%s".', get_debug_type($router), WarmableInterface::class)); diff --git a/CacheWarmer/TranslationsCacheWarmer.php b/CacheWarmer/TranslationsCacheWarmer.php index 19b2725c9..3b04a9bc2 100644 --- a/CacheWarmer/TranslationsCacheWarmer.php +++ b/CacheWarmer/TranslationsCacheWarmer.php @@ -40,7 +40,7 @@ public function warmUp(string $cacheDir, ?string $buildDir = null): array $this->translator ??= $this->container->get('translator'); if ($this->translator instanceof WarmableInterface) { - return (array) $this->translator->warmUp($cacheDir, $buildDir); + return $this->translator->warmUp($cacheDir, $buildDir); } return []; diff --git a/Tests/CacheWarmer/RouterCacheWarmerTest.php b/Tests/CacheWarmer/RouterCacheWarmerTest.php index 615010a47..06f738f2b 100644 --- a/Tests/CacheWarmer/RouterCacheWarmerTest.php +++ b/Tests/CacheWarmer/RouterCacheWarmerTest.php @@ -24,6 +24,8 @@ public function testWarmUpWithWarmableInterfaceWithBuildDir() $container = new Container(); $routerMock = $this->getMockBuilder(testRouterInterfaceWithWarmableInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection', 'warmUp'])->getMock(); + $routerMock->method('warmUp')->willReturn([]); + $container->set('router', $routerMock); $routerCacheWarmer = new RouterCacheWarmer($container); From 0643d07ef409d8246ffbab8a1f9205fda4998303 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 22 Jul 2024 12:05:17 +0200 Subject: [PATCH 104/281] terminate with non-zero exit code when a secret could not be read The fix for #42038 did a bit too much. Not only did it fix the PHP error that wasn't caught before, but also changed the exit code of the command. With this change the PHP error will still be prevented, but the command will terminate with a non-zero exit code to indicate the failure that occurred while reading the stored secrets. --- CHANGELOG.md | 2 +- Command/SecretsDecryptToLocalCommand.php | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2db5b9674..7058a3fb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ CHANGELOG * Derivate `kernel.secret` from the decryption secret when its env var is not defined * Make the `config/` directory optional in `MicroKernelTrait`, add support for service arguments in the invokable Kernel class, and register `FrameworkBundle` by default when the `bundles.php` file is missing - * Add `exit` option for `secrets:decrypt-to-local` command + * [BC BREAK] The `secrets:decrypt-to-local` command terminates with a non-zero exit code when a secret could not be read 7.1 --- diff --git a/Command/SecretsDecryptToLocalCommand.php b/Command/SecretsDecryptToLocalCommand.php index a88924646..c46da963b 100644 --- a/Command/SecretsDecryptToLocalCommand.php +++ b/Command/SecretsDecryptToLocalCommand.php @@ -38,7 +38,6 @@ public function __construct( protected function configure(): void { $this - ->addOption('exit', null, InputOption::VALUE_NONE, 'Returns a non-zero exit code if any errors are encountered') ->addOption('force', 'f', InputOption::VALUE_NONE, 'Force overriding of secrets that already exist in the local vault') ->setHelp(<<<'EOF' The %command.name% command decrypts all secrets and copies them in the local vault. @@ -48,10 +47,6 @@ protected function configure(): void When the --force option is provided, secrets that already exist in the local vault are overridden. %command.full_name% --force - -When the --exit option is provided, the command will return a non-zero exit code if any errors are encountered. - - %command.full_name% --exit EOF ) ; @@ -100,7 +95,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->note($this->localVault->getLastMessage()); } - if ($hadErrors && $input->getOption('exit')) { + if ($hadErrors) { return 1; } From d99ca6e6321b4db25d74fbc99ef4fbc1e6f7adcc Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 22 Jul 2024 10:27:43 +0200 Subject: [PATCH 105/281] Use CPP where possible --- CacheWarmer/CachePoolClearerCacheWarmer.php | 11 ++++------- CacheWarmer/ConfigBuilderCacheWarmer.php | 11 ++++------- CacheWarmer/RouterCacheWarmer.php | 12 ++++++------ CacheWarmer/TranslationsCacheWarmer.php | 11 ++++++----- Routing/Router.php | 13 +++++++++---- Secrets/SodiumVault.php | 9 +++++---- 6 files changed, 34 insertions(+), 33 deletions(-) diff --git a/CacheWarmer/CachePoolClearerCacheWarmer.php b/CacheWarmer/CachePoolClearerCacheWarmer.php index ae27502cf..e67f84dac 100644 --- a/CacheWarmer/CachePoolClearerCacheWarmer.php +++ b/CacheWarmer/CachePoolClearerCacheWarmer.php @@ -25,16 +25,13 @@ */ final class CachePoolClearerCacheWarmer implements CacheWarmerInterface { - private Psr6CacheClearer $poolClearer; - private array $pools; - /** * @param string[] $pools */ - public function __construct(Psr6CacheClearer $poolClearer, array $pools = []) - { - $this->poolClearer = $poolClearer; - $this->pools = $pools; + public function __construct( + private Psr6CacheClearer $poolClearer, + private array $pools = [], + ) { } public function warmUp(string $cacheDir, ?string $buildDir = null): array diff --git a/CacheWarmer/ConfigBuilderCacheWarmer.php b/CacheWarmer/ConfigBuilderCacheWarmer.php index 8b692c9c7..48ed51aec 100644 --- a/CacheWarmer/ConfigBuilderCacheWarmer.php +++ b/CacheWarmer/ConfigBuilderCacheWarmer.php @@ -34,13 +34,10 @@ */ class ConfigBuilderCacheWarmer implements CacheWarmerInterface { - private KernelInterface $kernel; - private ?LoggerInterface $logger; - - public function __construct(KernelInterface $kernel, ?LoggerInterface $logger = null) - { - $this->kernel = $kernel; - $this->logger = $logger; + public function __construct( + private KernelInterface $kernel, + private ?LoggerInterface $logger = null, + ) { } public function warmUp(string $cacheDir, ?string $buildDir = null): array diff --git a/CacheWarmer/RouterCacheWarmer.php b/CacheWarmer/RouterCacheWarmer.php index 149d20ce3..352d6e145 100644 --- a/CacheWarmer/RouterCacheWarmer.php +++ b/CacheWarmer/RouterCacheWarmer.php @@ -26,12 +26,12 @@ */ class RouterCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface { - private ContainerInterface $container; - - public function __construct(ContainerInterface $container) - { - // As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected. - $this->container = $container; + /** + * As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected. + */ + public function __construct( + private ContainerInterface $container, + ) { } public function warmUp(string $cacheDir, ?string $buildDir = null): array diff --git a/CacheWarmer/TranslationsCacheWarmer.php b/CacheWarmer/TranslationsCacheWarmer.php index 19b2725c9..ceed4e6ed 100644 --- a/CacheWarmer/TranslationsCacheWarmer.php +++ b/CacheWarmer/TranslationsCacheWarmer.php @@ -26,13 +26,14 @@ */ class TranslationsCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface { - private ContainerInterface $container; private TranslatorInterface $translator; - public function __construct(ContainerInterface $container) - { - // As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected. - $this->container = $container; + /** + * As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected. + */ + public function __construct( + private ContainerInterface $container, + ) { } public function warmUp(string $cacheDir, ?string $buildDir = null): array diff --git a/Routing/Router.php b/Routing/Router.php index 44d222a86..9efa07fae 100644 --- a/Routing/Router.php +++ b/Routing/Router.php @@ -35,16 +35,21 @@ */ class Router extends BaseRouter implements WarmableInterface, ServiceSubscriberInterface { - private ContainerInterface $container; private array $collectedParameters = []; private \Closure $paramFetcher; /** * @param mixed $resource The main resource to load */ - public function __construct(ContainerInterface $container, mixed $resource, array $options = [], ?RequestContext $context = null, ?ContainerInterface $parameters = null, ?LoggerInterface $logger = null, ?string $defaultLocale = null) - { - $this->container = $container; + public function __construct( + private ContainerInterface $container, + mixed $resource, + array $options = [], + ?RequestContext $context = null, + ?ContainerInterface $parameters = null, + ?LoggerInterface $logger = null, + ?string $defaultLocale = null, + ) { $this->resource = $resource; $this->context = $context ?? new RequestContext(); $this->logger = $logger; diff --git a/Secrets/SodiumVault.php b/Secrets/SodiumVault.php index 74713e28c..f747fed14 100644 --- a/Secrets/SodiumVault.php +++ b/Secrets/SodiumVault.php @@ -26,18 +26,19 @@ class SodiumVault extends AbstractVault implements EnvVarLoaderInterface private string|\Stringable|null $decryptionKey = null; private string $pathPrefix; private ?string $secretsDir; - private ?string $derivedSecretEnvVar; /** * @param $decryptionKey A string or a stringable object that defines the private key to use to decrypt the vault * or null to store generated keys in the provided $secretsDir */ - public function __construct(string $secretsDir, #[\SensitiveParameter] string|\Stringable|null $decryptionKey = null, ?string $derivedSecretEnvVar = null) - { + public function __construct( + string $secretsDir, + #[\SensitiveParameter] string|\Stringable|null $decryptionKey = null, + private ?string $derivedSecretEnvVar = null, + ) { $this->pathPrefix = rtrim(strtr($secretsDir, '/', \DIRECTORY_SEPARATOR), \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR.basename($secretsDir).'.'; $this->decryptionKey = $decryptionKey; $this->secretsDir = $secretsDir; - $this->derivedSecretEnvVar = $derivedSecretEnvVar; } public function generateKeys(bool $override = false): bool From 6ae2ba1a467890c38cd94146f63aa2e2d31103d0 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 19 Jul 2024 13:24:25 +0200 Subject: [PATCH 106/281] [Cache][Config][Console][DependencyInjection][FrameworkBundle] Remove dead code and useless casts --- CacheWarmer/AbstractPhpFileCacheWarmer.php | 2 +- Console/Descriptor/XmlDescriptor.php | 4 ++-- DependencyInjection/FrameworkExtension.php | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CacheWarmer/AbstractPhpFileCacheWarmer.php b/CacheWarmer/AbstractPhpFileCacheWarmer.php index d809888be..a18faae7d 100644 --- a/CacheWarmer/AbstractPhpFileCacheWarmer.php +++ b/CacheWarmer/AbstractPhpFileCacheWarmer.php @@ -58,7 +58,7 @@ public function warmUp(string $cacheDir, ?string $buildDir = null): array */ protected function warmUpPhpArrayAdapter(PhpArrayAdapter $phpArrayAdapter, array $values): array { - return (array) $phpArrayAdapter->warmUp($values); + return $phpArrayAdapter->warmUp($values); } /** diff --git a/Console/Descriptor/XmlDescriptor.php b/Console/Descriptor/XmlDescriptor.php index dd2744f80..c41ac296f 100644 --- a/Console/Descriptor/XmlDescriptor.php +++ b/Console/Descriptor/XmlDescriptor.php @@ -64,7 +64,7 @@ protected function describeContainerService(object $service, array $options = [] protected function describeContainerServices(ContainerBuilder $container, array $options = []): void { - $this->writeDocument($this->getContainerServicesDocument($container, $options['tag'] ?? null, isset($options['show_hidden']) && $options['show_hidden'], isset($options['show_arguments']) && $options['show_arguments'], $options['filter'] ?? null, $options['id'] ?? null)); + $this->writeDocument($this->getContainerServicesDocument($container, $options['tag'] ?? null, isset($options['show_hidden']) && $options['show_hidden'], isset($options['show_arguments']) && $options['show_arguments'], $options['filter'] ?? null)); } protected function describeContainerDefinition(Definition $definition, array $options = [], ?ContainerBuilder $container = null): void @@ -288,7 +288,7 @@ private function getContainerServiceDocument(object $service, string $id, ?Conta return $dom; } - private function getContainerServicesDocument(ContainerBuilder $container, ?string $tag = null, bool $showHidden = false, bool $showArguments = false, ?callable $filter = null, ?string $id = null): \DOMDocument + private function getContainerServicesDocument(ContainerBuilder $container, ?string $tag = null, bool $showHidden = false, bool $showArguments = false, ?callable $filter = null): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($containerXML = $dom->createElement('container')); diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index ac4c21114..49821a043 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -498,7 +498,7 @@ public function load(array $configs, ContainerBuilder $container): void if (!$messengerEnabled) { throw new LogicException('Scheduler support cannot be enabled as the Messenger component is not '.(interface_exists(MessageBusInterface::class) ? 'enabled.' : 'installed. Try running "composer require symfony/messenger".')); } - $this->registerSchedulerConfiguration($config['scheduler'], $container, $loader); + $this->registerSchedulerConfiguration($container, $loader); } else { $container->removeDefinition('cache.scheduler'); $container->removeDefinition('console.command.scheduler_debug'); @@ -570,7 +570,7 @@ public function load(array $configs, ContainerBuilder $container): void } if ($this->readConfigEnabled('remote-event', $container, $config['remote-event'])) { - $this->registerRemoteEventConfiguration($config['remote-event'], $container, $loader); + $this->registerRemoteEventConfiguration($loader); } if ($this->readConfigEnabled('html_sanitizer', $container, $config['html_sanitizer'])) { @@ -2073,7 +2073,7 @@ private function registerSemaphoreConfiguration(array $config, ContainerBuilder } } - private function registerSchedulerConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void + private function registerSchedulerConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void { if (!class_exists(SchedulerTransportFactory::class)) { throw new LogicException('Scheduler support cannot be enabled as the Scheduler component is not installed. Try running "composer require symfony/scheduler".'); @@ -2940,7 +2940,7 @@ private function registerWebhookConfiguration(array $config, ContainerBuilder $c $controller->replaceArgument(1, new Reference($config['message_bus'])); } - private function registerRemoteEventConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void + private function registerRemoteEventConfiguration(PhpFileLoader $loader): void { if (!class_exists(RemoteEvent::class)) { throw new LogicException('RemoteEvent support cannot be enabled as the component is not installed. Try running "composer require symfony/remote-event".'); From 0c8ac7da2b89cdd69dbb7072ff41a102b5aa4dfa Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 31 Jul 2024 16:13:26 +0200 Subject: [PATCH 107/281] Remove unused code and unnecessary `else` branches --- Controller/ControllerResolver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Controller/ControllerResolver.php b/Controller/ControllerResolver.php index 269709963..ef9ca3993 100644 --- a/Controller/ControllerResolver.php +++ b/Controller/ControllerResolver.php @@ -27,9 +27,9 @@ protected function instantiateController(string $class): object if ($controller instanceof AbstractController) { if (null === $previousContainer = $controller->setContainer($this->container)) { throw new \LogicException(\sprintf('"%s" has no container set, did you forget to define it as a service subscriber?', $class)); - } else { - $controller->setContainer($previousContainer); } + + $controller->setContainer($previousContainer); } return $controller; From 5b2ddbbf95108ea92616ca611ed1c693d7161369 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 5 Aug 2024 09:12:25 +0200 Subject: [PATCH 108/281] Fix multiple CS errors --- Command/SecretsDecryptToLocalCommand.php | 2 +- DependencyInjection/FrameworkExtension.php | 2 +- Resources/config/services.php | 2 +- Tests/DependencyInjection/ConfigurationTest.php | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Command/SecretsDecryptToLocalCommand.php b/Command/SecretsDecryptToLocalCommand.php index c46da963b..4e392b677 100644 --- a/Command/SecretsDecryptToLocalCommand.php +++ b/Command/SecretsDecryptToLocalCommand.php @@ -95,7 +95,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->note($this->localVault->getLastMessage()); } - if ($hadErrors) { + if ($hadErrors) { return 1; } diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 49821a043..067e631c7 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -14,7 +14,6 @@ use Composer\InstalledVersions; use Http\Client\HttpAsyncClient; use Http\Client\HttpClient; -use Symfony\Component\TypeInfo\TypeResolver\PhpDocAwareReflectionTypeResolver; use phpDocumentor\Reflection\DocBlockFactoryInterface; use phpDocumentor\Reflection\Types\ContextFactory; use PhpParser\Parser; @@ -173,6 +172,7 @@ use Symfony\Component\Translation\PseudoLocalizationTranslator; use Symfony\Component\Translation\Translator; use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\TypeResolver\PhpDocAwareReflectionTypeResolver; use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver; use Symfony\Component\Uid\Factory\UuidFactory; use Symfony\Component\Uid\UuidV4; diff --git a/Resources/config/services.php b/Resources/config/services.php index 4095af666..53856f356 100644 --- a/Resources/config/services.php +++ b/Resources/config/services.php @@ -155,7 +155,7 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : [] ->set('uri_signer', UriSigner::class) ->args([ - new Parameter('kernel.secret') + new Parameter('kernel.secret'), ]) ->alias(UriSigner::class, 'uri_signer') diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 793be0283..bad27f8ee 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -13,7 +13,6 @@ use Doctrine\DBAL\Connection; use PHPUnit\Framework\TestCase; -use Seld\JsonLint\JsonParser; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration; use Symfony\Bundle\FullStack; use Symfony\Component\Cache\Adapter\DoctrineAdapter; From 13dabdf3364ddaf3d4e439318144c32bbcfe11e9 Mon Sep 17 00:00:00 2001 From: Roy de Vos Burchart Date: Thu, 1 Aug 2024 17:21:17 +0200 Subject: [PATCH 109/281] Code style change in `@PER-CS2.0` affecting `@Symfony` (parentheses for anonymous classes) --- Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php | 10 +++++----- .../DependencyInjection/Compiler/ProfilerPassTest.php | 4 ++-- Tests/Test/WebTestCaseTest.php | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php b/Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php index cf5f88c15..994151807 100644 --- a/Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php +++ b/Tests/CacheWarmer/ConfigBuilderCacheWarmerTest.php @@ -189,7 +189,7 @@ public function testExtensionAddedInKernel() $kernel = new class($this->varDir) extends TestKernel { protected function build(ContainerBuilder $container): void { - $container->registerExtension(new class() extends Extension implements ConfigurationInterface { + $container->registerExtension(new class extends Extension implements ConfigurationInterface { public function load(array $configs, ContainerBuilder $container): void { } @@ -276,7 +276,7 @@ protected function build(ContainerBuilder $container): void { /** @var TestSecurityExtension $extension */ $extension = $container->getExtension('test_security'); - $extension->addAuthenticatorFactory(new class() implements TestAuthenticatorFactoryInterface { + $extension->addAuthenticatorFactory(new class implements TestAuthenticatorFactoryInterface { public function getKey(): string { return 'token'; @@ -292,19 +292,19 @@ public function registerBundles(): iterable { yield from parent::registerBundles(); - yield new class() extends Bundle { + yield new class extends Bundle { public function getContainerExtension(): ExtensionInterface { return new TestSecurityExtension(); } }; - yield new class() extends Bundle { + yield new class extends Bundle { public function build(ContainerBuilder $container): void { /** @var TestSecurityExtension $extension */ $extension = $container->getExtension('test_security'); - $extension->addAuthenticatorFactory(new class() implements TestAuthenticatorFactoryInterface { + $extension->addAuthenticatorFactory(new class implements TestAuthenticatorFactoryInterface { public function getKey(): string { return 'form-login'; diff --git a/Tests/DependencyInjection/Compiler/ProfilerPassTest.php b/Tests/DependencyInjection/Compiler/ProfilerPassTest.php index 1b699d4d1..5a2215009 100644 --- a/Tests/DependencyInjection/Compiler/ProfilerPassTest.php +++ b/Tests/DependencyInjection/Compiler/ProfilerPassTest.php @@ -66,7 +66,7 @@ public function testValidCollector() public static function provideValidCollectorWithTemplateUsingAutoconfigure(): \Generator { - yield [new class() implements TemplateAwareDataCollectorInterface { + yield [new class implements TemplateAwareDataCollectorInterface { public function collect(Request $request, Response $response, ?\Throwable $exception = null): void { } @@ -86,7 +86,7 @@ public static function getTemplate(): string } }]; - yield [new class() extends AbstractDataCollector { + yield [new class extends AbstractDataCollector { public function collect(Request $request, Response $response, ?\Throwable $exception = null): void { } diff --git a/Tests/Test/WebTestCaseTest.php b/Tests/Test/WebTestCaseTest.php index 54a8044b0..effc7b081 100644 --- a/Tests/Test/WebTestCaseTest.php +++ b/Tests/Test/WebTestCaseTest.php @@ -388,7 +388,7 @@ private function getRequestTester(): WebTestCase private function getTester(KernelBrowser $client): WebTestCase { - $tester = new class() extends WebTestCase { + $tester = new class extends WebTestCase { use WebTestAssertionsTrait { getClient as public; } From 6b21a82872bf856946bdd8f9319f78d7a8b4b20a Mon Sep 17 00:00:00 2001 From: Jonas Claes Date: Thu, 1 Aug 2024 19:22:52 +0200 Subject: [PATCH 110/281] [Mailer] Implement Postal mailer --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/mailer_transports.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 49821a043..35784d3ee 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2642,6 +2642,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co MailerBridge\Mailomat\Transport\MailomatTransportFactory::class => 'mailer.transport_factory.mailomat', MailerBridge\MailPace\Transport\MailPaceTransportFactory::class => 'mailer.transport_factory.mailpace', MailerBridge\Mailchimp\Transport\MandrillTransportFactory::class => 'mailer.transport_factory.mailchimp', + MailerBridge\Postal\Transport\PostalTransportFactory::class => 'mailer.transport_factory.postal', MailerBridge\Postmark\Transport\PostmarkTransportFactory::class => 'mailer.transport_factory.postmark', MailerBridge\Resend\Transport\ResendTransportFactory::class => 'mailer.transport_factory.resend', MailerBridge\Scaleway\Transport\ScalewayTransportFactory::class => 'mailer.transport_factory.scaleway', diff --git a/Resources/config/mailer_transports.php b/Resources/config/mailer_transports.php index bdcd7e9c6..34e688532 100644 --- a/Resources/config/mailer_transports.php +++ b/Resources/config/mailer_transports.php @@ -22,6 +22,7 @@ use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetTransportFactory; use Symfony\Component\Mailer\Bridge\Mailomat\Transport\MailomatTransportFactory; use Symfony\Component\Mailer\Bridge\MailPace\Transport\MailPaceTransportFactory; +use Symfony\Component\Mailer\Bridge\Postal\Transport\PostalTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; use Symfony\Component\Mailer\Bridge\Resend\Transport\ResendTransportFactory; use Symfony\Component\Mailer\Bridge\Scaleway\Transport\ScalewayTransportFactory; @@ -57,6 +58,7 @@ 'mailpace' => MailPaceTransportFactory::class, 'native' => NativeTransportFactory::class, 'null' => NullTransportFactory::class, + 'postal' => PostalTransportFactory::class, 'postmark' => PostmarkTransportFactory::class, 'resend' => ResendTransportFactory::class, 'scaleway' => ScalewayTransportFactory::class, From 711af4eefcb4054a9c93e44b403626e1826bcddd Mon Sep 17 00:00:00 2001 From: HypeMC Date: Sun, 11 Aug 2024 18:10:02 +0200 Subject: [PATCH 111/281] [FrameworkBundle] Re-remove redundant name attribute from `default_context` --- DependencyInjection/Configuration.php | 1 - 1 file changed, 1 deletion(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 1094c14d3..2e9cacaa8 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1118,7 +1118,6 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $e ->end() ->arrayNode('default_context') ->normalizeKeys(false) - ->useAttributeAsKey('name') ->validate() ->ifTrue(fn () => $this->debug && class_exists(JsonParser::class)) ->then(fn (array $v) => $v + [JsonDecode::DETAILED_ERROR_MESSAGES => true]) From 6dd1dab4040a0022eb22f44bc07f12a551fd5449 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 6 Aug 2024 09:58:47 +0200 Subject: [PATCH 112/281] [FrameworkBundle] Deprecate making `cache.app` adapter taggable --- CHANGELOG.md | 1 + DependencyInjection/FrameworkExtension.php | 5 +++++ .../Fixtures/php/cache_cacheapp_tagaware.php | 16 ++++++++++++++++ .../Fixtures/xml/cache_cacheapp_tagaware.xml | 15 +++++++++++++++ .../Fixtures/yml/cache_cacheapp_tagaware.yml | 11 +++++++++++ .../FrameworkExtensionTestCase.php | 13 +++++++++++++ 6 files changed, 61 insertions(+) create mode 100644 Tests/DependencyInjection/Fixtures/php/cache_cacheapp_tagaware.php create mode 100644 Tests/DependencyInjection/Fixtures/xml/cache_cacheapp_tagaware.xml create mode 100644 Tests/DependencyInjection/Fixtures/yml/cache_cacheapp_tagaware.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 7058a3fb2..62eab0457 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ CHANGELOG * Make the `config/` directory optional in `MicroKernelTrait`, add support for service arguments in the invokable Kernel class, and register `FrameworkBundle` by default when the `bundles.php` file is missing * [BC BREAK] The `secrets:decrypt-to-local` command terminates with a non-zero exit code when a secret could not be read + * Deprecate making `cache.app` adapter taggable, use the `cache.app.taggable` adapter instead 7.1 --- diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 067e631c7..e79dba300 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2363,6 +2363,11 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con ]; } foreach ($config['pools'] as $name => $pool) { + if (\in_array('cache.app', $pool['adapters'] ?? [], true) && $pool['tags']) { + trigger_deprecation('symfony/framework-bundle', '7.2', 'Using the "tags" option with the "cache.app" adapter is deprecated. You can use the "cache.app.taggable" adapter instead (aliased to the TagAwareCacheInterface for autowiring).'); + // throw new LogicException('The "tags" option cannot be used with the "cache.app" adapter. You can use the "cache.app.taggable" adapter instead (aliased to the TagAwareCacheInterface for autowiring).'); + } + $pool['adapters'] = $pool['adapters'] ?: ['cache.app']; $isRedisTagAware = ['cache.adapter.redis_tag_aware'] === $pool['adapters']; diff --git a/Tests/DependencyInjection/Fixtures/php/cache_cacheapp_tagaware.php b/Tests/DependencyInjection/Fixtures/php/cache_cacheapp_tagaware.php new file mode 100644 index 000000000..77606f5b1 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/cache_cacheapp_tagaware.php @@ -0,0 +1,16 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'cache' => [ + 'pools' => [ + 'app.tagaware' => [ + 'adapter' => 'cache.app', + 'tags' => true, + ], + ], + ], +]); diff --git a/Tests/DependencyInjection/Fixtures/xml/cache_cacheapp_tagaware.xml b/Tests/DependencyInjection/Fixtures/xml/cache_cacheapp_tagaware.xml new file mode 100644 index 000000000..7d59e19d5 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/cache_cacheapp_tagaware.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/Tests/DependencyInjection/Fixtures/yml/cache_cacheapp_tagaware.yml b/Tests/DependencyInjection/Fixtures/yml/cache_cacheapp_tagaware.yml new file mode 100644 index 000000000..32ef3d49c --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/cache_cacheapp_tagaware.yml @@ -0,0 +1,11 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + cache: + pools: + app.tagaware: + adapter: cache.app + tags: true diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index b37d2e910..d71237c5d 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -13,6 +13,7 @@ use Psr\Cache\CacheItemPoolInterface; use Psr\Log\LoggerAwareInterface; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DummyMessage; @@ -93,6 +94,8 @@ abstract class FrameworkExtensionTestCase extends TestCase { + use ExpectDeprecationTrait; + private static array $containerCache = []; abstract protected function loadFromFile(ContainerBuilder $container, $file); @@ -1833,6 +1836,16 @@ public function testCacheTaggableTagAppliedToPools() } } + /** + * @group legacy + */ + public function testTaggableCacheAppIsDeprecated() + { + $this->expectDeprecation('Since symfony/framework-bundle 7.2: Using the "tags" option with the "cache.app" adapter is deprecated. You can use the "cache.app.taggable" adapter instead (aliased to the TagAwareCacheInterface for autowiring).'); + + $this->createContainerFromFile('cache_cacheapp_tagaware'); + } + /** * @dataProvider appRedisTagAwareConfigProvider */ From e1949dafd4796882f5b378deb01b56d9a1d91d6d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 23 Jul 2024 08:39:50 +0200 Subject: [PATCH 113/281] [FrameworkBundle] enable detailed error messages by default when debug enabled --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 4 ++++ .../DependencyInjection/ConfigurationTest.php | 24 +++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62eab0457..c9edde83a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG invokable Kernel class, and register `FrameworkBundle` by default when the `bundles.php` file is missing * [BC BREAK] The `secrets:decrypt-to-local` command terminates with a non-zero exit code when a secret could not be read * Deprecate making `cache.app` adapter taggable, use the `cache.app.taggable` adapter instead + * Enable `json_decode_detailed_errors` in the default serializer context in debug mode by default when `seld/jsonlint` is installed 7.1 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 42181d7ac..ed7aff374 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1126,6 +1126,10 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $e ->prototype('variable')->end() ->end() ->end() + ->validate() + ->ifTrue(fn ($v) => $this->debug && class_exists(JsonParser::class) && !isset($v['default_context'][JsonDecode::DETAILED_ERROR_MESSAGES])) + ->then(function ($v) { $v['default_context'][JsonDecode::DETAILED_ERROR_MESSAGES] = true; return $v; }) + ->end() ->end() ->end() ; diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index bad27f8ee..948df2cd9 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -673,6 +673,30 @@ public function testScopedHttpClientsInheritRateLimiterAndRetryFailedConfigurati $this->assertSame(999, $scopedClients['qux']['retry_failed']['delay']); } + public function testSerializerJsonDetailedErrorMessagesEnabledByDefaultWithDebugEnabled() + { + $processor = new Processor(); + $config = $processor->processConfiguration(new Configuration(true), [ + [ + 'serializer' => null, + ], + ]); + + $this->assertSame([JsonDecode::DETAILED_ERROR_MESSAGES => true], $config['serializer']['default_context'] ?? []); + } + + public function testSerializerJsonDetailedErrorMessagesNotSetByDefaultWithDebugDisabled() + { + $processor = new Processor(); + $config = $processor->processConfiguration(new Configuration(false), [ + [ + 'serializer' => null, + ], + ]); + + $this->assertSame([], $config['serializer']['default_context'] ?? []); + } + protected static function getBundleDefaultConfig() { return [ From 6a9d887df947cd3c765725c6f35dcf82741bfdb7 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 29 Jun 2024 20:46:00 +0200 Subject: [PATCH 114/281] decouple the Webhook component from the Serializer component --- DependencyInjection/FrameworkExtension.php | 15 +++++---------- Resources/config/webhook.php | 9 +++++++++ Tests/DependencyInjection/ConfigurationTest.php | 6 ++++-- .../FrameworkExtensionTestCase.php | 8 ++------ composer.json | 2 ++ 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 49821a043..e784329dd 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -548,7 +548,7 @@ public function load(array $configs, ContainerBuilder $container): void $this->registerProfilerConfiguration($config['profiler'], $container, $loader); if ($this->readConfigEnabled('webhook', $container, $config['webhook'])) { - $this->registerWebhookConfiguration($config['webhook'], $container, $loader); + $this->registerWebhookConfiguration($config['webhook'], $container, $loader, $this->readConfigEnabled('serializer', $container, $config['serializer'])); // If Webhook is installed but the HttpClient or Serializer components are not available, we should throw an error if (!$this->readConfigEnabled('http_client', $container, $config['http_client'])) { @@ -559,14 +559,6 @@ public function load(array $configs, ContainerBuilder $container): void ) ->addTag('container.error'); } - if (!$this->readConfigEnabled('serializer', $container, $config['serializer'])) { - $container->getDefinition('webhook.body_configurator.json') - ->setArguments([]) - ->addError('You cannot use the "webhook transport" service since the Serializer component is not ' - .(class_exists(Serializer::class) ? 'enabled. Try setting "framework.serializer.enabled" to true.' : 'installed. Try running "composer require symfony/serializer-pack".') - ) - ->addTag('container.error'); - } } if ($this->readConfigEnabled('remote-event', $container, $config['remote-event'])) { @@ -2919,7 +2911,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ } } - private function registerWebhookConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void + private function registerWebhookConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $serializerEnabled): void { if (!class_exists(WebhookController::class)) { throw new LogicException('Webhook support cannot be enabled as the component is not installed. Try running "composer require symfony/webhook".'); @@ -2938,6 +2930,9 @@ private function registerWebhookConfiguration(array $config, ContainerBuilder $c $controller = $container->getDefinition('webhook.controller'); $controller->replaceArgument(0, $parsers); $controller->replaceArgument(1, new Reference($config['message_bus'])); + + $jsonBodyConfigurator = $container->getDefinition('webhook.body_configurator.json'); + $jsonBodyConfigurator->replaceArgument(0, new Reference($serializerEnabled ? 'webhook.payload_serializer.serializer' : 'webhook.payload_serializer.json')); } private function registerRemoteEventConfiguration(PhpFileLoader $loader): void diff --git a/Resources/config/webhook.php b/Resources/config/webhook.php index a7e9d58ce..85cf9bb40 100644 --- a/Resources/config/webhook.php +++ b/Resources/config/webhook.php @@ -17,6 +17,8 @@ use Symfony\Component\Webhook\Server\HeadersConfigurator; use Symfony\Component\Webhook\Server\HeaderSignatureConfigurator; use Symfony\Component\Webhook\Server\JsonBodyConfigurator; +use Symfony\Component\Webhook\Server\NativeJsonPayloadSerializer; +use Symfony\Component\Webhook\Server\SerializerPayloadSerializer; use Symfony\Component\Webhook\Server\Transport; return static function (ContainerConfigurator $container) { @@ -32,6 +34,13 @@ ->set('webhook.headers_configurator', HeadersConfigurator::class) ->set('webhook.body_configurator.json', JsonBodyConfigurator::class) + ->args([ + abstract_arg('payload serializer'), + ]) + + ->set('webhook.payload_serializer.json', NativeJsonPayloadSerializer::class) + + ->set('webhook.payload_serializer.serializer', SerializerPayloadSerializer::class) ->args([ service('serializer'), ]) diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 793be0283..1aee05646 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -27,10 +27,12 @@ use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Notifier\Notifier; use Symfony\Component\RateLimiter\Policy\TokenBucketLimiter; +use Symfony\Component\RemoteEvent\RemoteEvent; use Symfony\Component\Scheduler\Messenger\SchedulerTransportFactory; use Symfony\Component\Serializer\Encoder\JsonDecode; use Symfony\Component\TypeInfo\Type; use Symfony\Component\Uid\Factory\UuidFactory; +use Symfony\Component\Webhook\Controller\WebhookController; class ConfigurationTest extends TestCase { @@ -925,12 +927,12 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor ], 'exceptions' => [], 'webhook' => [ - 'enabled' => false, + 'enabled' => !class_exists(FullStack::class) && class_exists(WebhookController::class), 'routing' => [], 'message_bus' => 'messenger.default_bus', ], 'remote-event' => [ - 'enabled' => false, + 'enabled' => !class_exists(FullStack::class) && class_exists(RemoteEvent::class), ], ]; } diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index b37d2e910..dc35d0f86 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -2357,7 +2357,7 @@ public function testWebhook() $this->assertSame(RequestParser::class, $container->getDefinition('webhook.request_parser')->getClass()); $this->assertFalse($container->getDefinition('webhook.transport')->hasErrors()); - $this->assertFalse($container->getDefinition('webhook.body_configurator.json')->hasErrors()); + $this->assertEquals('webhook.payload_serializer.serializer', $container->getDefinition('webhook.body_configurator.json')->getArgument(0)); } public function testWebhookWithoutSerializer() @@ -2369,11 +2369,7 @@ public function testWebhookWithoutSerializer() $container = $this->createContainerFromFile('webhook_without_serializer'); $this->assertFalse($container->getDefinition('webhook.transport')->hasErrors()); - $this->assertTrue($container->getDefinition('webhook.body_configurator.json')->hasErrors()); - $this->assertSame( - ['You cannot use the "webhook transport" service since the Serializer component is not enabled. Try setting "framework.serializer.enabled" to true.'], - $container->getDefinition('webhook.body_configurator.json')->getErrors() - ); + $this->assertEquals('webhook.payload_serializer.json', $container->getDefinition('webhook.body_configurator.json')->getArgument(0)); } public function testAssetMapperWithoutAssets() diff --git a/composer.json b/composer.json index af934f35d..5e27ec62c 100644 --- a/composer.json +++ b/composer.json @@ -71,6 +71,7 @@ "symfony/property-info": "^6.4|^7.0", "symfony/uid": "^6.4|^7.0", "symfony/web-link": "^6.4|^7.0", + "symfony/webhook": "^7.2", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "twig/twig": "^3.0.4" }, @@ -102,6 +103,7 @@ "symfony/twig-bundle": "<6.4", "symfony/validator": "<6.4", "symfony/web-profiler-bundle": "<6.4", + "symfony/webhook": "<7.2", "symfony/workflow": "<6.4" }, "autoload": { From 1fc8184b44eb05359dc2b25abed5ee87ecb53d39 Mon Sep 17 00:00:00 2001 From: Mathieu Santostefano Date: Mon, 17 Jun 2024 20:02:47 +0200 Subject: [PATCH 115/281] Add Sweego Mailer Bridge --- DependencyInjection/FrameworkExtension.php | 2 ++ Resources/config/mailer_transports.php | 2 ++ Resources/config/mailer_webhook.php | 7 +++++++ 3 files changed, 11 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index b773c9860..dd8a0777c 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2645,6 +2645,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co MailerBridge\Scaleway\Transport\ScalewayTransportFactory::class => 'mailer.transport_factory.scaleway', MailerBridge\Sendgrid\Transport\SendgridTransportFactory::class => 'mailer.transport_factory.sendgrid', MailerBridge\Amazon\Transport\SesTransportFactory::class => 'mailer.transport_factory.amazon', + MailerBridge\Sweego\Transport\SweegoTransportFactory::class => 'mailer.transport_factory.sweego', ]; foreach ($classToServices as $class => $service) { @@ -2665,6 +2666,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co MailerBridge\Postmark\Webhook\PostmarkRequestParser::class => 'mailer.webhook.request_parser.postmark', MailerBridge\Resend\Webhook\ResendRequestParser::class => 'mailer.webhook.request_parser.resend', MailerBridge\Sendgrid\Webhook\SendgridRequestParser::class => 'mailer.webhook.request_parser.sendgrid', + MailerBridge\Sweego\Webhook\SweegoRequestParser::class => 'mailer.webhook.request_parser.sweego', ]; foreach ($webhookRequestParsers as $class => $service) { diff --git a/Resources/config/mailer_transports.php b/Resources/config/mailer_transports.php index 34e688532..8c7131b79 100644 --- a/Resources/config/mailer_transports.php +++ b/Resources/config/mailer_transports.php @@ -27,6 +27,7 @@ use Symfony\Component\Mailer\Bridge\Resend\Transport\ResendTransportFactory; use Symfony\Component\Mailer\Bridge\Scaleway\Transport\ScalewayTransportFactory; use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory; +use Symfony\Component\Mailer\Bridge\Sweego\Transport\SweegoTransportFactory; use Symfony\Component\Mailer\Transport\AbstractTransportFactory; use Symfony\Component\Mailer\Transport\NativeTransportFactory; use Symfony\Component\Mailer\Transport\NullTransportFactory; @@ -65,6 +66,7 @@ 'sendgrid' => SendgridTransportFactory::class, 'sendmail' => SendmailTransportFactory::class, 'smtp' => EsmtpTransportFactory::class, + 'sweego' => SweegoTransportFactory::class, ]; foreach ($factories as $name => $class) { diff --git a/Resources/config/mailer_webhook.php b/Resources/config/mailer_webhook.php index 64020c1b1..f0a50800f 100644 --- a/Resources/config/mailer_webhook.php +++ b/Resources/config/mailer_webhook.php @@ -27,6 +27,8 @@ use Symfony\Component\Mailer\Bridge\Resend\Webhook\ResendRequestParser; use Symfony\Component\Mailer\Bridge\Sendgrid\RemoteEvent\SendgridPayloadConverter; use Symfony\Component\Mailer\Bridge\Sendgrid\Webhook\SendgridRequestParser; +use Symfony\Component\Mailer\Bridge\Sweego\RemoteEvent\SweegoPayloadConverter; +use Symfony\Component\Mailer\Bridge\Sweego\Webhook\SweegoRequestParser; return static function (ContainerConfigurator $container) { $container->services() @@ -69,5 +71,10 @@ ->set('mailer.webhook.request_parser.sendgrid', SendgridRequestParser::class) ->args([service('mailer.payload_converter.sendgrid')]) ->alias(SendgridRequestParser::class, 'mailer.webhook.request_parser.sendgrid') + + ->set('mailer.payload_converter.sweego', SweegoPayloadConverter::class) + ->set('mailer.webhook.request_parser.sweego', SweegoRequestParser::class) + ->args([service('mailer.payload_converter.sweego')]) + ->alias(SweegoRequestParser::class, 'mailer.webhook.request_parser.sweego') ; }; From eaad4d43143db46e08f0f80334cfe8948c49c06d Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Sat, 17 Aug 2024 09:09:39 +0200 Subject: [PATCH 116/281] [FrameworkBundle] Fix comment --- DependencyInjection/FrameworkExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index b773c9860..8f659f657 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -550,7 +550,7 @@ public function load(array $configs, ContainerBuilder $container): void if ($this->readConfigEnabled('webhook', $container, $config['webhook'])) { $this->registerWebhookConfiguration($config['webhook'], $container, $loader, $this->readConfigEnabled('serializer', $container, $config['serializer'])); - // If Webhook is installed but the HttpClient or Serializer components are not available, we should throw an error + // If Webhook is installed but the HttpClient component is not available, we should throw an error if (!$this->readConfigEnabled('http_client', $container, $config['http_client'])) { $container->getDefinition('webhook.transport') ->setArguments([]) From 4c523688dadef21a2660819f961556930121c161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Thu, 22 Aug 2024 11:18:18 +0200 Subject: [PATCH 117/281] [Serializer] Add SnakeCaseToCamelCaseNameConverter --- CHANGELOG.md | 1 + DependencyInjection/FrameworkExtension.php | 6 ++++++ Resources/config/serializer.php | 4 +++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9edde83a..193f4570c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ CHANGELOG * [BC BREAK] The `secrets:decrypt-to-local` command terminates with a non-zero exit code when a secret could not be read * Deprecate making `cache.app` adapter taggable, use the `cache.app.taggable` adapter instead * Enable `json_decode_detailed_errors` in the default serializer context in debug mode by default when `seld/jsonlint` is installed + * Register `Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter` as a service named `serializer.name_converter.snake_case_to_camel_case` if available 7.1 --- diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index a44e9a25b..d7c4403b0 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -158,6 +158,7 @@ use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader; use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; +use Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Serializer; @@ -1849,6 +1850,11 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->removeDefinition('serializer.normalizer.mime_message'); } + // BC layer Serializer < 7.2 + if (!class_exists(SnakeCaseToCamelCaseNameConverter::class)) { + $container->removeDefinition('serializer.name_converter.snake_case_to_camel_case'); + } + if ($container->getParameter('kernel.debug')) { $container->removeDefinition('serializer.mapping.cache_class_metadata_factory'); } diff --git a/Resources/config/serializer.php b/Resources/config/serializer.php index c75776900..36c8b89b5 100644 --- a/Resources/config/serializer.php +++ b/Resources/config/serializer.php @@ -31,6 +31,7 @@ use Symfony\Component\Serializer\Mapping\Loader\LoaderChain; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter; +use Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter; use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer; use Symfony\Component\Serializer\Normalizer\ConstraintViolationListNormalizer; @@ -186,8 +187,9 @@ ->set('serializer.encoder.csv', CsvEncoder::class) ->tag('serializer.encoder') - // Name converter + // Name converters ->set('serializer.name_converter.camel_case_to_snake_case', CamelCaseToSnakeCaseNameConverter::class) + ->set('serializer.name_converter.snake_case_to_camel_case', SnakeCaseToCamelCaseNameConverter::class) ->set('serializer.name_converter.metadata_aware', MetadataAwareNameConverter::class) ->args([service('serializer.mapping.class_metadata_factory')]) From 4d9f5d3c52135246aa5a96452c9f7e43f3f54783 Mon Sep 17 00:00:00 2001 From: Ahmed Ghanem Date: Mon, 8 Jul 2024 01:50:11 +0300 Subject: [PATCH 118/281] [Notifier] Support for desktop notifications via `jolicode/JoliNotif` --- DependencyInjection/FrameworkExtension.php | 2 ++ Resources/config/notifier.php | 10 ++++++++++ Resources/config/notifier_transports.php | 1 + 3 files changed, 13 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index d7c4403b0..75cfe1d1f 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2766,6 +2766,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ } $container->getDefinition('notifier.channel.sms')->setArgument(0, null); $container->getDefinition('notifier.channel.push')->setArgument(0, null); + $container->getDefinition('notifier.channel.desktop')->setArgument(0, null); } $container->getDefinition('notifier.channel_policy')->setArgument(0, $config['channel_policy']); @@ -2801,6 +2802,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\Infobip\InfobipTransportFactory::class => 'notifier.transport_factory.infobip', NotifierBridge\Iqsms\IqsmsTransportFactory::class => 'notifier.transport_factory.iqsms', NotifierBridge\Isendpro\IsendproTransportFactory::class => 'notifier.transport_factory.isendpro', + NotifierBridge\JoliNotif\JoliNotifTransportFactory::class => 'notifier.transport_factory.joli-notif', NotifierBridge\KazInfoTeh\KazInfoTehTransportFactory::class => 'notifier.transport_factory.kaz-info-teh', NotifierBridge\LightSms\LightSmsTransportFactory::class => 'notifier.transport_factory.light-sms', NotifierBridge\LineNotify\LineNotifyTransportFactory::class => 'notifier.transport_factory.line-notify', diff --git a/Resources/config/notifier.php b/Resources/config/notifier.php index 3bd19b8dd..95a4d12e9 100644 --- a/Resources/config/notifier.php +++ b/Resources/config/notifier.php @@ -15,6 +15,7 @@ use Symfony\Component\Notifier\Channel\BrowserChannel; use Symfony\Component\Notifier\Channel\ChannelPolicy; use Symfony\Component\Notifier\Channel\ChatChannel; +use Symfony\Component\Notifier\Channel\DesktopChannel; use Symfony\Component\Notifier\Channel\EmailChannel; use Symfony\Component\Notifier\Channel\PushChannel; use Symfony\Component\Notifier\Channel\SmsChannel; @@ -24,6 +25,7 @@ use Symfony\Component\Notifier\EventListener\SendFailedMessageToNotifierListener; use Symfony\Component\Notifier\FlashMessage\DefaultFlashMessageImportanceMapper; use Symfony\Component\Notifier\Message\ChatMessage; +use Symfony\Component\Notifier\Message\DesktopMessage; use Symfony\Component\Notifier\Message\PushMessage; use Symfony\Component\Notifier\Message\SmsMessage; use Symfony\Component\Notifier\Messenger\MessageHandler; @@ -76,6 +78,10 @@ ->args([service('texter.transports'), service('messenger.default_bus')->ignoreOnInvalid()]) ->tag('notifier.channel', ['channel' => 'push']) + ->set('notifier.channel.desktop', DesktopChannel::class) + ->args([service('texter.transports'), service('messenger.default_bus')->ignoreOnInvalid()]) + ->tag('notifier.channel', ['channel' => 'desktop']) + ->set('notifier.monolog_handler', NotifierHandler::class) ->args([service('notifier')]) @@ -126,6 +132,10 @@ ->args([service('texter.transports')]) ->tag('messenger.message_handler', ['handles' => PushMessage::class]) + ->set('texter.messenger.desktop_handler', MessageHandler::class) + ->args([service('texter.transports')]) + ->tag('messenger.message_handler', ['handles' => DesktopMessage::class]) + ->set('notifier.notification_logger_listener', NotificationLoggerListener::class) ->tag('kernel.event_subscriber') diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index a773899f7..7c8002993 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -72,6 +72,7 @@ 'infobip' => Bridge\Infobip\InfobipTransportFactory::class, 'iqsms' => Bridge\Iqsms\IqsmsTransportFactory::class, 'isendpro' => Bridge\Isendpro\IsendproTransportFactory::class, + 'joli-notif' => Bridge\JoliNotif\JoliNotifTransportFactory::class, 'kaz-info-teh' => Bridge\KazInfoTeh\KazInfoTehTransportFactory::class, 'light-sms' => Bridge\LightSms\LightSmsTransportFactory::class, 'lox24' => Bridge\Lox24\Lox24TransportFactory::class, From bfd6ef6c982ac04c5b786a4b636088464687e767 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 27 Aug 2024 09:19:01 +0200 Subject: [PATCH 119/281] [FrameworkBundle] Fix low-deps tests --- Resources/config/notifier.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Resources/config/notifier.php b/Resources/config/notifier.php index 95a4d12e9..04fa4ce51 100644 --- a/Resources/config/notifier.php +++ b/Resources/config/notifier.php @@ -132,12 +132,14 @@ ->args([service('texter.transports')]) ->tag('messenger.message_handler', ['handles' => PushMessage::class]) - ->set('texter.messenger.desktop_handler', MessageHandler::class) - ->args([service('texter.transports')]) - ->tag('messenger.message_handler', ['handles' => DesktopMessage::class]) - ->set('notifier.notification_logger_listener', NotificationLoggerListener::class) ->tag('kernel.event_subscriber') - ; + + if (class_exists(DesktopMessage::class)) { + $container->services() + ->set('texter.messenger.desktop_handler', MessageHandler::class) + ->args([service('texter.transports')]) + ->tag('messenger.message_handler', ['handles' => DesktopMessage::class]); + } }; From a63613f379dd5261113be7abd51de74ca826248f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 29 Aug 2024 10:25:47 +0200 Subject: [PATCH 120/281] bump requirement for Twig to 3.12+ --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5e27ec62c..50672edb9 100644 --- a/composer.json +++ b/composer.json @@ -73,7 +73,7 @@ "symfony/web-link": "^6.4|^7.0", "symfony/webhook": "^7.2", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "twig/twig": "^3.0.4" + "twig/twig": "^3.12" }, "conflict": { "doctrine/persistence": "<1.3", From d2b3f073884f675f748b49fac2fb8234557832f6 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sat, 31 Aug 2024 00:31:12 +0200 Subject: [PATCH 121/281] CS: re-apply `trailing_comma_in_multiline` --- .../php/messenger_multiple_failure_transports.php | 6 +++--- .../php/messenger_multiple_failure_transports_global.php | 4 ++-- .../Fixtures/php/messenger_transports.php | 2 +- Tests/DependencyInjection/Fixtures/php/notifier.php | 8 ++++---- .../Fixtures/php/notifier_with_disabled_message_bus.php | 4 ++-- .../Fixtures/php/notifier_with_specific_message_bus.php | 4 ++-- .../Fixtures/php/notifier_without_mailer.php | 6 +++--- .../Fixtures/php/notifier_without_messenger.php | 6 +++--- Tests/DependencyInjection/Fixtures/php/profiler.php | 2 +- .../Fixtures/php/profiler_collect_serializer_data.php | 2 +- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Tests/DependencyInjection/Fixtures/php/messenger_multiple_failure_transports.php b/Tests/DependencyInjection/Fixtures/php/messenger_multiple_failure_transports.php index 88a4a8073..1fa698076 100644 --- a/Tests/DependencyInjection/Fixtures/php/messenger_multiple_failure_transports.php +++ b/Tests/DependencyInjection/Fixtures/php/messenger_multiple_failure_transports.php @@ -9,15 +9,15 @@ 'transports' => [ 'transport_1' => [ 'dsn' => 'null://', - 'failure_transport' => 'failure_transport_1' + 'failure_transport' => 'failure_transport_1', ], 'transport_2' => 'null://', 'transport_3' => [ 'dsn' => 'null://', - 'failure_transport' => 'failure_transport_3' + 'failure_transport' => 'failure_transport_3', ], 'failure_transport_1' => 'null://', - 'failure_transport_3' => 'null://' + 'failure_transport_3' => 'null://', ], ], ]); diff --git a/Tests/DependencyInjection/Fixtures/php/messenger_multiple_failure_transports_global.php b/Tests/DependencyInjection/Fixtures/php/messenger_multiple_failure_transports_global.php index 9f794556b..763db88a8 100644 --- a/Tests/DependencyInjection/Fixtures/php/messenger_multiple_failure_transports_global.php +++ b/Tests/DependencyInjection/Fixtures/php/messenger_multiple_failure_transports_global.php @@ -10,12 +10,12 @@ 'transports' => [ 'transport_1' => [ 'dsn' => 'null://', - 'failure_transport' => 'failure_transport_1' + 'failure_transport' => 'failure_transport_1', ], 'transport_2' => 'null://', 'transport_3' => [ 'dsn' => 'null://', - 'failure_transport' => 'failure_transport_3' + 'failure_transport' => 'failure_transport_3', ], 'failure_transport_global' => 'null://', 'failure_transport_1' => 'null://', diff --git a/Tests/DependencyInjection/Fixtures/php/messenger_transports.php b/Tests/DependencyInjection/Fixtures/php/messenger_transports.php index bba32ce0b..a010da534 100644 --- a/Tests/DependencyInjection/Fixtures/php/messenger_transports.php +++ b/Tests/DependencyInjection/Fixtures/php/messenger_transports.php @@ -23,7 +23,7 @@ 'multiplier' => 3, 'max_delay' => 100, ], - 'rate_limiter' => 'customised_worker' + 'rate_limiter' => 'customised_worker', ], 'failed' => 'in-memory:///', 'redis' => 'redis://127.0.0.1:6379/messages', diff --git a/Tests/DependencyInjection/Fixtures/php/notifier.php b/Tests/DependencyInjection/Fixtures/php/notifier.php index 0def62cac..9abf2c880 100644 --- a/Tests/DependencyInjection/Fixtures/php/notifier.php +++ b/Tests/DependencyInjection/Fixtures/php/notifier.php @@ -9,7 +9,7 @@ 'handle_all_throwables' => true, 'php_errors' => ['log' => true], 'messenger' => [ - 'enabled' => true + 'enabled' => true, ], 'mailer' => [ 'dsn' => 'smtp://example.com', @@ -18,10 +18,10 @@ 'enabled' => true, 'notification_on_failed_messages' => true, 'chatter_transports' => [ - 'slack' => 'null' + 'slack' => 'null', ], 'texter_transports' => [ - 'twilio' => 'null' + 'twilio' => 'null', ], 'channel_policy' => [ 'low' => ['slack'], @@ -29,6 +29,6 @@ ], 'admin_recipients' => [ ['email' => 'test@test.de', 'phone' => '+490815',], - ] + ], ], ]); diff --git a/Tests/DependencyInjection/Fixtures/php/notifier_with_disabled_message_bus.php b/Tests/DependencyInjection/Fixtures/php/notifier_with_disabled_message_bus.php index 6b9b4ff07..8c6b2f002 100644 --- a/Tests/DependencyInjection/Fixtures/php/notifier_with_disabled_message_bus.php +++ b/Tests/DependencyInjection/Fixtures/php/notifier_with_disabled_message_bus.php @@ -14,10 +14,10 @@ 'notifier' => [ 'message_bus' => false, 'chatter_transports' => [ - 'test' => 'null' + 'test' => 'null', ], 'texter_transports' => [ - 'test' => 'null' + 'test' => 'null', ], ], ]); diff --git a/Tests/DependencyInjection/Fixtures/php/notifier_with_specific_message_bus.php b/Tests/DependencyInjection/Fixtures/php/notifier_with_specific_message_bus.php index f8b4ad66d..4c38323bd 100644 --- a/Tests/DependencyInjection/Fixtures/php/notifier_with_specific_message_bus.php +++ b/Tests/DependencyInjection/Fixtures/php/notifier_with_specific_message_bus.php @@ -14,10 +14,10 @@ 'notifier' => [ 'message_bus' => 'app.another_bus', 'chatter_transports' => [ - 'test' => 'null' + 'test' => 'null', ], 'texter_transports' => [ - 'test' => 'null' + 'test' => 'null', ], ], ]); diff --git a/Tests/DependencyInjection/Fixtures/php/notifier_without_mailer.php b/Tests/DependencyInjection/Fixtures/php/notifier_without_mailer.php index 5967dcb9e..fbb988527 100644 --- a/Tests/DependencyInjection/Fixtures/php/notifier_without_mailer.php +++ b/Tests/DependencyInjection/Fixtures/php/notifier_without_mailer.php @@ -18,10 +18,10 @@ 'enabled' => true, 'notification_on_failed_messages' => true, 'chatter_transports' => [ - 'slack' => 'null' + 'slack' => 'null', ], 'texter_transports' => [ - 'twilio' => 'null' + 'twilio' => 'null', ], 'channel_policy' => [ 'low' => ['slack'], @@ -29,6 +29,6 @@ ], 'admin_recipients' => [ ['email' => 'test@test.de', 'phone' => '+490815',], - ] + ], ], ]); diff --git a/Tests/DependencyInjection/Fixtures/php/notifier_without_messenger.php b/Tests/DependencyInjection/Fixtures/php/notifier_without_messenger.php index 4a477e008..454f8ccec 100644 --- a/Tests/DependencyInjection/Fixtures/php/notifier_without_messenger.php +++ b/Tests/DependencyInjection/Fixtures/php/notifier_without_messenger.php @@ -18,10 +18,10 @@ 'enabled' => true, 'notification_on_failed_messages' => true, 'chatter_transports' => [ - 'slack' => 'null' + 'slack' => 'null', ], 'texter_transports' => [ - 'twilio' => 'null' + 'twilio' => 'null', ], 'channel_policy' => [ 'low' => ['slack'], @@ -29,7 +29,7 @@ ], 'admin_recipients' => [ ['email' => 'test@test.de', 'phone' => '+490815',], - ] + ], ], 'scheduler' => false, ]); diff --git a/Tests/DependencyInjection/Fixtures/php/profiler.php b/Tests/DependencyInjection/Fixtures/php/profiler.php index 43a7a002c..faf76bbc7 100644 --- a/Tests/DependencyInjection/Fixtures/php/profiler.php +++ b/Tests/DependencyInjection/Fixtures/php/profiler.php @@ -9,6 +9,6 @@ 'enabled' => true, ], 'serializer' => [ - 'enabled' => true + 'enabled' => true, ], ]); diff --git a/Tests/DependencyInjection/Fixtures/php/profiler_collect_serializer_data.php b/Tests/DependencyInjection/Fixtures/php/profiler_collect_serializer_data.php index 1fb869a80..99e2a52cf 100644 --- a/Tests/DependencyInjection/Fixtures/php/profiler_collect_serializer_data.php +++ b/Tests/DependencyInjection/Fixtures/php/profiler_collect_serializer_data.php @@ -11,5 +11,5 @@ ], 'serializer' => [ 'enabled' => true, - ] + ], ]); From 78e8f4344088f60d92120b1e48bad93e144cf251 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 2 Sep 2024 14:07:48 +0200 Subject: [PATCH 122/281] Fix configurations info consistency --- DependencyInjection/Configuration.php | 120 +++++++++++++------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index ed7aff374..bbdfb1e03 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -85,7 +85,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->children() ->scalarNode('secret')->end() ->booleanNode('http_method_override') - ->info("Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. Note: When using the HttpCache, you need to call the method in your front controller instead") + ->info("Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. Note: When using the HttpCache, you need to call the method in your front controller instead.") ->defaultFalse() ->end() ->scalarNode('trust_x_sendfile_type_header') @@ -132,7 +132,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->scalarNode('error_controller') ->defaultValue('error_controller') ->end() - ->booleanNode('handle_all_throwables')->info('HttpKernel will handle all kinds of \Throwable')->defaultTrue()->end() + ->booleanNode('handle_all_throwables')->info('HttpKernel will handle all kinds of \Throwable.')->defaultTrue()->end() ->end() ; @@ -226,7 +226,7 @@ private function addFormSection(ArrayNodeDefinition $rootNode, callable $enableI $rootNode ->children() ->arrayNode('form') - ->info('form configuration') + ->info('Form configuration') ->{$enableIfStandalone('symfony/form', Form::class)}() ->children() ->arrayNode('csrf_protection') @@ -284,7 +284,7 @@ private function addEsiSection(ArrayNodeDefinition $rootNode): void $rootNode ->children() ->arrayNode('esi') - ->info('esi configuration') + ->info('ESI configuration') ->canBeEnabled() ->end() ->end() @@ -296,7 +296,7 @@ private function addSsiSection(ArrayNodeDefinition $rootNode): void $rootNode ->children() ->arrayNode('ssi') - ->info('ssi configuration') + ->info('SSI configuration') ->canBeEnabled() ->end() ->end(); @@ -307,7 +307,7 @@ private function addFragmentsSection(ArrayNodeDefinition $rootNode): void $rootNode ->children() ->arrayNode('fragments') - ->info('fragments configuration') + ->info('Fragments configuration') ->canBeEnabled() ->children() ->scalarNode('hinclude_default_template')->defaultNull()->end() @@ -323,15 +323,15 @@ private function addProfilerSection(ArrayNodeDefinition $rootNode): void $rootNode ->children() ->arrayNode('profiler') - ->info('profiler configuration') + ->info('Profiler configuration') ->canBeEnabled() ->children() ->booleanNode('collect')->defaultTrue()->end() - ->scalarNode('collect_parameter')->defaultNull()->info('The name of the parameter to use to enable or disable collection on a per request basis')->end() + ->scalarNode('collect_parameter')->defaultNull()->info('The name of the parameter to use to enable or disable collection on a per request basis.')->end() ->booleanNode('only_exceptions')->defaultFalse()->end() ->booleanNode('only_main_requests')->defaultFalse()->end() ->scalarNode('dsn')->defaultValue('file:%kernel.cache_dir%/profiler')->end() - ->booleanNode('collect_serializer_data')->info('Enables the serializer data collector and profiler panel')->defaultFalse()->end() + ->booleanNode('collect_serializer_data')->info('Enables the serializer data collector and profiler panel.')->defaultFalse()->end() ->end() ->end() ->end() @@ -450,7 +450,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void }) ->thenInvalid('The value must be "null" or an array of workflow events (like ["workflow.enter"]).') ->end() - ->info('Select which Transition events should be dispatched for this Workflow') + ->info('Select which Transition events should be dispatched for this Workflow.') ->example(['workflow.enter', 'workflow.transition']) ->end() ->arrayNode('places') @@ -534,7 +534,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->end() ->scalarNode('guard') ->cannotBeEmpty() - ->info('An expression to block the transition') + ->info('An expression to block the transition.') ->example('is_fully_authenticated() and is_granted(\'ROLE_JOURNALIST\') and subject.getTitle() == \'My first article\'') ->end() ->arrayNode('from') @@ -612,7 +612,7 @@ private function addRouterSection(ArrayNodeDefinition $rootNode): void $rootNode ->children() ->arrayNode('router') - ->info('router configuration') + ->info('Router configuration') ->canBeEnabled() ->children() ->scalarNode('resource')->isRequired()->end() @@ -622,7 +622,7 @@ private function addRouterSection(ArrayNodeDefinition $rootNode): void ->setDeprecated('symfony/framework-bundle', '7.1', 'Setting the "%path%.%node%" configuration option is deprecated. It will be removed in version 8.0.') ->end() ->scalarNode('default_uri') - ->info('The default URI used to generate URLs in a non-HTTP context') + ->info('The default URI used to generate URLs in a non-HTTP context.') ->defaultNull() ->end() ->scalarNode('http_port')->defaultValue(80)->end() @@ -648,7 +648,7 @@ private function addSessionSection(ArrayNodeDefinition $rootNode): void $rootNode ->children() ->arrayNode('session') - ->info('session configuration') + ->info('Session configuration') ->canBeEnabled() ->children() ->scalarNode('storage_factory_id')->defaultValue('session.storage.factory.native')->end() @@ -676,11 +676,11 @@ private function addSessionSection(ArrayNodeDefinition $rootNode): void ->scalarNode('gc_probability')->defaultValue(1)->end() ->scalarNode('gc_maxlifetime')->end() ->scalarNode('save_path') - ->info('Defaults to "%kernel.cache_dir%/sessions" if the "handler_id" option is not null') + ->info('Defaults to "%kernel.cache_dir%/sessions" if the "handler_id" option is not null.') ->end() ->integerNode('metadata_update_threshold') ->defaultValue(0) - ->info('seconds to wait between 2 session metadata updates') + ->info('Seconds to wait between 2 session metadata updates.') ->end() ->integerNode('sid_length') ->min(22) @@ -701,7 +701,7 @@ private function addRequestSection(ArrayNodeDefinition $rootNode): void $rootNode ->children() ->arrayNode('request') - ->info('request configuration') + ->info('Request configuration') ->canBeEnabled() ->fixXmlConfig('format') ->children() @@ -727,12 +727,12 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode, callable $enabl $rootNode ->children() ->arrayNode('assets') - ->info('assets configuration') + ->info('Assets configuration') ->{$enableIfStandalone('symfony/asset', Package::class)}() ->fixXmlConfig('base_url') ->children() ->booleanNode('strict_mode') - ->info('Throw an exception if an entry is missing from the manifest.json') + ->info('Throw an exception if an entry is missing from the manifest.json.') ->defaultFalse() ->end() ->scalarNode('version_strategy')->defaultNull()->end() @@ -773,7 +773,7 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode, callable $enabl ->fixXmlConfig('base_url') ->children() ->booleanNode('strict_mode') - ->info('Throw an exception if an entry is missing from the manifest.json') + ->info('Throw an exception if an entry is missing from the manifest.json.') ->defaultFalse() ->end() ->scalarNode('version_strategy')->defaultNull()->end() @@ -832,7 +832,7 @@ private function addAssetMapperSection(ArrayNodeDefinition $rootNode, callable $ ->children() // add array node called "paths" that will be an array of strings ->arrayNode('paths') - ->info('Directories that hold assets that should be in the mapper. Can be a simple array of an array of ["path/to/assets": "namespace"]') + ->info('Directories that hold assets that should be in the mapper. Can be a simple array of an array of ["path/to/assets": "namespace"].') ->example(['assets/']) ->normalizeKeys(false) ->useAttributeAsKey('namespace') @@ -863,21 +863,21 @@ private function addAssetMapperSection(ArrayNodeDefinition $rootNode, callable $ ->prototype('scalar')->end() ->end() ->arrayNode('excluded_patterns') - ->info('Array of glob patterns of asset file paths that should not be in the asset mapper') + ->info('Array of glob patterns of asset file paths that should not be in the asset mapper.') ->prototype('scalar')->end() ->example(['*/assets/build/*', '*/*_.scss']) ->end() // boolean called defaulting to true ->booleanNode('exclude_dotfiles') - ->info('If true, any files starting with "." will be excluded from the asset mapper') + ->info('If true, any files starting with "." will be excluded from the asset mapper.') ->defaultTrue() ->end() ->booleanNode('server') - ->info('If true, a "dev server" will return the assets from the public directory (true in "debug" mode only by default)') + ->info('If true, a "dev server" will return the assets from the public directory (true in "debug" mode only by default).') ->defaultValue($this->debug) ->end() ->scalarNode('public_prefix') - ->info('The public path where the assets will be written to (and served from when "server" is true)') + ->info('The public path where the assets will be written to (and served from when "server" is true).') ->defaultValue('/assets/') ->end() ->enumNode('missing_import_mode') @@ -926,7 +926,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e $rootNode ->children() ->arrayNode('translator') - ->info('translator configuration') + ->info('Translator configuration') ->{$enableIfStandalone('symfony/translation', Translator::class)}() ->fixXmlConfig('fallback') ->fixXmlConfig('path') @@ -942,7 +942,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e ->scalarNode('formatter')->defaultValue('translator.formatter.default')->end() ->scalarNode('cache_dir')->defaultValue('%kernel.cache_dir%/translations')->end() ->scalarNode('default_path') - ->info('The default path used to load translations') + ->info('The default path used to load translations.') ->defaultValue('%kernel.project_dir%/translations') ->end() ->arrayNode('paths') @@ -965,7 +965,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e ->end() ->end() ->arrayNode('providers') - ->info('Translation providers you can read/write your translations from') + ->info('Translation providers you can read/write your translations from.') ->useAttributeAsKey('name') ->prototype('array') ->fixXmlConfig('domain') @@ -996,7 +996,7 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e $rootNode ->children() ->arrayNode('validation') - ->info('validation configuration') + ->info('Validation configuration') ->{$enableIfStandalone('symfony/validator', Validation::class)}() ->children() ->scalarNode('cache')->end() @@ -1100,7 +1100,7 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $e $rootNode ->children() ->arrayNode('serializer') - ->info('serializer configuration') + ->info('Serializer configuration') ->{$enableIfStandalone('symfony/serializer', Serializer::class)}() ->children() ->booleanNode('enable_attributes')->{class_exists(FullStack::class) ? 'defaultFalse' : 'defaultTrue'}()->end() @@ -1189,16 +1189,16 @@ private function addCacheSection(ArrayNodeDefinition $rootNode, callable $willBe ->fixXmlConfig('pool') ->children() ->scalarNode('prefix_seed') - ->info('Used to namespace cache keys when using several apps with the same shared backend') + ->info('Used to namespace cache keys when using several apps with the same shared backend.') ->defaultValue('_%kernel.project_dir%.%kernel.container_class%') ->example('my-application-name/%kernel.environment%') ->end() ->scalarNode('app') - ->info('App related cache pools configuration') + ->info('App related cache pools configuration.') ->defaultValue('cache.adapter.filesystem') ->end() ->scalarNode('system') - ->info('System related cache pools configuration') + ->info('System related cache pools configuration.') ->defaultValue('cache.adapter.system') ->end() ->scalarNode('directory')->defaultValue('%kernel.cache_dir%/pools/app')->end() @@ -1247,7 +1247,7 @@ private function addCacheSection(ArrayNodeDefinition $rootNode, callable $willBe ->scalarNode('tags')->defaultNull()->end() ->booleanNode('public')->defaultFalse()->end() ->scalarNode('default_lifetime') - ->info('Default lifetime of the pool') + ->info('Default lifetime of the pool.') ->example('"300" for 5 minutes expressed in seconds, "PT5M" for five minutes expressed as ISO 8601 time interval, or "5 minutes" as a date expression') ->end() ->scalarNode('provider') @@ -1478,7 +1478,7 @@ private function addWebLinkSection(ArrayNodeDefinition $rootNode, callable $enab $rootNode ->children() ->arrayNode('web_link') - ->info('web links configuration') + ->info('Web links configuration') ->{$enableIfStandalone('symfony/weblink', HttpHeaderSerializer::class)}() ->end() ->end() @@ -1604,17 +1604,17 @@ function ($a) { }) ->end() ->children() - ->scalarNode('service')->defaultNull()->info('Service id to override the retry strategy entirely')->end() + ->scalarNode('service')->defaultNull()->info('Service id to override the retry strategy entirely.')->end() ->integerNode('max_retries')->defaultValue(3)->min(0)->end() - ->integerNode('delay')->defaultValue(1000)->min(0)->info('Time in ms to delay (or the initial value when multiplier is used)')->end() - ->floatNode('multiplier')->defaultValue(2)->min(1)->info('If greater than 1, delay will grow exponentially for each retry: this delay = (delay * (multiple ^ retries))')->end() - ->integerNode('max_delay')->defaultValue(0)->min(0)->info('Max time in ms that a retry should ever be delayed (0 = infinite)')->end() - ->floatNode('jitter')->defaultValue(0.1)->min(0)->max(1)->info('Randomness to apply to the delay (between 0 and 1)')->end() + ->integerNode('delay')->defaultValue(1000)->min(0)->info('Time in ms to delay (or the initial value when multiplier is used).')->end() + ->floatNode('multiplier')->defaultValue(2)->min(1)->info('If greater than 1, delay will grow exponentially for each retry: this delay = (delay * (multiple ^ retries)).')->end() + ->integerNode('max_delay')->defaultValue(0)->min(0)->info('Max time in ms that a retry should ever be delayed (0 = infinite).')->end() + ->floatNode('jitter')->defaultValue(0.1)->min(0)->max(1)->info('Randomness to apply to the delay (between 0 and 1).')->end() ->end() ->end() ->scalarNode('rate_limiter') ->defaultNull() - ->info('Rate limiter name to use when processing messages') + ->info('Rate limiter name to use when processing messages.') ->end() ->end() ->end() @@ -1859,13 +1859,13 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e ->info('The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants.') ->end() ->arrayNode('extra') - ->info('Extra options for specific HTTP client') + ->info('Extra options for specific HTTP client.') ->normalizeKeys(false) ->variablePrototype()->end() ->end() ->scalarNode('rate_limiter') ->defaultNull() - ->info('Rate limiter name to use for throttling requests') + ->info('Rate limiter name to use for throttling requests.') ->end() ->append($this->createHttpClientRetrySection()) ->end() @@ -1999,7 +1999,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e ->info('The passphrase used to encrypt the "local_pk" file.') ->end() ->scalarNode('ciphers') - ->info('A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)') + ->info('A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...).') ->end() ->arrayNode('peer_fingerprint') ->info('Associative array: hashing algorithm => hash(es).') @@ -2011,13 +2011,13 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e ->end() ->end() ->arrayNode('extra') - ->info('Extra options for specific HTTP client') + ->info('Extra options for specific HTTP client.') ->normalizeKeys(false) ->variablePrototype()->end() ->end() ->scalarNode('rate_limiter') ->defaultNull() - ->info('Rate limiter name to use for throttling requests') + ->info('Rate limiter name to use for throttling requests.') ->end() ->append($this->createHttpClientRetrySection()) ->end() @@ -2048,7 +2048,7 @@ private function createHttpClientRetrySection(): ArrayNodeDefinition }) ->end() ->children() - ->scalarNode('retry_strategy')->defaultNull()->info('service id to override the retry strategy')->end() + ->scalarNode('retry_strategy')->defaultNull()->info('service id to override the retry strategy.')->end() ->arrayNode('http_codes') ->performNoDeepMerging() ->beforeNormalization() @@ -2083,17 +2083,17 @@ private function createHttpClientRetrySection(): ArrayNodeDefinition ->then(fn ($v) => array_map('strtoupper', $v)) ->end() ->prototype('scalar')->end() - ->info('A list of HTTP methods that triggers a retry for this status code. When empty, all methods are retried') + ->info('A list of HTTP methods that triggers a retry for this status code. When empty, all methods are retried.') ->end() ->end() ->end() - ->info('A list of HTTP status code that triggers a retry') + ->info('A list of HTTP status code that triggers a retry.') ->end() ->integerNode('max_retries')->defaultValue(3)->min(0)->end() - ->integerNode('delay')->defaultValue(1000)->min(0)->info('Time in ms to delay (or the initial value when multiplier is used)')->end() - ->floatNode('multiplier')->defaultValue(2)->min(1)->info('If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries)')->end() - ->integerNode('max_delay')->defaultValue(0)->min(0)->info('Max time in ms that a retry should ever be delayed (0 = infinite)')->end() - ->floatNode('jitter')->defaultValue(0.1)->min(0)->max(1)->info('Randomness in percent (between 0 and 1) to apply to the delay')->end() + ->integerNode('delay')->defaultValue(1000)->min(0)->info('Time in ms to delay (or the initial value when multiplier is used).')->end() + ->floatNode('multiplier')->defaultValue(2)->min(1)->info('If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries).')->end() + ->integerNode('max_delay')->defaultValue(0)->min(0)->info('Max time in ms that a retry should ever be delayed (0 = infinite).')->end() + ->floatNode('jitter')->defaultValue(0.1)->min(0)->max(1)->info('Randomness in percent (between 0 and 1) to apply to the delay.')->end() ->end() ; } @@ -2284,35 +2284,35 @@ private function addRateLimiterSection(ArrayNodeDefinition $rootNode, callable $ ->arrayPrototype() ->children() ->scalarNode('lock_factory') - ->info('The service ID of the lock factory used by this limiter (or null to disable locking)') + ->info('The service ID of the lock factory used by this limiter (or null to disable locking).') ->defaultValue('lock.factory') ->end() ->scalarNode('cache_pool') - ->info('The cache pool to use for storing the current limiter state') + ->info('The cache pool to use for storing the current limiter state.') ->defaultValue('cache.rate_limiter') ->end() ->scalarNode('storage_service') - ->info('The service ID of a custom storage implementation, this precedes any configured "cache_pool"') + ->info('The service ID of a custom storage implementation, this precedes any configured "cache_pool".') ->defaultNull() ->end() ->enumNode('policy') - ->info('The algorithm to be used by this limiter') + ->info('The algorithm to be used by this limiter.') ->isRequired() ->values(['fixed_window', 'token_bucket', 'sliding_window', 'no_limit']) ->end() ->integerNode('limit') - ->info('The maximum allowed hits in a fixed interval or burst') + ->info('The maximum allowed hits in a fixed interval or burst.') ->end() ->scalarNode('interval') ->info('Configures the fixed interval if "policy" is set to "fixed_window" or "sliding_window". The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent).') ->end() ->arrayNode('rate') - ->info('Configures the fill rate if "policy" is set to "token_bucket"') + ->info('Configures the fill rate if "policy" is set to "token_bucket".') ->children() ->scalarNode('interval') ->info('Configures the rate interval. The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent).') ->end() - ->integerNode('amount')->info('Amount of tokens to add each interval')->defaultValue(1)->end() + ->integerNode('amount')->info('Amount of tokens to add each interval.')->defaultValue(1)->end() ->end() ->end() ->end() From e3c09d429955e750a0746e4ed0905b47f4f5b559 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 30 Aug 2024 09:35:32 +0200 Subject: [PATCH 123/281] allow Twig 4 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 50672edb9..a6552ca5b 100644 --- a/composer.json +++ b/composer.json @@ -73,7 +73,7 @@ "symfony/web-link": "^6.4|^7.0", "symfony/webhook": "^7.2", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "twig/twig": "^3.12" + "twig/twig": "^3.12|^4.0" }, "conflict": { "doctrine/persistence": "<1.3", From f7b477a510628b22733e4a454e34c5ab3d2d3b0c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 3 Sep 2024 10:21:02 +0200 Subject: [PATCH 124/281] [HttpFoundation] Add `PRIVATE_SUBNETS` as a shortcut for private IP address ranges to `Request::setTrustedProxies()` --- DependencyInjection/Configuration.php | 6 +++--- Tests/DependencyInjection/FrameworkExtensionTestCase.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index ed7aff374..44bbe6718 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -111,10 +111,10 @@ public function getConfigTreeBuilder(): TreeBuilder ->beforeNormalization()->ifString()->then(fn ($v) => [$v])->end() ->prototype('scalar')->end() ->end() - ->scalarNode('trusted_proxies') + ->variableNode('trusted_proxies') ->beforeNormalization() - ->ifTrue(fn ($v) => 'private_ranges' === $v) - ->then(fn ($v) => implode(',', IpUtils::PRIVATE_SUBNETS)) + ->ifTrue(fn ($v) => 'private_ranges' === $v || 'PRIVATE_SUBNETS' === $v) + ->then(fn () => IpUtils::PRIVATE_SUBNETS) ->end() ->end() ->arrayNode('trusted_headers') diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 4b7431fc5..a6da61ae3 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -2354,7 +2354,7 @@ public function testTrustedProxiesWithPrivateRanges() { $container = $this->createContainerFromFile('trusted_proxies_private_ranges'); - $this->assertSame(IpUtils::PRIVATE_SUBNETS, array_map('trim', explode(',', $container->getParameter('kernel.trusted_proxies')))); + $this->assertSame(IpUtils::PRIVATE_SUBNETS, $container->getParameter('kernel.trusted_proxies')); } public function testWebhook() From 8735303b4c22ac46736f168d451e9c5182063f98 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 3 Sep 2024 19:21:15 +0200 Subject: [PATCH 125/281] [FrameworkBundle] Remove default value for gc_probability config option --- DependencyInjection/Configuration.php | 2 +- Tests/DependencyInjection/ConfigurationTest.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 44bbe6718..aef052eab 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -673,7 +673,7 @@ private function addSessionSection(ArrayNodeDefinition $rootNode): void ->enumNode('cookie_samesite')->values([null, Cookie::SAMESITE_LAX, Cookie::SAMESITE_STRICT, Cookie::SAMESITE_NONE])->defaultValue('lax')->end() ->booleanNode('use_cookies')->end() ->scalarNode('gc_divisor')->end() - ->scalarNode('gc_probability')->defaultValue(1)->end() + ->scalarNode('gc_probability')->end() ->scalarNode('gc_maxlifetime')->end() ->scalarNode('save_path') ->info('Defaults to "%kernel.cache_dir%/sessions" if the "handler_id" option is not null') diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 820f4bf11..c4c81538c 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -814,7 +814,6 @@ protected static function getBundleDefaultConfig() 'cookie_httponly' => true, 'cookie_samesite' => 'lax', 'cookie_secure' => 'auto', - 'gc_probability' => 1, 'metadata_update_threshold' => 0, ], 'request' => [ From e967c07eade67be68b58caa73e779a3926fc2492 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sun, 8 Sep 2024 18:56:12 +0200 Subject: [PATCH 126/281] Remove obsolete requires annotation --- Tests/Functional/Psr4RoutingTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/Tests/Functional/Psr4RoutingTest.php b/Tests/Functional/Psr4RoutingTest.php index 811a99a11..6104a52ce 100644 --- a/Tests/Functional/Psr4RoutingTest.php +++ b/Tests/Functional/Psr4RoutingTest.php @@ -11,9 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; -/** - * @requires function Symfony\Component\Routing\Loader\Psr4DirectoryLoader::__construct - */ final class Psr4RoutingTest extends AbstractAttributeRoutingTestCase { protected function getTestCaseApp(): string From 2fb5d662fb0cb0c710bb26e525a9405960868385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Geffroy?= <81738559+raphael-geffroy@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:43:15 +0200 Subject: [PATCH 127/281] fix: pass validator.translation_domain to RequestPayloadValueResolver --- Resources/config/web.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/config/web.php b/Resources/config/web.php index 6710dabda..6f8358fb0 100644 --- a/Resources/config/web.php +++ b/Resources/config/web.php @@ -71,6 +71,7 @@ service('serializer'), service('validator')->nullOnInvalid(), service('translator')->nullOnInvalid(), + param('validator.translation_domain'), ]) ->tag('controller.targeted_value_resolver', ['name' => RequestPayloadValueResolver::class]) ->tag('kernel.event_subscriber') From b44cf99da7add4b23ff0b84a1d8111cedf00b8ca Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Sun, 8 Sep 2024 13:05:43 -0400 Subject: [PATCH 128/281] [Mailer] add Mailtrap bridge --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/mailer_transports.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 75cfe1d1f..e6fd4e58e 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2647,6 +2647,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co MailerBridge\Mailchimp\Transport\MandrillTransportFactory::class => 'mailer.transport_factory.mailchimp', MailerBridge\Postal\Transport\PostalTransportFactory::class => 'mailer.transport_factory.postal', MailerBridge\Postmark\Transport\PostmarkTransportFactory::class => 'mailer.transport_factory.postmark', + MailerBridge\Mailtrap\Transport\MailtrapTransportFactory::class => 'mailer.transport_factory.mailtrap', MailerBridge\Resend\Transport\ResendTransportFactory::class => 'mailer.transport_factory.resend', MailerBridge\Scaleway\Transport\ScalewayTransportFactory::class => 'mailer.transport_factory.scaleway', MailerBridge\Sendgrid\Transport\SendgridTransportFactory::class => 'mailer.transport_factory.sendgrid', diff --git a/Resources/config/mailer_transports.php b/Resources/config/mailer_transports.php index 8c7131b79..c0e7cc06a 100644 --- a/Resources/config/mailer_transports.php +++ b/Resources/config/mailer_transports.php @@ -22,6 +22,7 @@ use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetTransportFactory; use Symfony\Component\Mailer\Bridge\Mailomat\Transport\MailomatTransportFactory; use Symfony\Component\Mailer\Bridge\MailPace\Transport\MailPaceTransportFactory; +use Symfony\Component\Mailer\Bridge\Mailtrap\Transport\MailtrapTransportFactory; use Symfony\Component\Mailer\Bridge\Postal\Transport\PostalTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; use Symfony\Component\Mailer\Bridge\Resend\Transport\ResendTransportFactory; @@ -61,6 +62,7 @@ 'null' => NullTransportFactory::class, 'postal' => PostalTransportFactory::class, 'postmark' => PostmarkTransportFactory::class, + 'mailtrap' => MailtrapTransportFactory::class, 'resend' => ResendTransportFactory::class, 'scaleway' => ScalewayTransportFactory::class, 'sendgrid' => SendgridTransportFactory::class, From c8c19b8ae841ee0a68d31190fe48d8fff9266b7d Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 23 Jul 2024 09:29:37 +0200 Subject: [PATCH 129/281] [FrameworkBundle] Deprecate `session.sid_length` and `session.sid_bits_per_character` config options --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 2 ++ Tests/DependencyInjection/Fixtures/php/full.php | 2 -- Tests/DependencyInjection/Fixtures/xml/full.xml | 2 +- Tests/DependencyInjection/Fixtures/yml/full.yml | 2 -- Tests/DependencyInjection/FrameworkExtensionTestCase.php | 2 -- 6 files changed, 4 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 193f4570c..4d7fbaa4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ CHANGELOG * Deprecate making `cache.app` adapter taggable, use the `cache.app.taggable` adapter instead * Enable `json_decode_detailed_errors` in the default serializer context in debug mode by default when `seld/jsonlint` is installed * Register `Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter` as a service named `serializer.name_converter.snake_case_to_camel_case` if available + * Deprecate `session.sid_length` and `session.sid_bits_per_character` config options 7.1 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 4e66371e4..02872f1b8 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -685,10 +685,12 @@ private function addSessionSection(ArrayNodeDefinition $rootNode): void ->integerNode('sid_length') ->min(22) ->max(256) + ->setDeprecated('symfony/framework-bundle', '7.2', 'Setting the "%path%.%node%" configuration option is deprecated. It will be removed in version 8.0. No alternative is provided as PHP 8.4 has deprecated the related option.') ->end() ->integerNode('sid_bits_per_character') ->min(4) ->max(6) + ->setDeprecated('symfony/framework-bundle', '7.2', 'Setting the "%path%.%node%" configuration option is deprecated. It will be removed in version 8.0. No alternative is provided as PHP 8.4 has deprecated the related option.') ->end() ->end() ->end() diff --git a/Tests/DependencyInjection/Fixtures/php/full.php b/Tests/DependencyInjection/Fixtures/php/full.php index 4fbf72a9f..8c97c84a5 100644 --- a/Tests/DependencyInjection/Fixtures/php/full.php +++ b/Tests/DependencyInjection/Fixtures/php/full.php @@ -43,8 +43,6 @@ 'gc_maxlifetime' => 90000, 'gc_divisor' => 108, 'gc_probability' => 1, - 'sid_length' => 22, - 'sid_bits_per_character' => 4, 'save_path' => '/path/to/sessions', ], 'assets' => [ diff --git a/Tests/DependencyInjection/Fixtures/xml/full.xml b/Tests/DependencyInjection/Fixtures/xml/full.xml index fd5d52e1c..48c5e6f6d 100644 --- a/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -17,7 +17,7 @@ - + text/csv diff --git a/Tests/DependencyInjection/Fixtures/yml/full.yml b/Tests/DependencyInjection/Fixtures/yml/full.yml index 96001f1d2..69a19baa4 100644 --- a/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -36,8 +36,6 @@ framework: gc_probability: 1 gc_divisor: 108 gc_maxlifetime: 90000 - sid_length: 22 - sid_bits_per_character: 4 save_path: /path/to/sessions assets: version: v1 diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index a6da61ae3..2fadb76fd 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -676,8 +676,6 @@ public function testSession() $this->assertEquals(108, $options['gc_divisor']); $this->assertEquals(1, $options['gc_probability']); $this->assertEquals(90000, $options['gc_maxlifetime']); - $this->assertEquals(22, $options['sid_length']); - $this->assertEquals(4, $options['sid_bits_per_character']); $this->assertEquals('/path/to/sessions', $container->getParameter('session.save_path')); } From 328ef309092b87b47b798a8464b8f91d2e6a0272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Ostroluck=C3=BD?= Date: Sat, 7 Sep 2024 23:01:09 +0200 Subject: [PATCH 130/281] [FrameworkBundle] Add --resolve-env-vars option to lint:container command --- CHANGELOG.md | 1 + Command/ContainerLintCommand.php | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 193f4570c..c60b730d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add support for setting `headers` with `Symfony\Bundle\FrameworkBundle\Controller\TemplateController` + * Add `--resolve-env-vars` option to `lint:container` command * Derivate `kernel.secret` from the decryption secret when its env var is not defined * Make the `config/` directory optional in `MicroKernelTrait`, add support for service arguments in the invokable Kernel class, and register `FrameworkBundle` by default when the `bundles.php` file is missing diff --git a/Command/ContainerLintCommand.php b/Command/ContainerLintCommand.php index 0e6c72d16..e794e88c4 100644 --- a/Command/ContainerLintCommand.php +++ b/Command/ContainerLintCommand.php @@ -17,6 +17,7 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\DependencyInjection\Compiler\CheckAliasValidityPass; @@ -39,6 +40,7 @@ protected function configure(): void { $this ->setHelp('This command parses service definitions and ensures that injected values match the type declarations of each services\' class.') + ->addOption('resolve-env-vars', null, InputOption::VALUE_NONE, 'Resolve environment variables and fail if one is missing.') ; } @@ -58,7 +60,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $container->setParameter('container.build_time', time()); try { - $container->compile(); + $container->compile((bool) $input->getOption('resolve-env-vars')); } catch (InvalidArgumentException $e) { $errorIo->error($e->getMessage()); From b2f46bd80928cdf5942062697d68c0354acf308d Mon Sep 17 00:00:00 2001 From: HypeMC Date: Fri, 13 Sep 2024 02:53:00 +0200 Subject: [PATCH 131/281] [FrameworkBundle] Add ability to use existing service as lock/semaphore resource --- CHANGELOG.md | 1 + DependencyInjection/FrameworkExtension.php | 8 ++++- .../Fixtures/php/lock_service.php | 13 +++++++++ .../Fixtures/php/semaphore_service.php | 13 +++++++++ .../Fixtures/xml/lock_service.xml | 20 +++++++++++++ .../Fixtures/xml/semaphore_service.xml | 20 +++++++++++++ .../Fixtures/yml/lock_service.yml | 14 +++++++++ .../Fixtures/yml/semaphore_service.yml | 14 +++++++++ .../FrameworkExtensionTestCase.php | 29 +++++++++++++++++++ 9 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 Tests/DependencyInjection/Fixtures/php/lock_service.php create mode 100644 Tests/DependencyInjection/Fixtures/php/semaphore_service.php create mode 100644 Tests/DependencyInjection/Fixtures/xml/lock_service.xml create mode 100644 Tests/DependencyInjection/Fixtures/xml/semaphore_service.xml create mode 100644 Tests/DependencyInjection/Fixtures/yml/lock_service.yml create mode 100644 Tests/DependencyInjection/Fixtures/yml/semaphore_service.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index f5f05ff36..7b5bbaeda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ CHANGELOG * Enable `json_decode_detailed_errors` in the default serializer context in debug mode by default when `seld/jsonlint` is installed * Register `Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter` as a service named `serializer.name_converter.snake_case_to_camel_case` if available * Deprecate `session.sid_length` and `session.sid_bits_per_character` config options + * Add the ability to use an existing service as a lock/semaphore resource 7.1 --- diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 75cfe1d1f..5b644c740 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -1230,7 +1230,7 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c } $container->resolveEnvPlaceholders($config['handler_id'], null, $usedEnvs); - if ($usedEnvs || preg_match('#^[a-z]++://#', $config['handler_id'])) { + if ($usedEnvs || str_contains($config['handler_id'], '://')) { $id = '.cache_connection.'.ContainerBuilder::hash($config['handler_id']); $container->getDefinition('session.abstract_handler') @@ -2004,6 +2004,9 @@ private function registerLockConfiguration(array $config, ContainerBuilder $cont $storeDefinitions = []; foreach ($resourceStores as $resourceStore) { $storeDsn = $container->resolveEnvPlaceholders($resourceStore, null, $usedEnvs); + if (!$usedEnvs && !str_contains($resourceStore, '://')) { + $resourceStore = new Reference($resourceStore); + } $storeDefinition = new Definition(PersistingStoreInterface::class); $storeDefinition ->setFactory([StoreFactory::class, 'createStore']) @@ -2045,6 +2048,9 @@ private function registerSemaphoreConfiguration(array $config, ContainerBuilder foreach ($config['resources'] as $resourceName => $resourceStore) { $storeDsn = $container->resolveEnvPlaceholders($resourceStore, null, $usedEnvs); + if (!$usedEnvs && !str_contains($resourceStore, '://')) { + $resourceStore = new Reference($resourceStore); + } $storeDefinition = new Definition(SemaphoreStoreInterface::class); $storeDefinition->setFactory([SemaphoreStoreFactory::class, 'createStore']); $storeDefinition->setArguments([$resourceStore]); diff --git a/Tests/DependencyInjection/Fixtures/php/lock_service.php b/Tests/DependencyInjection/Fixtures/php/lock_service.php new file mode 100644 index 000000000..b3a17ee43 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/lock_service.php @@ -0,0 +1,13 @@ +register('my_service', \Redis::class); +$container->setAlias('factory_public_alias', 'lock.default.factory') + ->setPublic(true); + +$container->loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'lock' => 'my_service', +]); diff --git a/Tests/DependencyInjection/Fixtures/php/semaphore_service.php b/Tests/DependencyInjection/Fixtures/php/semaphore_service.php new file mode 100644 index 000000000..e94d809be --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/semaphore_service.php @@ -0,0 +1,13 @@ +register('my_service', \Redis::class); +$container->setAlias('factory_public_alias', 'semaphore.default.factory') + ->setPublic(true); + +$container->loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'semaphore' => 'my_service', +]); diff --git a/Tests/DependencyInjection/Fixtures/xml/lock_service.xml b/Tests/DependencyInjection/Fixtures/xml/lock_service.xml new file mode 100644 index 000000000..d184cc2f7 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/lock_service.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + my_service + + + diff --git a/Tests/DependencyInjection/Fixtures/xml/semaphore_service.xml b/Tests/DependencyInjection/Fixtures/xml/semaphore_service.xml new file mode 100644 index 000000000..4939a3300 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/semaphore_service.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + my_service + + + diff --git a/Tests/DependencyInjection/Fixtures/yml/lock_service.yml b/Tests/DependencyInjection/Fixtures/yml/lock_service.yml new file mode 100644 index 000000000..4cea81ea0 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/lock_service.yml @@ -0,0 +1,14 @@ +services: + my_service: + class: \Redis + factory_public_alias: + alias: lock.default.factory + public: true + +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + lock: my_service diff --git a/Tests/DependencyInjection/Fixtures/yml/semaphore_service.yml b/Tests/DependencyInjection/Fixtures/yml/semaphore_service.yml new file mode 100644 index 000000000..f966965b5 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/semaphore_service.yml @@ -0,0 +1,14 @@ +services: + my_service: + class: \Redis + factory_public_alias: + alias: semaphore.default.factory + public: true + +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + semaphore: my_service diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index a381e1472..b4d7af104 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -34,6 +34,7 @@ use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -57,6 +58,7 @@ use Symfony\Component\HttpFoundation\IpUtils; use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass; use Symfony\Component\HttpKernel\Fragment\FragmentUriGeneratorInterface; +use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\Store\SemaphoreStore; use Symfony\Component\Messenger\Bridge\AmazonSqs\Transport\AmazonSqsTransportFactory; use Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpTransportFactory; @@ -67,6 +69,7 @@ use Symfony\Component\Notifier\TexterInterface; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\Security\Core\AuthenticationEvents; +use Symfony\Component\Semaphore\SemaphoreFactory; use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader; use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; @@ -2433,6 +2436,19 @@ public function testNamedLocks() self::assertStringContainsString('REDIS_DSN', $storeDef->getArgument(0)); } + public function testLockWithService() + { + $container = $this->createContainerFromFile('lock_service', [], true, false); + $container->getCompilerPassConfig()->setOptimizationPasses([new ResolveChildDefinitionsPass()]); + $container->compile(); + + self::assertTrue($container->hasDefinition('lock.default.factory')); + $storeDef = $container->getDefinition($container->getDefinition('lock.default.factory')->getArgument(0)); + self::assertEquals(new Reference('my_service'), $storeDef->getArgument(0)); + + self::assertInstanceOf(LockFactory::class, $container->get('factory_public_alias')); + } + public function testDefaultSemaphore() { $container = $this->createContainerFromFile('semaphore'); @@ -2455,6 +2471,19 @@ public function testNamedSemaphores() self::assertStringContainsString('REDIS_DSN', $storeDef->getArgument(0)); } + public function testSemaphoreWithService() + { + $container = $this->createContainerFromFile('semaphore_service', [], true, false); + $container->getCompilerPassConfig()->setOptimizationPasses([new ResolveChildDefinitionsPass()]); + $container->compile(); + + self::assertTrue($container->hasDefinition('semaphore.default.factory')); + $storeDef = $container->getDefinition($container->getDefinition('semaphore.default.factory')->getArgument(0)); + self::assertEquals(new Reference('my_service'), $storeDef->getArgument(0)); + + self::assertInstanceOf(SemaphoreFactory::class, $container->get('factory_public_alias')); + } + protected function createContainer(array $data = []) { return new ContainerBuilder(new EnvPlaceholderParameterBag(array_merge([ From 63d2b98935adca0264ceef816ad33451e905915f Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Sun, 30 Jun 2024 23:54:38 -0400 Subject: [PATCH 132/281] Introducing container non-empty parameters --- DependencyInjection/FrameworkExtension.php | 1 + composer.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 75cfe1d1f..407ae5237 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -309,6 +309,7 @@ public function load(array $configs, ContainerBuilder $container): void if (isset($config['secret'])) { $container->setParameter('kernel.secret', $config['secret']); } + $container->nonEmptyParameter('kernel.secret', 'A non-empty value for the parameter "kernel.secret" is required. Did you forget to configure the "framework.secret" option?'); $container->setParameter('kernel.http_method_override', $config['http_method_override']); $container->setParameter('kernel.trust_x_sendfile_type_header', $config['trust_x_sendfile_type_header']); diff --git a/composer.json b/composer.json index e3e101e1a..da5b96454 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "ext-xml": "*", "symfony/cache": "^6.4|^7.0", "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^7.1.5", + "symfony/dependency-injection": "^7.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.4|^7.0", "symfony/event-dispatcher": "^6.4|^7.0", From b3958d511c39b7ff646a9acc29cd280e317ae0e3 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Mon, 20 May 2024 06:59:56 +0200 Subject: [PATCH 133/281] [Serializer] Introduce named serializers --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 43 +++++++++++++++--- DependencyInjection/FrameworkExtension.php | 3 ++ Resources/config/messenger.php | 2 +- Resources/config/schema/symfony-1.0.xsd | 11 +++++ Resources/config/serializer.php | 44 ++++++++++--------- Resources/config/serializer_debug.php | 1 + .../DependencyInjection/ConfigurationTest.php | 18 ++++++++ .../DependencyInjection/Fixtures/php/full.php | 7 +++ .../DependencyInjection/Fixtures/xml/full.xml | 5 +++ .../DependencyInjection/Fixtures/yml/full.yml | 6 +++ .../FrameworkExtensionTestCase.php | 20 +++++++++ 12 files changed, 133 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b5bbaeda..f6fb34737 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ CHANGELOG * Register `Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter` as a service named `serializer.name_converter.snake_case_to_camel_case` if available * Deprecate `session.sid_length` and `session.sid_bits_per_character` config options * Add the ability to use an existing service as a lock/semaphore resource + * Add support for configuring multiple serializer instances via the configuration 7.1 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 02872f1b8..0914a0d22 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1099,10 +1099,22 @@ private function addAnnotationsSection(ArrayNodeDefinition $rootNode): void private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone): void { + $defaultContextNode = fn () => (new NodeBuilder()) + ->arrayNode('default_context') + ->normalizeKeys(false) + ->validate() + ->ifTrue(fn () => $this->debug && class_exists(JsonParser::class)) + ->then(fn (array $v) => $v + [JsonDecode::DETAILED_ERROR_MESSAGES => true]) + ->end() + ->defaultValue([]) + ->prototype('variable')->end() + ; + $rootNode ->children() ->arrayNode('serializer') ->info('Serializer configuration') + ->fixXmlConfig('named_serializer', 'named_serializers') ->{$enableIfStandalone('symfony/serializer', Serializer::class)}() ->children() ->booleanNode('enable_attributes')->{class_exists(FullStack::class) ? 'defaultFalse' : 'defaultTrue'}()->end() @@ -1118,19 +1130,36 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $e ->end() ->end() ->end() - ->arrayNode('default_context') - ->normalizeKeys(false) + ->append($defaultContextNode()) + ->arrayNode('named_serializers') + ->useAttributeAsKey('name') + ->arrayPrototype() + ->children() + ->scalarNode('name_converter')->end() + ->append($defaultContextNode()) + ->booleanNode('include_built_in_normalizers') + ->info('Whether to include the built-in normalizers') + ->defaultTrue() + ->end() + ->booleanNode('include_built_in_encoders') + ->info('Whether to include the built-in encoders') + ->defaultTrue() + ->end() + ->end() + ->end() ->validate() - ->ifTrue(fn () => $this->debug && class_exists(JsonParser::class)) - ->then(fn (array $v) => $v + [JsonDecode::DETAILED_ERROR_MESSAGES => true]) + ->ifTrue(fn ($v) => isset($v['default'])) + ->thenInvalid('"default" is a reserved name.') ->end() - ->defaultValue([]) - ->prototype('variable')->end() ->end() ->end() ->validate() ->ifTrue(fn ($v) => $this->debug && class_exists(JsonParser::class) && !isset($v['default_context'][JsonDecode::DETAILED_ERROR_MESSAGES])) - ->then(function ($v) { $v['default_context'][JsonDecode::DETAILED_ERROR_MESSAGES] = true; return $v; }) + ->then(function ($v) { + $v['default_context'][JsonDecode::DETAILED_ERROR_MESSAGES] = true; + + return $v; + }) ->end() ->end() ->end() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 44a27f903..17c779276 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -1906,6 +1906,7 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->getDefinition('serializer.mapping.cache_warmer')->replaceArgument(0, $serializerLoaders); if (isset($config['name_converter']) && $config['name_converter']) { + $container->setParameter('.serializer.name_converter', $config['name_converter']); $container->getDefinition('serializer.name_converter.metadata_aware')->setArgument(1, new Reference($config['name_converter'])); } @@ -1934,6 +1935,8 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->getDefinition('serializer.normalizer.object')->setArgument(6, $context); $container->getDefinition('serializer.normalizer.property')->setArgument(5, $defaultContext); + + $container->setParameter('.serializer.named_serializers', $config['named_serializers'] ?? []); } private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void diff --git a/Resources/config/messenger.php b/Resources/config/messenger.php index df2476096..40f5b84ca 100644 --- a/Resources/config/messenger.php +++ b/Resources/config/messenger.php @@ -72,7 +72,7 @@ ]) ->set('serializer.normalizer.flatten_exception', FlattenExceptionNormalizer::class) - ->tag('serializer.normalizer', ['priority' => -880]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -880]) ->set('messenger.transport.native_php_serializer', PhpSerializer::class) ->alias('messenger.default_serializer', 'messenger.transport.native_php_serializer') diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index c6816fbd0..64e9c76cb 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -320,6 +320,7 @@ + @@ -332,6 +333,16 @@ + + + + + + + + + + diff --git a/Resources/config/serializer.php b/Resources/config/serializer.php index 36c8b89b5..d689d4299 100644 --- a/Resources/config/serializer.php +++ b/Resources/config/serializer.php @@ -80,46 +80,46 @@ ->set('serializer.normalizer.constraint_violation_list', ConstraintViolationListNormalizer::class) ->args([1 => service('serializer.name_converter.metadata_aware')]) ->autowire(true) - ->tag('serializer.normalizer', ['priority' => -915]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915]) ->set('serializer.normalizer.mime_message', MimeMessageNormalizer::class) ->args([service('serializer.normalizer.property')]) - ->tag('serializer.normalizer', ['priority' => -915]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915]) ->set('serializer.normalizer.datetimezone', DateTimeZoneNormalizer::class) - ->tag('serializer.normalizer', ['priority' => -915]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915]) ->set('serializer.normalizer.dateinterval', DateIntervalNormalizer::class) - ->tag('serializer.normalizer', ['priority' => -915]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915]) ->set('serializer.normalizer.data_uri', DataUriNormalizer::class) ->args([service('mime_types')->nullOnInvalid()]) - ->tag('serializer.normalizer', ['priority' => -920]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -920]) ->set('serializer.normalizer.datetime', DateTimeNormalizer::class) - ->tag('serializer.normalizer', ['priority' => -910]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -910]) ->set('serializer.normalizer.json_serializable', JsonSerializableNormalizer::class) ->args([null, null]) - ->tag('serializer.normalizer', ['priority' => -950]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -950]) ->set('serializer.normalizer.problem', ProblemNormalizer::class) ->args([param('kernel.debug'), '$translator' => service('translator')->nullOnInvalid()]) - ->tag('serializer.normalizer', ['priority' => -890]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -890]) ->set('serializer.denormalizer.unwrapping', UnwrappingDenormalizer::class) ->args([service('serializer.property_accessor')]) - ->tag('serializer.normalizer', ['priority' => 1000]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => 1000]) ->set('serializer.normalizer.uid', UidNormalizer::class) - ->tag('serializer.normalizer', ['priority' => -890]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -890]) ->set('serializer.normalizer.translatable', TranslatableNormalizer::class) ->args(['$translator' => service('translator')]) - ->tag('serializer.normalizer', ['priority' => -920]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -920]) ->set('serializer.normalizer.form_error', FormErrorNormalizer::class) - ->tag('serializer.normalizer', ['priority' => -915]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915]) ->set('serializer.normalizer.object', ObjectNormalizer::class) ->args([ @@ -132,7 +132,7 @@ null, service('property_info')->ignoreOnInvalid(), ]) - ->tag('serializer.normalizer', ['priority' => -1000]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -1000]) ->set('serializer.normalizer.property', PropertyNormalizer::class) ->args([ @@ -144,7 +144,7 @@ ]) ->set('serializer.denormalizer.array', ArrayDenormalizer::class) - ->tag('serializer.normalizer', ['priority' => -990]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -990]) // Loader ->set('serializer.mapping.chain_loader', LoaderChain::class) @@ -174,26 +174,30 @@ // Encoders ->set('serializer.encoder.xml', XmlEncoder::class) - ->tag('serializer.encoder') + ->tag('serializer.encoder', ['built_in' => true]) ->set('serializer.encoder.json', JsonEncoder::class) ->args([null, null]) - ->tag('serializer.encoder') + ->tag('serializer.encoder', ['built_in' => true]) ->set('serializer.encoder.yaml', YamlEncoder::class) ->args([null, null]) - ->tag('serializer.encoder') + ->tag('serializer.encoder', ['built_in' => true]) ->set('serializer.encoder.csv', CsvEncoder::class) - ->tag('serializer.encoder') + ->tag('serializer.encoder', ['built_in' => true]) // Name converters ->set('serializer.name_converter.camel_case_to_snake_case', CamelCaseToSnakeCaseNameConverter::class) ->set('serializer.name_converter.snake_case_to_camel_case', SnakeCaseToCamelCaseNameConverter::class) - ->set('serializer.name_converter.metadata_aware', MetadataAwareNameConverter::class) + ->set('serializer.name_converter.metadata_aware.abstract', MetadataAwareNameConverter::class) + ->abstract() ->args([service('serializer.mapping.class_metadata_factory')]) + ->set('serializer.name_converter.metadata_aware') + ->parent('serializer.name_converter.metadata_aware.abstract') + // PropertyInfo extractor ->set('property_info.serializer_extractor', SerializerExtractor::class) ->args([service('serializer.mapping.class_metadata_factory')]) @@ -216,6 +220,6 @@ ]) ->set('serializer.normalizer.backed_enum', BackedEnumNormalizer::class) - ->tag('serializer.normalizer', ['priority' => -915]) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915]) ; }; diff --git a/Resources/config/serializer_debug.php b/Resources/config/serializer_debug.php index 45b764fdd..520d145cb 100644 --- a/Resources/config/serializer_debug.php +++ b/Resources/config/serializer_debug.php @@ -21,6 +21,7 @@ ->args([ service('debug.serializer.inner'), service('serializer.data_collector'), + 'default', ]) ->set('serializer.data_collector', SerializerDataCollector::class) diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index c4c81538c..73e6c2025 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -784,6 +784,7 @@ protected static function getBundleDefaultConfig() 'enabled' => true, 'enable_attributes' => !class_exists(FullStack::class), 'mapping' => ['paths' => []], + 'named_serializers' => [], ], 'property_access' => [ 'enabled' => true, @@ -958,4 +959,21 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor ], ]; } + + public function testNamedSerializersReservedName() + { + $processor = new Processor(); + $configuration = new Configuration(true); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('Invalid configuration for path "framework.serializer.named_serializers": "default" is a reserved name.'); + + $processor->processConfiguration($configuration, [[ + 'serializer' => [ + 'named_serializers' => [ + 'default' => ['include_built_in_normalizers' => false], + ], + ], + ]]); + } } diff --git a/Tests/DependencyInjection/Fixtures/php/full.php b/Tests/DependencyInjection/Fixtures/php/full.php index 8c97c84a5..0a32ce8b3 100644 --- a/Tests/DependencyInjection/Fixtures/php/full.php +++ b/Tests/DependencyInjection/Fixtures/php/full.php @@ -66,6 +66,13 @@ 'circular_reference_handler' => 'my.circular.reference.handler', 'max_depth_handler' => 'my.max.depth.handler', 'default_context' => ['enable_max_depth' => true], + 'named_serializers' => [ + 'api' => [ + 'include_built_in_normalizers' => true, + 'include_built_in_encoders' => true, + 'default_context' => ['enable_max_depth' => false], + ], + ], ], 'property_info' => true, 'type_info' => true, diff --git a/Tests/DependencyInjection/Fixtures/xml/full.xml b/Tests/DependencyInjection/Fixtures/xml/full.xml index 48c5e6f6d..c01e85783 100644 --- a/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -38,6 +38,11 @@ true + + + false + + diff --git a/Tests/DependencyInjection/Fixtures/yml/full.yml b/Tests/DependencyInjection/Fixtures/yml/full.yml index 69a19baa4..7550749eb 100644 --- a/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -57,6 +57,12 @@ framework: max_depth_handler: my.max.depth.handler default_context: enable_max_depth: true + named_serializers: + api: + include_built_in_normalizers: true + include_built_in_encoders: true + default_context: + enable_max_depth: false type_info: ~ property_info: ~ ide: file%%link%%format diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index b4d7af104..8d902bd47 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -1463,6 +1463,26 @@ public function testSerializerWithoutTranslator() $this->assertFalse($container->hasDefinition('serializer.normalizer.translatable')); } + public function testSerializerDefaultParameters() + { + $container = $this->createContainerFromFile('serializer_enabled'); + $this->assertFalse($container->hasParameter('.serializer.name_converter')); + $this->assertFalse($container->hasParameter('serializer.default_context')); + $this->assertTrue($container->hasParameter('.serializer.named_serializers')); + $this->assertSame([], $container->getParameter('.serializer.named_serializers')); + } + + public function testSerializerParametersAreSet() + { + $container = $this->createContainerFromFile('full'); + $this->assertTrue($container->hasParameter('.serializer.name_converter')); + $this->assertSame('serializer.name_converter.camel_case_to_snake_case', $container->getParameter('.serializer.name_converter')); + $this->assertTrue($container->hasParameter('serializer.default_context')); + $this->assertSame(['enable_max_depth' => true], $container->getParameter('serializer.default_context')); + $this->assertTrue($container->hasParameter('.serializer.named_serializers')); + $this->assertSame(['api' => ['include_built_in_normalizers' => true, 'include_built_in_encoders' => true, 'default_context' => ['enable_max_depth' => false]]], $container->getParameter('.serializer.named_serializers')); + } + public function testRegisterSerializerExtractor() { $container = $this->createContainerFromFile('full'); From eda025df6deb419d73fc478c4c892cc66ed25434 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 19 Sep 2024 09:56:35 +0200 Subject: [PATCH 134/281] Switch to ExpectUserDeprecationMessageTrait --- Tests/DependencyInjection/FrameworkExtensionTestCase.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 8d902bd47..5f8562e67 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -13,7 +13,7 @@ use Psr\Cache\CacheItemPoolInterface; use Psr\Log\LoggerAwareInterface; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; +use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DummyMessage; @@ -98,7 +98,7 @@ abstract class FrameworkExtensionTestCase extends TestCase { - use ExpectDeprecationTrait; + use ExpectUserDeprecationMessageTrait; private static array $containerCache = []; @@ -1863,7 +1863,7 @@ public function testCacheTaggableTagAppliedToPools() */ public function testTaggableCacheAppIsDeprecated() { - $this->expectDeprecation('Since symfony/framework-bundle 7.2: Using the "tags" option with the "cache.app" adapter is deprecated. You can use the "cache.app.taggable" adapter instead (aliased to the TagAwareCacheInterface for autowiring).'); + $this->expectUserDeprecationMessage('Since symfony/framework-bundle 7.2: Using the "tags" option with the "cache.app" adapter is deprecated. You can use the "cache.app.taggable" adapter instead (aliased to the TagAwareCacheInterface for autowiring).'); $this->createContainerFromFile('cache_cacheapp_tagaware'); } From 80325b96ae8cff6def34ef0adffabf6809864288 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 21 Sep 2024 09:18:43 +0200 Subject: [PATCH 135/281] fix tests to not depend on the installation of the redis extension --- Tests/DependencyInjection/Fixtures/php/lock_service.php | 2 -- .../DependencyInjection/Fixtures/php/semaphore_service.php | 2 -- Tests/DependencyInjection/Fixtures/xml/lock_service.xml | 1 - .../DependencyInjection/Fixtures/xml/semaphore_service.xml | 1 - Tests/DependencyInjection/Fixtures/yml/lock_service.yml | 3 --- .../DependencyInjection/Fixtures/yml/semaphore_service.yml | 3 --- Tests/DependencyInjection/FrameworkExtensionTestCase.php | 6 ------ 7 files changed, 18 deletions(-) diff --git a/Tests/DependencyInjection/Fixtures/php/lock_service.php b/Tests/DependencyInjection/Fixtures/php/lock_service.php index b3a17ee43..4bdbd29b8 100644 --- a/Tests/DependencyInjection/Fixtures/php/lock_service.php +++ b/Tests/DependencyInjection/Fixtures/php/lock_service.php @@ -1,8 +1,6 @@ register('my_service', \Redis::class); -$container->setAlias('factory_public_alias', 'lock.default.factory') - ->setPublic(true); $container->loadFromExtension('framework', [ 'annotations' => false, diff --git a/Tests/DependencyInjection/Fixtures/php/semaphore_service.php b/Tests/DependencyInjection/Fixtures/php/semaphore_service.php index e94d809be..279f1c158 100644 --- a/Tests/DependencyInjection/Fixtures/php/semaphore_service.php +++ b/Tests/DependencyInjection/Fixtures/php/semaphore_service.php @@ -1,8 +1,6 @@ register('my_service', \Redis::class); -$container->setAlias('factory_public_alias', 'semaphore.default.factory') - ->setPublic(true); $container->loadFromExtension('framework', [ 'annotations' => false, diff --git a/Tests/DependencyInjection/Fixtures/xml/lock_service.xml b/Tests/DependencyInjection/Fixtures/xml/lock_service.xml index d184cc2f7..a175526a9 100644 --- a/Tests/DependencyInjection/Fixtures/xml/lock_service.xml +++ b/Tests/DependencyInjection/Fixtures/xml/lock_service.xml @@ -7,7 +7,6 @@ - diff --git a/Tests/DependencyInjection/Fixtures/xml/semaphore_service.xml b/Tests/DependencyInjection/Fixtures/xml/semaphore_service.xml index 4939a3300..814823802 100644 --- a/Tests/DependencyInjection/Fixtures/xml/semaphore_service.xml +++ b/Tests/DependencyInjection/Fixtures/xml/semaphore_service.xml @@ -7,7 +7,6 @@ - diff --git a/Tests/DependencyInjection/Fixtures/yml/lock_service.yml b/Tests/DependencyInjection/Fixtures/yml/lock_service.yml index 4cea81ea0..1b5dfea17 100644 --- a/Tests/DependencyInjection/Fixtures/yml/lock_service.yml +++ b/Tests/DependencyInjection/Fixtures/yml/lock_service.yml @@ -1,9 +1,6 @@ services: my_service: class: \Redis - factory_public_alias: - alias: lock.default.factory - public: true framework: annotations: false diff --git a/Tests/DependencyInjection/Fixtures/yml/semaphore_service.yml b/Tests/DependencyInjection/Fixtures/yml/semaphore_service.yml index f966965b5..62765ac91 100644 --- a/Tests/DependencyInjection/Fixtures/yml/semaphore_service.yml +++ b/Tests/DependencyInjection/Fixtures/yml/semaphore_service.yml @@ -1,9 +1,6 @@ services: my_service: class: \Redis - factory_public_alias: - alias: semaphore.default.factory - public: true framework: annotations: false diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 5f8562e67..fc66c889f 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -58,7 +58,6 @@ use Symfony\Component\HttpFoundation\IpUtils; use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass; use Symfony\Component\HttpKernel\Fragment\FragmentUriGeneratorInterface; -use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\Store\SemaphoreStore; use Symfony\Component\Messenger\Bridge\AmazonSqs\Transport\AmazonSqsTransportFactory; use Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpTransportFactory; @@ -69,7 +68,6 @@ use Symfony\Component\Notifier\TexterInterface; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\Security\Core\AuthenticationEvents; -use Symfony\Component\Semaphore\SemaphoreFactory; use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader; use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; @@ -2465,8 +2463,6 @@ public function testLockWithService() self::assertTrue($container->hasDefinition('lock.default.factory')); $storeDef = $container->getDefinition($container->getDefinition('lock.default.factory')->getArgument(0)); self::assertEquals(new Reference('my_service'), $storeDef->getArgument(0)); - - self::assertInstanceOf(LockFactory::class, $container->get('factory_public_alias')); } public function testDefaultSemaphore() @@ -2500,8 +2496,6 @@ public function testSemaphoreWithService() self::assertTrue($container->hasDefinition('semaphore.default.factory')); $storeDef = $container->getDefinition($container->getDefinition('semaphore.default.factory')->getArgument(0)); self::assertEquals(new Reference('my_service'), $storeDef->getArgument(0)); - - self::assertInstanceOf(SemaphoreFactory::class, $container->get('factory_public_alias')); } protected function createContainer(array $data = []) From 777484373ee96258184a6ece27050a8341452054 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Mon, 23 Sep 2024 09:07:01 -0400 Subject: [PATCH 136/281] rename non-empty parameter methods --- DependencyInjection/FrameworkExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 17c779276..e4e32affe 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -309,7 +309,7 @@ public function load(array $configs, ContainerBuilder $container): void if (isset($config['secret'])) { $container->setParameter('kernel.secret', $config['secret']); } - $container->nonEmptyParameter('kernel.secret', 'A non-empty value for the parameter "kernel.secret" is required. Did you forget to configure the "framework.secret" option?'); + $container->parameterCannotBeEmpty('kernel.secret', 'A non-empty value for the parameter "kernel.secret" is required. Did you forget to configure the "framework.secret" option?'); $container->setParameter('kernel.http_method_override', $config['http_method_override']); $container->setParameter('kernel.trust_x_sendfile_type_header', $config['trust_x_sendfile_type_header']); From a5fc026a01fb5b91617908cff759bf37ec352c46 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 24 Sep 2024 13:28:07 +0200 Subject: [PATCH 137/281] Remove useless parent method calls in tests --- Tests/Functional/UidTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/Tests/Functional/UidTest.php b/Tests/Functional/UidTest.php index da0b1e4fc..245cb11b6 100644 --- a/Tests/Functional/UidTest.php +++ b/Tests/Functional/UidTest.php @@ -20,8 +20,6 @@ class UidTest extends AbstractWebTestCase { protected function setUp(): void { - parent::setUp(); - self::deleteTmpDir(); } From ffe8193ad33c98477462a7478544cc0dad1f8b8b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 3 Sep 2024 14:33:41 +0200 Subject: [PATCH 138/281] [FrameworkBundle][HttpKernel] Add support for `SYMFONY_TRUSTED_PROXIES`, `SYMFONY_TRUSTED_HEADERS`, `SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER` and `SYMFONY_TRUSTED_HOSTS` env vars --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 17 ++++++------- DependencyInjection/FrameworkExtension.php | 25 +++---------------- .../DependencyInjection/ConfigurationTest.php | 11 +++----- 4 files changed, 15 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6fb34737..e9a7186a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ CHANGELOG * Deprecate `session.sid_length` and `session.sid_bits_per_character` config options * Add the ability to use an existing service as a lock/semaphore resource * Add support for configuring multiple serializer instances via the configuration + * Add support for `SYMFONY_TRUSTED_PROXIES`, `SYMFONY_TRUSTED_HEADERS`, `SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER` and `SYMFONY_TRUSTED_HOSTS` env vars 7.1 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 0914a0d22..aeb36ca5f 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -90,7 +90,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->scalarNode('trust_x_sendfile_type_header') ->info('Set true to enable support for xsendfile in binary file responses.') - ->defaultFalse() + ->defaultValue('%env(bool:default::SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER)%') ->end() ->scalarNode('ide')->defaultValue($this->debug ? '%env(default::SYMFONY_IDE)%' : null)->end() ->booleanNode('test')->end() @@ -108,26 +108,23 @@ public function getConfigTreeBuilder(): TreeBuilder ->prototype('scalar')->end() ->end() ->arrayNode('trusted_hosts') - ->beforeNormalization()->ifString()->then(fn ($v) => [$v])->end() + ->beforeNormalization()->ifString()->then(static fn ($v) => $v ? [$v] : [])->end() ->prototype('scalar')->end() + ->defaultValue(['%env(default::SYMFONY_TRUSTED_HOSTS)%']) ->end() ->variableNode('trusted_proxies') ->beforeNormalization() ->ifTrue(fn ($v) => 'private_ranges' === $v || 'PRIVATE_SUBNETS' === $v) ->then(fn () => IpUtils::PRIVATE_SUBNETS) ->end() + ->defaultValue(['%env(default::SYMFONY_TRUSTED_PROXIES)%']) ->end() ->arrayNode('trusted_headers') ->fixXmlConfig('trusted_header') ->performNoDeepMerging() - ->defaultValue(['x-forwarded-for', 'x-forwarded-port', 'x-forwarded-proto']) - ->beforeNormalization()->ifString()->then(fn ($v) => $v ? array_map('trim', explode(',', $v)) : [])->end() - ->enumPrototype() - ->values([ - 'forwarded', - 'x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-forwarded-port', 'x-forwarded-prefix', - ]) - ->end() + ->beforeNormalization()->ifString()->then(static fn ($v) => $v ? [$v] : [])->end() + ->prototype('scalar')->end() + ->defaultValue(['%env(default::SYMFONY_TRUSTED_HEADERS)%']) ->end() ->scalarNode('error_controller') ->defaultValue('error_controller') diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 17c779276..8011c964f 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -313,14 +313,14 @@ public function load(array $configs, ContainerBuilder $container): void $container->setParameter('kernel.http_method_override', $config['http_method_override']); $container->setParameter('kernel.trust_x_sendfile_type_header', $config['trust_x_sendfile_type_header']); - $container->setParameter('kernel.trusted_hosts', $config['trusted_hosts']); + $container->setParameter('kernel.trusted_hosts', [0] === array_keys($config['trusted_hosts']) ? $config['trusted_hosts'][0] : $config['trusted_hosts']); $container->setParameter('kernel.default_locale', $config['default_locale']); $container->setParameter('kernel.enabled_locales', $config['enabled_locales']); $container->setParameter('kernel.error_controller', $config['error_controller']); if (($config['trusted_proxies'] ?? false) && ($config['trusted_headers'] ?? false)) { - $container->setParameter('kernel.trusted_proxies', $config['trusted_proxies']); - $container->setParameter('kernel.trusted_headers', $this->resolveTrustedHeaders($config['trusted_headers'])); + $container->setParameter('kernel.trusted_proxies', \is_array($config['trusted_proxies']) && [0] === array_keys($config['trusted_proxies']) ? $config['trusted_proxies'][0] : $config['trusted_proxies']); + $container->setParameter('kernel.trusted_headers', [0] === array_keys($config['trusted_headers']) ? $config['trusted_headers'][0] : $config['trusted_headers']); } if (!$container->hasParameter('debug.file_link_format')) { @@ -3114,25 +3114,6 @@ private function registerHtmlSanitizerConfiguration(array $config, ContainerBuil } } - private function resolveTrustedHeaders(array $headers): int - { - $trustedHeaders = 0; - - foreach ($headers as $h) { - $trustedHeaders |= match ($h) { - 'forwarded' => Request::HEADER_FORWARDED, - 'x-forwarded-for' => Request::HEADER_X_FORWARDED_FOR, - 'x-forwarded-host' => Request::HEADER_X_FORWARDED_HOST, - 'x-forwarded-proto' => Request::HEADER_X_FORWARDED_PROTO, - 'x-forwarded-port' => Request::HEADER_X_FORWARDED_PORT, - 'x-forwarded-prefix' => Request::HEADER_X_FORWARDED_PREFIX, - default => 0, - }; - } - - return $trustedHeaders; - } - public function getXsdValidationBasePath(): string|false { return \dirname(__DIR__).'/Resources/config/schema'; diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 73e6c2025..c569a852d 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -704,19 +704,16 @@ protected static function getBundleDefaultConfig() return [ 'http_method_override' => false, 'handle_all_throwables' => true, - 'trust_x_sendfile_type_header' => false, + 'trust_x_sendfile_type_header' => '%env(bool:default::SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER)%', 'ide' => '%env(default::SYMFONY_IDE)%', 'default_locale' => 'en', 'enabled_locales' => [], 'set_locale_from_accept_language' => false, 'set_content_language_from_locale' => false, 'secret' => 's3cr3t', - 'trusted_hosts' => [], - 'trusted_headers' => [ - 'x-forwarded-for', - 'x-forwarded-port', - 'x-forwarded-proto', - ], + 'trusted_hosts' => ['%env(default::SYMFONY_TRUSTED_HOSTS)%'], + 'trusted_proxies' => ['%env(default::SYMFONY_TRUSTED_PROXIES)%'], + 'trusted_headers' => ['%env(default::SYMFONY_TRUSTED_HEADERS)%'], 'csrf_protection' => [ 'enabled' => false, ], From 919bc2e0c7dc573b29af66fd8bfebda938a17c00 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 25 Sep 2024 22:02:16 +0200 Subject: [PATCH 139/281] fix named parameters in data providers --- Tests/Functional/ApiAttributesTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Functional/ApiAttributesTest.php b/Tests/Functional/ApiAttributesTest.php index a64da93b3..350446dc7 100644 --- a/Tests/Functional/ApiAttributesTest.php +++ b/Tests/Functional/ApiAttributesTest.php @@ -705,7 +705,7 @@ public static function mapRequestPayloadProvider(): iterable yield 'invalid request mapping attribute with default value' => [ 'uri' => '/map-request-to-attribute-with-default-value.json', 'format' => 'json', - 'input' => ['comment' => '', 'approved' => '1'], + 'parameters' => ['comment' => '', 'approved' => '1'], 'content' => null, 'expectedResponse' => <<<'JSON' { @@ -964,7 +964,7 @@ public static function mapRequestPayloadProvider(): iterable yield 'invalid request mapping non-nullable attribute without default value' => [ 'uri' => '/map-request-to-non-nullable-attribute-without-default-value.json', 'format' => 'json', - 'input' => ['comment' => '', 'approved' => '1'], + 'parameters' => ['comment' => '', 'approved' => '1'], 'content' => null, 'expectedResponse' => <<<'JSON' { From f7f6aae026a9e144c09bf45da3bee86ab4c88293 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 26 Sep 2024 10:09:09 +0200 Subject: [PATCH 140/281] Remove unused imports --- Tests/DependencyInjection/Fixtures/php/notifier.php | 3 --- .../Fixtures/php/notifier_without_mailer.php | 3 --- .../Fixtures/php/notifier_without_messenger.php | 3 --- .../Fixtures/php/notifier_without_transports.php | 3 --- 4 files changed, 12 deletions(-) diff --git a/Tests/DependencyInjection/Fixtures/php/notifier.php b/Tests/DependencyInjection/Fixtures/php/notifier.php index 9abf2c880..058ec7175 100644 --- a/Tests/DependencyInjection/Fixtures/php/notifier.php +++ b/Tests/DependencyInjection/Fixtures/php/notifier.php @@ -1,8 +1,5 @@ loadFromExtension('framework', [ 'annotations' => false, 'http_method_override' => false, diff --git a/Tests/DependencyInjection/Fixtures/php/notifier_without_mailer.php b/Tests/DependencyInjection/Fixtures/php/notifier_without_mailer.php index fbb988527..107803ef9 100644 --- a/Tests/DependencyInjection/Fixtures/php/notifier_without_mailer.php +++ b/Tests/DependencyInjection/Fixtures/php/notifier_without_mailer.php @@ -1,8 +1,5 @@ loadFromExtension('framework', [ 'annotations' => false, 'http_method_override' => false, diff --git a/Tests/DependencyInjection/Fixtures/php/notifier_without_messenger.php b/Tests/DependencyInjection/Fixtures/php/notifier_without_messenger.php index 454f8ccec..0c43db7cd 100644 --- a/Tests/DependencyInjection/Fixtures/php/notifier_without_messenger.php +++ b/Tests/DependencyInjection/Fixtures/php/notifier_without_messenger.php @@ -1,8 +1,5 @@ loadFromExtension('framework', [ 'annotations' => false, 'http_method_override' => false, diff --git a/Tests/DependencyInjection/Fixtures/php/notifier_without_transports.php b/Tests/DependencyInjection/Fixtures/php/notifier_without_transports.php index 5861634e1..6392f0b33 100644 --- a/Tests/DependencyInjection/Fixtures/php/notifier_without_transports.php +++ b/Tests/DependencyInjection/Fixtures/php/notifier_without_transports.php @@ -1,8 +1,5 @@ loadFromExtension('framework', [ 'annotations' => false, 'http_method_override' => false, From 43c85605ce1452507ea09e25d1ffa0c3c281f813 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Sat, 14 Sep 2024 17:20:04 -0400 Subject: [PATCH 141/281] [Mailer] add Mailtrap webhook support --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/mailer_webhook.php | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 200310ec9..83bb23f2b 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2681,6 +2681,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co MailerBridge\Mailjet\Webhook\MailjetRequestParser::class => 'mailer.webhook.request_parser.mailjet', MailerBridge\Mailomat\Webhook\MailomatRequestParser::class => 'mailer.webhook.request_parser.mailomat', MailerBridge\Postmark\Webhook\PostmarkRequestParser::class => 'mailer.webhook.request_parser.postmark', + MailerBridge\Mailtrap\Webhook\MailtrapRequestParser::class => 'mailer.webhook.request_parser.mailtrap', MailerBridge\Resend\Webhook\ResendRequestParser::class => 'mailer.webhook.request_parser.resend', MailerBridge\Sendgrid\Webhook\SendgridRequestParser::class => 'mailer.webhook.request_parser.sendgrid', MailerBridge\Sweego\Webhook\SweegoRequestParser::class => 'mailer.webhook.request_parser.sweego', diff --git a/Resources/config/mailer_webhook.php b/Resources/config/mailer_webhook.php index f0a50800f..e6f6c425b 100644 --- a/Resources/config/mailer_webhook.php +++ b/Resources/config/mailer_webhook.php @@ -21,6 +21,8 @@ use Symfony\Component\Mailer\Bridge\Mailjet\Webhook\MailjetRequestParser; use Symfony\Component\Mailer\Bridge\Mailomat\RemoteEvent\MailomatPayloadConverter; use Symfony\Component\Mailer\Bridge\Mailomat\Webhook\MailomatRequestParser; +use Symfony\Component\Mailer\Bridge\Mailtrap\RemoteEvent\MailtrapPayloadConverter; +use Symfony\Component\Mailer\Bridge\Mailtrap\Webhook\MailtrapRequestParser; use Symfony\Component\Mailer\Bridge\Postmark\RemoteEvent\PostmarkPayloadConverter; use Symfony\Component\Mailer\Bridge\Postmark\Webhook\PostmarkRequestParser; use Symfony\Component\Mailer\Bridge\Resend\RemoteEvent\ResendPayloadConverter; @@ -62,6 +64,11 @@ ->args([service('mailer.payload_converter.postmark')]) ->alias(PostmarkRequestParser::class, 'mailer.webhook.request_parser.postmark') + ->set('mailer.payload_converter.mailtrap', MailtrapPayloadConverter::class) + ->set('mailer.webhook.request_parser.mailtrap', MailtrapRequestParser::class) + ->args([service('mailer.payload_converter.mailtrap')]) + ->alias(MailtrapRequestParser::class, 'mailer.webhook.request_parser.mailtrap') + ->set('mailer.payload_converter.resend', ResendPayloadConverter::class) ->set('mailer.webhook.request_parser.resend', ResendRequestParser::class) ->args([service('mailer.payload_converter.resend')]) From 585504cc56b3a3586ea56106e8048f865c5b4507 Mon Sep 17 00:00:00 2001 From: Daniel Gorgan Date: Fri, 27 Sep 2024 19:27:01 +0300 Subject: [PATCH 142/281] Fix git --- CHANGELOG.md | 1 + Command/TranslationUpdateCommand.php | 85 +++++++++++++++---- .../Command/TranslationUpdateCommandTest.php | 29 ++++++- 3 files changed, 97 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9a7186a4..19ce70b6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.2 --- + * Add support for `--sort` option when extracting translations with `translation:extract` command and `--force` option * Add support for setting `headers` with `Symfony\Bundle\FrameworkBundle\Controller\TemplateController` * Add `--resolve-env-vars` option to `lint:container` command * Derivate `kernel.secret` from the decryption secret when its env var is not defined diff --git a/Command/TranslationUpdateCommand.php b/Command/TranslationUpdateCommand.php index 2f61c81d6..04f812146 100644 --- a/Command/TranslationUpdateCommand.php +++ b/Command/TranslationUpdateCommand.php @@ -76,7 +76,7 @@ protected function configure(): void 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 (only works with --dump-messages)', 'asc'), + 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'), ]) ->setHelp(<<<'EOF' @@ -100,7 +100,7 @@ protected function configure(): void You can sort the output with the --sort flag: php %command.full_name% --dump-messages --sort=asc en AcmeBundle - php %command.full_name% --dump-messages --sort=desc fr + php %command.full_name% --force --sort=desc fr You can dump a tree-like structure using the yaml format with --as-tree flag: @@ -207,6 +207,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int $operation->moveMessagesToIntlDomainsIfPossible('new'); + if ($sort = $input->getOption('sort')) { + $sort = strtolower($sort); + if (!\in_array($sort, self::SORT_ORDERS, true)) { + $errorIo->error(['Wrong sort order', 'Supported formats are: '.implode(', ', self::SORT_ORDERS).'.']); + + return 1; + } + } + // show compiled list of messages if (true === $input->getOption('dump-messages')) { $extractedMessagesCount = 0; @@ -223,19 +232,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int $domainMessagesCount = \count($list); - if ($sort = $input->getOption('sort')) { - $sort = strtolower($sort); - if (!\in_array($sort, self::SORT_ORDERS, true)) { - $errorIo->error(['Wrong sort order', 'Supported formats are: '.implode(', ', self::SORT_ORDERS).'.']); - - return 1; - } - - if (self::DESC === $sort) { - rsort($list); - } else { - sort($list); - } + if (self::DESC === $sort) { + rsort($list); + } else { + sort($list); } $io->section(\sprintf('Messages extracted for domain "%s" (%d message%s)', $domain, $domainMessagesCount, $domainMessagesCount > 1 ? 's' : '')); @@ -266,7 +266,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int $bundleTransPath = end($transPaths); } - $this->writer->write($operation->getResult(), $format, ['path' => $bundleTransPath, 'default_locale' => $this->defaultLocale, 'xliff_version' => $xliffVersion, 'as_tree' => $input->getOption('as-tree'), 'inline' => $input->getOption('as-tree') ?? 0]); + $operationResult = $operation->getResult(); + if ($sort) { + $operationResult = $this->sortCatalogue($operationResult, $sort); + } + + $this->writer->write($operationResult, $format, ['path' => $bundleTransPath, 'default_locale' => $this->defaultLocale, 'xliff_version' => $xliffVersion, 'as_tree' => $input->getOption('as-tree'), 'inline' => $input->getOption('as-tree') ?? 0]); if (true === $input->getOption('dump-messages')) { $resultMessage .= ' and translation files were updated'; @@ -365,6 +370,54 @@ private function filterCatalogue(MessageCatalogue $catalogue, string $domain): M return $filteredCatalogue; } + private function sortCatalogue(MessageCatalogue $catalogue, string $sort): MessageCatalogue + { + $sortedCatalogue = new MessageCatalogue($catalogue->getLocale()); + + foreach ($catalogue->getDomains() as $domain) { + // extract intl-icu messages only + $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; + if ($intlMessages = $catalogue->all($intlDomain)) { + if (self::DESC === $sort) { + krsort($intlMessages); + } elseif (self::ASC === $sort) { + ksort($intlMessages); + } + + $sortedCatalogue->add($intlMessages, $intlDomain); + } + + // extract all messages and subtract intl-icu messages + if ($messages = array_diff($catalogue->all($domain), $intlMessages)) { + if (self::DESC === $sort) { + krsort($messages); + } elseif (self::ASC === $sort) { + ksort($messages); + } + + $sortedCatalogue->add($messages, $domain); + } + + if ($metadata = $catalogue->getMetadata('', $intlDomain)) { + foreach ($metadata as $k => $v) { + $sortedCatalogue->setMetadata($k, $v, $intlDomain); + } + } + + if ($metadata = $catalogue->getMetadata('', $domain)) { + foreach ($metadata as $k => $v) { + $sortedCatalogue->setMetadata($k, $v, $domain); + } + } + } + + foreach ($catalogue->getResources() as $resource) { + $sortedCatalogue->addResource($resource); + } + + return $sortedCatalogue; + } + private function extractMessages(string $locale, array $transPaths, string $prefix): MessageCatalogue { $extractedCatalogue = new MessageCatalogue($locale); diff --git a/Tests/Command/TranslationUpdateCommandTest.php b/Tests/Command/TranslationUpdateCommandTest.php index def589f58..2304e75ad 100644 --- a/Tests/Command/TranslationUpdateCommandTest.php +++ b/Tests/Command/TranslationUpdateCommandTest.php @@ -20,6 +20,7 @@ use Symfony\Component\HttpKernel\Bundle\BundleInterface; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Translation\Extractor\ExtractorInterface; +use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Reader\TranslationReader; use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\Writer\TranslationWriter; @@ -103,11 +104,25 @@ public function testDumpMessagesForSpecificDomain() public function testWriteMessages() { - $tester = $this->createCommandTester(['messages' => ['foo' => 'foo']]); + $tester = $this->createCommandTester(['messages' => ['foo' => 'foo', 'test' => 'test', 'bar' => 'bar']], writerMessages: ['foo', 'test', 'bar']); $tester->execute(['command' => 'translation:extract', 'locale' => 'en', 'bundle' => 'foo', '--force' => true]); $this->assertMatchesRegularExpression('/Translation files were successfully updated./', $tester->getDisplay()); } + public function testWriteSortMessages() + { + $tester = $this->createCommandTester(['messages' => ['foo' => 'foo', 'test' => 'test', 'bar' => 'bar']], writerMessages: ['bar', 'foo', 'test']); + $tester->execute(['command' => 'translation:extract', 'locale' => 'en', 'bundle' => 'foo', '--force' => true, '--sort' => 'asc']); + $this->assertMatchesRegularExpression('/Translation files were successfully updated./', $tester->getDisplay()); + } + + public function testWriteReverseSortedMessages() + { + $tester = $this->createCommandTester(['messages' => ['foo' => 'foo', 'test' => 'test', 'bar' => 'bar']], writerMessages: ['test', 'foo', 'bar']); + $tester->execute(['command' => 'translation:extract', 'locale' => 'en', 'bundle' => 'foo', '--force' => true, '--sort' => 'desc']); + $this->assertMatchesRegularExpression('/Translation files were successfully updated./', $tester->getDisplay()); + } + public function testWriteMessagesInRootDirectory() { $tester = $this->createCommandTester(['messages' => ['foo' => 'foo']]); @@ -175,7 +190,7 @@ protected function tearDown(): void $this->fs->remove($this->translationDir); } - private function createCommandTester($extractedMessages = [], $loadedMessages = [], ?KernelInterface $kernel = null, array $transPaths = [], array $codePaths = []): CommandTester + private function createCommandTester($extractedMessages = [], $loadedMessages = [], ?KernelInterface $kernel = null, array $transPaths = [], array $codePaths = [], ?array $writerMessages = null): CommandTester { $translator = $this->createMock(Translator::class); $translator @@ -212,6 +227,16 @@ function ($path, $catalogue) use ($loadedMessages) { ->willReturn( ['xlf', 'yml', 'yaml'] ); + if (null !== $writerMessages) { + $writer + ->expects($this->any()) + ->method('write') + ->willReturnCallback( + function (MessageCatalogue $catalogue) use ($writerMessages) { + $this->assertSame($writerMessages, array_keys($catalogue->all()['messages'])); + } + ); + } if (null === $kernel) { $returnValues = [ From 68ece90b3478d75523d162146a67081bf4852549 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 30 Sep 2024 15:26:46 +0200 Subject: [PATCH 143/281] [FrameworkBundle] Never hash the empty decryption key to compute kernel.secret --- Secrets/SodiumVault.php | 6 +++--- Tests/Secrets/SodiumVaultTest.php | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Secrets/SodiumVault.php b/Secrets/SodiumVault.php index f747fed14..2a8e5dcc8 100644 --- a/Secrets/SodiumVault.php +++ b/Secrets/SodiumVault.php @@ -114,7 +114,7 @@ public function reveal(string $name): ?string $this->loadKeys(); - if ('' === $this->decryptionKey) { + if ('' === $this->decryptionKey = (string) $this->decryptionKey) { $this->lastMessage = \sprintf('Secret "%s" cannot be revealed as no decryption key was found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); return null; @@ -181,8 +181,8 @@ public function loadEnvVars(): array } if ($this->derivedSecretEnvVar && !\array_key_exists($this->derivedSecretEnvVar, $envs)) { - $decryptionKey = $this->decryptionKey; - $envs[$this->derivedSecretEnvVar] = LazyString::fromCallable(static fn () => base64_encode(hash('sha256', $decryptionKey, true))); + $k = $this->decryptionKey; + $envs[$this->derivedSecretEnvVar] = LazyString::fromCallable(static fn () => '' !== ($k = (string) $k) ? base64_encode(hash('sha256', $k, true)) : ''); } return $envs; diff --git a/Tests/Secrets/SodiumVaultTest.php b/Tests/Secrets/SodiumVaultTest.php index e31e8364f..f91f4bced 100644 --- a/Tests/Secrets/SodiumVaultTest.php +++ b/Tests/Secrets/SodiumVaultTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\Secrets\SodiumVault; use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\String\LazyString; /** * @requires extension sodium @@ -84,4 +85,17 @@ public function testDerivedSecretEnvVar() $this->assertSame(['FOO', 'MY_SECRET'], array_keys($vault->loadEnvVars())); } + + public function testEmptySecretEnvVar() + { + $vault = new SodiumVault($this->secretsDir, '', 'MY_SECRET'); + $envVars = $vault->loadEnvVars(); + $envVars['MY_SECRET'] = (string) $envVars['MY_SECRET']; + $this->assertSame(['MY_SECRET' => ''], $envVars); + + $vault = new SodiumVault($this->secretsDir, LazyString::fromCallable(fn () => ''), 'MY_SECRET'); + $envVars = $vault->loadEnvVars(); + $envVars['MY_SECRET'] = (string) $envVars['MY_SECRET']; + $this->assertSame(['MY_SECRET' => ''], $envVars); + } } From 7e09e75c5fcdc8bbf769303557b54f1c3be34960 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 2 Oct 2024 10:47:28 +0200 Subject: [PATCH 144/281] Make `@var` occurrences consistent --- Tests/Functional/Bundle/TestBundle/TestBundle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Functional/Bundle/TestBundle/TestBundle.php b/Tests/Functional/Bundle/TestBundle/TestBundle.php index d0c6588b0..73cb63d1f 100644 --- a/Tests/Functional/Bundle/TestBundle/TestBundle.php +++ b/Tests/Functional/Bundle/TestBundle/TestBundle.php @@ -24,7 +24,7 @@ public function build(ContainerBuilder $container): void { parent::build($container); - /** @var $extension DependencyInjection\TestExtension */ + /** @var DependencyInjection\TestExtension $extension */ $extension = $container->getExtension('test'); if (!$container->getParameterBag() instanceof FrozenParameterBag) { From a8f57a0a7a3b8ae1bea4032541df9e76414a12ad Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 4 Oct 2024 07:56:00 +0200 Subject: [PATCH 145/281] [FrameworkBundle] Simplify the configuration class --- DependencyInjection/Configuration.php | 31 +++++++-------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index dcb4e0eed..7a3983101 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -403,10 +403,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->end() ->end() ->arrayNode('supports') - ->beforeNormalization() - ->ifString() - ->then(fn ($v) => [$v]) - ->end() + ->beforeNormalization()->castToArray()->end() ->prototype('scalar') ->cannotBeEmpty() ->validate() @@ -535,20 +532,14 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->example('is_fully_authenticated() and is_granted(\'ROLE_JOURNALIST\') and subject.getTitle() == \'My first article\'') ->end() ->arrayNode('from') - ->beforeNormalization() - ->ifString() - ->then(fn ($v) => [$v]) - ->end() + ->beforeNormalization()->castToArray()->end() ->requiresAtLeastOneElement() ->prototype('scalar') ->cannotBeEmpty() ->end() ->end() ->arrayNode('to') - ->beforeNormalization() - ->ifString() - ->then(fn ($v) => [$v]) - ->end() + ->beforeNormalization()->castToArray()->end() ->requiresAtLeastOneElement() ->prototype('scalar') ->cannotBeEmpty() @@ -933,7 +924,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e ->children() ->arrayNode('fallbacks') ->info('Defaults to the value of "default_locale".') - ->beforeNormalization()->ifString()->then(fn ($v) => [$v])->end() + ->beforeNormalization()->castToArray()->end() ->prototype('scalar')->end() ->defaultValue([]) ->end() @@ -1436,7 +1427,7 @@ private function addLockSection(ArrayNodeDefinition $rootNode, callable $enableI ->end() ->prototype('array') ->performNoDeepMerging() - ->beforeNormalization()->ifString()->then(fn ($v) => [$v])->end() + ->beforeNormalization()->castToArray()->end() ->prototype('scalar')->end() ->end() ->end() @@ -2226,7 +2217,7 @@ private function addNotifierSection(ArrayNodeDefinition $rootNode, callable $ena ->arrayNode('channel_policy') ->useAttributeAsKey('name') ->prototype('array') - ->beforeNormalization()->ifString()->then(fn ($v) => [$v])->end() + ->beforeNormalization()->castToArray()->end() ->prototype('scalar')->end() ->end() ->end() @@ -2442,18 +2433,12 @@ private function addHtmlSanitizerSection(ArrayNodeDefinition $rootNode, callable ->end() ->arrayNode('block_elements') ->info('Configures elements as blocked. Blocked elements are elements the sanitizer should remove from the input, but retain their children.') - ->beforeNormalization() - ->ifString() - ->then(fn (string $n): array => (array) $n) - ->end() + ->beforeNormalization()->castToArray()->end() ->scalarPrototype()->end() ->end() ->arrayNode('drop_elements') ->info('Configures elements as dropped. Dropped elements are elements the sanitizer should remove from the input, including their children.') - ->beforeNormalization() - ->ifString() - ->then(fn (string $n): array => (array) $n) - ->end() + ->beforeNormalization()->castToArray()->end() ->scalarPrototype()->end() ->end() ->arrayNode('allow_attributes') From 8f26278831be7bdfacb46566a65402a876405b37 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Fri, 4 Oct 2024 12:54:27 +0200 Subject: [PATCH 146/281] [FrameworkBundle][Lock] Fix certain DSNs identified as service IDs --- DependencyInjection/FrameworkExtension.php | 2 +- .../Fixtures/php/lock_named.php | 2 ++ .../Fixtures/xml/lock_named.xml | 2 ++ .../Fixtures/yml/lock_named.yml | 2 ++ .../FrameworkExtensionTestCase.php | 20 +++++++++++++------ 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 83bb23f2b..87a173fac 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2008,7 +2008,7 @@ private function registerLockConfiguration(array $config, ContainerBuilder $cont $storeDefinitions = []; foreach ($resourceStores as $resourceStore) { $storeDsn = $container->resolveEnvPlaceholders($resourceStore, null, $usedEnvs); - if (!$usedEnvs && !str_contains($resourceStore, '://')) { + if (!$usedEnvs && !str_contains($resourceStore, ':') && !\in_array($resourceStore, ['flock', 'semaphore', 'in-memory'], true)) { $resourceStore = new Reference($resourceStore); } $storeDefinition = new Definition(PersistingStoreInterface::class); diff --git a/Tests/DependencyInjection/Fixtures/php/lock_named.php b/Tests/DependencyInjection/Fixtures/php/lock_named.php index de8a8f495..cbfead830 100644 --- a/Tests/DependencyInjection/Fixtures/php/lock_named.php +++ b/Tests/DependencyInjection/Fixtures/php/lock_named.php @@ -12,5 +12,7 @@ 'bar' => 'flock', 'baz' => ['semaphore', 'flock'], 'qux' => '%env(REDIS_DSN)%', + 'corge' => 'in-memory', + 'grault' => 'mysql:host=localhost;dbname=test', ], ]); diff --git a/Tests/DependencyInjection/Fixtures/xml/lock_named.xml b/Tests/DependencyInjection/Fixtures/xml/lock_named.xml index 02659713e..7c48fa447 100644 --- a/Tests/DependencyInjection/Fixtures/xml/lock_named.xml +++ b/Tests/DependencyInjection/Fixtures/xml/lock_named.xml @@ -18,6 +18,8 @@ semaphore flock %env(REDIS_DSN)% + in-memory + mysql:host=localhost;dbname=test diff --git a/Tests/DependencyInjection/Fixtures/yml/lock_named.yml b/Tests/DependencyInjection/Fixtures/yml/lock_named.yml index 01f3af47a..04ec9fc07 100644 --- a/Tests/DependencyInjection/Fixtures/yml/lock_named.yml +++ b/Tests/DependencyInjection/Fixtures/yml/lock_named.yml @@ -12,3 +12,5 @@ framework: bar: flock baz: [semaphore, flock] qux: "%env(REDIS_DSN)%" + corge: in-memory + grault: mysql:host=localhost;dbname=test diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index fc66c889f..42017e29a 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -2423,9 +2423,9 @@ public function testDefaultLock() $storeDef = $container->getDefinition($container->getDefinition('lock.default.factory')->getArgument(0)); if (class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported()) { - self::assertEquals(new Reference('semaphore'), $storeDef->getArgument(0)); + self::assertSame('semaphore', $storeDef->getArgument(0)); } else { - self::assertEquals(new Reference('flock'), $storeDef->getArgument(0)); + self::assertSame('flock', $storeDef->getArgument(0)); } } @@ -2435,23 +2435,31 @@ public function testNamedLocks() self::assertTrue($container->hasDefinition('lock.foo.factory')); $storeDef = $container->getDefinition($container->getDefinition('lock.foo.factory')->getArgument(0)); - self::assertEquals(new Reference('semaphore'), $storeDef->getArgument(0)); + self::assertSame('semaphore', $storeDef->getArgument(0)); self::assertTrue($container->hasDefinition('lock.bar.factory')); $storeDef = $container->getDefinition($container->getDefinition('lock.bar.factory')->getArgument(0)); - self::assertEquals(new Reference('flock'), $storeDef->getArgument(0)); + self::assertSame('flock', $storeDef->getArgument(0)); self::assertTrue($container->hasDefinition('lock.baz.factory')); $storeDef = $container->getDefinition($container->getDefinition('lock.baz.factory')->getArgument(0)); self::assertIsArray($storeDefArg = $storeDef->getArgument(0)); $storeDef1 = $container->getDefinition($storeDefArg[0]); $storeDef2 = $container->getDefinition($storeDefArg[1]); - self::assertEquals(new Reference('semaphore'), $storeDef1->getArgument(0)); - self::assertEquals(new Reference('flock'), $storeDef2->getArgument(0)); + self::assertSame('semaphore', $storeDef1->getArgument(0)); + self::assertSame('flock', $storeDef2->getArgument(0)); self::assertTrue($container->hasDefinition('lock.qux.factory')); $storeDef = $container->getDefinition($container->getDefinition('lock.qux.factory')->getArgument(0)); self::assertStringContainsString('REDIS_DSN', $storeDef->getArgument(0)); + + self::assertTrue($container->hasDefinition('lock.corge.factory')); + $storeDef = $container->getDefinition($container->getDefinition('lock.corge.factory')->getArgument(0)); + self::assertSame('in-memory', $storeDef->getArgument(0)); + + self::assertTrue($container->hasDefinition('lock.grault.factory')); + $storeDef = $container->getDefinition($container->getDefinition('lock.grault.factory')->getArgument(0)); + self::assertSame('mysql:host=localhost;dbname=test', $storeDef->getArgument(0)); } public function testLockWithService() From 6e7f87fb6f189817886a00a08aa77621be0cfd2f Mon Sep 17 00:00:00 2001 From: HypeMC Date: Fri, 4 Oct 2024 18:58:29 +0200 Subject: [PATCH 147/281] [FrameworkBundle] Add support for setting headers to `TemplateController::__invoke()` --- Controller/TemplateController.php | 4 ++-- Tests/Controller/TemplateControllerTest.php | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Controller/TemplateController.php b/Controller/TemplateController.php index 125e04f62..c08ea347b 100644 --- a/Controller/TemplateController.php +++ b/Controller/TemplateController.php @@ -71,8 +71,8 @@ public function templateAction(string $template, ?int $maxAge = null, ?int $shar /** * @param int $statusCode The HTTP status code (200 "OK" by default) */ - public function __invoke(string $template, ?int $maxAge = null, ?int $sharedAge = null, ?bool $private = null, array $context = [], int $statusCode = 200): Response + public function __invoke(string $template, ?int $maxAge = null, ?int $sharedAge = null, ?bool $private = null, array $context = [], int $statusCode = 200, array $headers = []): Response { - return $this->templateAction($template, $maxAge, $sharedAge, $private, $context, $statusCode); + return $this->templateAction($template, $maxAge, $sharedAge, $private, $context, $statusCode, $headers); } } diff --git a/Tests/Controller/TemplateControllerTest.php b/Tests/Controller/TemplateControllerTest.php index 28f63ac01..1d98f4f8e 100644 --- a/Tests/Controller/TemplateControllerTest.php +++ b/Tests/Controller/TemplateControllerTest.php @@ -21,6 +21,19 @@ */ class TemplateControllerTest extends TestCase { + public function testMethodSignaturesMatch() + { + $ref = new \ReflectionClass(TemplateController::class); + + $templateActionRef = $ref->getMethod('templateAction'); + $invokeRef = $ref->getMethod('__invoke'); + + $this->assertSame( + array_map(strval(...), $templateActionRef->getParameters()), + array_map(strval(...), $invokeRef->getParameters()), + ); + } + public function testTwig() { $twig = $this->createMock(Environment::class); @@ -82,7 +95,10 @@ public function testStatusCode() $controller = new TemplateController($twig); $this->assertSame(201, $controller->templateAction($templateName, null, null, null, [], $statusCode)->getStatusCode()); + $this->assertSame(201, $controller($templateName, null, null, null, [], $statusCode)->getStatusCode()); + $this->assertSame(200, $controller->templateAction($templateName)->getStatusCode()); + $this->assertSame(200, $controller($templateName)->getStatusCode()); } public function testHeaders() @@ -96,6 +112,9 @@ public function testHeaders() $controller = new TemplateController($twig); $this->assertSame('image/svg+xml', $controller->templateAction($templateName, headers: ['Content-Type' => 'image/svg+xml'])->headers->get('Content-Type')); + $this->assertSame('image/svg+xml', $controller($templateName, headers: ['Content-Type' => 'image/svg+xml'])->headers->get('Content-Type')); + $this->assertNull($controller->templateAction($templateName)->headers->get('Content-Type')); + $this->assertNull($controller($templateName)->headers->get('Content-Type')); } } From 43607eef8f1c2ef9e8d27b813b5dd463c90588eb Mon Sep 17 00:00:00 2001 From: johan Vlaar Date: Mon, 7 Oct 2024 09:47:58 +0200 Subject: [PATCH 148/281] [Webhook] Add Mailchimp webhook (fixes #50285) --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/mailer_webhook.php | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 87a173fac..826e8fb0f 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2677,6 +2677,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co $webhookRequestParsers = [ MailerBridge\Brevo\Webhook\BrevoRequestParser::class => 'mailer.webhook.request_parser.brevo', MailerBridge\MailerSend\Webhook\MailerSendRequestParser::class => 'mailer.webhook.request_parser.mailersend', + MailerBridge\Mailchimp\Webhook\MailchimpRequestParser::class => 'mailer.webhook.request_parser.mailchimp', MailerBridge\Mailgun\Webhook\MailgunRequestParser::class => 'mailer.webhook.request_parser.mailgun', MailerBridge\Mailjet\Webhook\MailjetRequestParser::class => 'mailer.webhook.request_parser.mailjet', MailerBridge\Mailomat\Webhook\MailomatRequestParser::class => 'mailer.webhook.request_parser.mailomat', diff --git a/Resources/config/mailer_webhook.php b/Resources/config/mailer_webhook.php index e6f6c425b..c574324db 100644 --- a/Resources/config/mailer_webhook.php +++ b/Resources/config/mailer_webhook.php @@ -13,6 +13,8 @@ use Symfony\Component\Mailer\Bridge\Brevo\RemoteEvent\BrevoPayloadConverter; use Symfony\Component\Mailer\Bridge\Brevo\Webhook\BrevoRequestParser; +use Symfony\Component\Mailer\Bridge\Mailchimp\RemoteEvent\MailchimpPayloadConverter; +use Symfony\Component\Mailer\Bridge\Mailchimp\Webhook\MailchimpRequestParser; use Symfony\Component\Mailer\Bridge\MailerSend\RemoteEvent\MailerSendPayloadConverter; use Symfony\Component\Mailer\Bridge\MailerSend\Webhook\MailerSendRequestParser; use Symfony\Component\Mailer\Bridge\Mailgun\RemoteEvent\MailgunPayloadConverter; @@ -83,5 +85,10 @@ ->set('mailer.webhook.request_parser.sweego', SweegoRequestParser::class) ->args([service('mailer.payload_converter.sweego')]) ->alias(SweegoRequestParser::class, 'mailer.webhook.request_parser.sweego') + + ->set('mailer.payload_converter.mailchimp', MailchimpPayloadConverter::class) + ->set('mailer.webhook.request_parser.mailchimp', MailchimpRequestParser::class) + ->args([service('mailer.payload_converter.mailchimp')]) + ->alias(MailchimpRequestParser::class, 'mailer.webhook.request_parser.mailchimp') ; }; From 062c0a75d537172a5403f6474bc0ab3207d15225 Mon Sep 17 00:00:00 2001 From: JoppeDC Date: Thu, 25 Jul 2024 12:32:12 +0200 Subject: [PATCH 149/281] [FrameworkBundle] Finetune `AboutCommand` --- Command/AboutCommand.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Command/AboutCommand.php b/Command/AboutCommand.php index 2c6cb440f..f4281cd21 100644 --- a/Command/AboutCommand.php +++ b/Command/AboutCommand.php @@ -25,6 +25,7 @@ * A console command to display information about the current installation. * * @author Roland Franssen + * @author Joppe De Cuyper * * @final */ @@ -57,6 +58,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $buildDir = $kernel->getCacheDir(); } + $xdebugMode = getenv('XDEBUG_MODE') ?: \ini_get('xdebug.mode'); + $rows = [ ['Symfony'], new TableSeparator(), @@ -81,9 +84,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int ['Architecture', (\PHP_INT_SIZE * 8).' bits'], ['Intl locale', class_exists(\Locale::class, false) && \Locale::getDefault() ? \Locale::getDefault() : 'n/a'], ['Timezone', date_default_timezone_get().' ('.(new \DateTimeImmutable())->format(\DateTimeInterface::W3C).')'], - ['OPcache', \extension_loaded('Zend OPcache') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOL) ? 'true' : 'false'], - ['APCu', \extension_loaded('apcu') && filter_var(\ini_get('apc.enabled'), \FILTER_VALIDATE_BOOL) ? 'true' : 'false'], - ['Xdebug', \extension_loaded('xdebug') ? 'true' : 'false'], + ['OPcache', \extension_loaded('Zend OPcache') ? (filter_var(\ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN) ? 'Enabled' : 'Not enabled') : 'Not installed'], + ['APCu', \extension_loaded('apcu') ? (filter_var(\ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? 'Enabled' : 'Not enabled') : 'Not installed'], + ['Xdebug', \extension_loaded('xdebug') ? ($xdebugMode && $xdebugMode !== 'off' ? 'Enabled (' . $xdebugMode . ')' : 'Not enabled') : 'Not installed'], ]; $io->table([], $rows); From 91e5b6517da49b058346d33528184e5a0a0f28bf Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 26 Aug 2024 16:50:35 +0200 Subject: [PATCH 150/281] [Security] Implement stateless headers/cookies-based CSRF protection --- CHANGELOG.md | 2 ++ DependencyInjection/Configuration.php | 25 ++++++++++++++--- DependencyInjection/FrameworkExtension.php | 27 ++++++++++++++++--- Resources/config/form_csrf.php | 2 ++ Resources/config/schema/symfony-1.0.xsd | 13 +++++++++ Resources/config/security_csrf.php | 14 ++++++++++ .../DependencyInjection/ConfigurationTest.php | 7 ++++- composer.json | 2 +- 8 files changed, 84 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19ce70b6b..3d6acb26a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ CHANGELOG * Deprecate making `cache.app` adapter taggable, use the `cache.app.taggable` adapter instead * Enable `json_decode_detailed_errors` in the default serializer context in debug mode by default when `seld/jsonlint` is installed * Register `Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter` as a service named `serializer.name_converter.snake_case_to_camel_case` if available + * Add `framework.csrf_protection.stateless_token_ids`, `.cookie_name`, and `.check_header` options to use stateless headers/cookies-based CSRF protection + * Add `framework.form.csrf_protection.field_attr` option * Deprecate `session.sid_length` and `session.sid_bits_per_character` config options * Add the ability to use an existing service as a lock/semaphore resource * Add support for configuring multiple serializer instances via the configuration diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 7a3983101..9abd10e73 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -209,9 +209,22 @@ private function addCsrfSection(ArrayNodeDefinition $rootNode): void ->treatTrueLike(['enabled' => true]) ->treatNullLike(['enabled' => true]) ->addDefaultsIfNotSet() + ->fixXmlConfig('stateless_token_id') ->children() - // defaults to framework.session.enabled && !class_exists(FullStack::class) && interface_exists(CsrfTokenManagerInterface::class) - ->booleanNode('enabled')->defaultNull()->end() + // defaults to framework.csrf_protection.stateless_token_ids || framework.session.enabled && !class_exists(FullStack::class) && interface_exists(CsrfTokenManagerInterface::class) + ->scalarNode('enabled')->defaultNull()->end() + ->arrayNode('stateless_token_ids') + ->scalarPrototype()->end() + ->info('Enable headers/cookies-based CSRF validation for the listed token ids.') + ->end() + ->scalarNode('check_header') + ->defaultFalse() + ->info('Whether to check the CSRF token in a header in addition to a cookie when using stateless protection.') + ->end() + ->scalarNode('cookie_name') + ->defaultValue('csrf-token') + ->info('The name of the cookie to use when using stateless protection.') + ->end() ->end() ->end() ->end() @@ -232,8 +245,14 @@ private function addFormSection(ArrayNodeDefinition $rootNode, callable $enableI ->treatNullLike(['enabled' => true]) ->addDefaultsIfNotSet() ->children() - ->booleanNode('enabled')->defaultNull()->end() // defaults to framework.csrf_protection.enabled + ->scalarNode('enabled')->defaultNull()->end() // defaults to framework.csrf_protection.enabled + ->scalarNode('token_id')->defaultNull()->end() ->scalarNode('field_name')->defaultValue('_token')->end() + ->arrayNode('field_attr') + ->performNoDeepMerging() + ->scalarPrototype()->end() + ->defaultValue(['data-controller' => 'csrf-protection']) + ->end() ->end() ->end() ->end() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 826e8fb0f..139379771 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -464,7 +464,7 @@ public function load(array $configs, ContainerBuilder $container): void // csrf depends on session being registered if (null === $config['csrf_protection']['enabled']) { - $this->writeConfigEnabled('csrf_protection', $this->readConfigEnabled('session', $container, $config['session']) && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']), $config['csrf_protection']); + $this->writeConfigEnabled('csrf_protection', $config['csrf_protection']['stateless_token_ids'] || $this->readConfigEnabled('session', $container, $config['session']) && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']), $config['csrf_protection']); } $this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader); @@ -765,6 +765,10 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont $container->setParameter('form.type_extension.csrf.enabled', true); $container->setParameter('form.type_extension.csrf.field_name', $config['form']['csrf_protection']['field_name']); + $container->setParameter('form.type_extension.csrf.field_attr', $config['form']['csrf_protection']['field_attr']); + + $container->getDefinition('form.type_extension.csrf') + ->replaceArgument(7, $config['form']['csrf_protection']['token_id']); } else { $container->setParameter('form.type_extension.csrf.enabled', false); } @@ -1815,8 +1819,7 @@ private function registerSecurityCsrfConfiguration(array $config, ContainerBuild if (!class_exists(\Symfony\Component\Security\Csrf\CsrfToken::class)) { throw new LogicException('CSRF support cannot be enabled as the Security CSRF component is not installed. Try running "composer require symfony/security-csrf".'); } - - if (!$this->isInitializedConfigEnabled('session')) { + if (!$config['stateless_token_ids'] && !$this->isInitializedConfigEnabled('session')) { throw new \LogicException('CSRF protection needs sessions to be enabled.'); } @@ -1826,6 +1829,24 @@ private function registerSecurityCsrfConfiguration(array $config, ContainerBuild if (!class_exists(CsrfExtension::class)) { $container->removeDefinition('twig.extension.security_csrf'); } + + if (!$config['stateless_token_ids']) { + $container->removeDefinition('security.csrf.same_origin_token_manager'); + + return; + } + + $container->getDefinition('security.csrf.same_origin_token_manager') + ->replaceArgument(3, $config['stateless_token_ids']) + ->replaceArgument(4, $config['check_header']) + ->replaceArgument(5, $config['cookie_name']); + + if (!$this->isInitializedConfigEnabled('session')) { + $container->setAlias('security.csrf.token_manager', 'security.csrf.same_origin_token_manager'); + $container->getDefinition('security.csrf.same_origin_token_manager') + ->setDecoratedService(null) + ->replaceArgument(2, null); + } } private function registerSerializerConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void diff --git a/Resources/config/form_csrf.php b/Resources/config/form_csrf.php index c8e5e973e..c63d087c8 100644 --- a/Resources/config/form_csrf.php +++ b/Resources/config/form_csrf.php @@ -23,6 +23,8 @@ service('translator')->nullOnInvalid(), param('validator.translation_domain'), service('form.server_params'), + param('form.type_extension.csrf.field_attr'), + abstract_arg('framework.form.csrf_protection.token_id'), ]) ->tag('form.type_extension') ; diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index 64e9c76cb..ed7cc744f 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -71,12 +71,25 @@ + + + + + + + + + + + + + diff --git a/Resources/config/security_csrf.php b/Resources/config/security_csrf.php index bad2284bf..ca5d69be3 100644 --- a/Resources/config/security_csrf.php +++ b/Resources/config/security_csrf.php @@ -15,6 +15,7 @@ use Symfony\Bridge\Twig\Extension\CsrfRuntime; use Symfony\Component\Security\Csrf\CsrfTokenManager; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Security\Csrf\SameOriginCsrfTokenManager; use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface; use Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator; use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage; @@ -46,5 +47,18 @@ ->set('twig.extension.security_csrf', CsrfExtension::class) ->tag('twig.extension') + + ->set('security.csrf.same_origin_token_manager', SameOriginCsrfTokenManager::class) + ->decorate('security.csrf.token_manager') + ->args([ + service('request_stack'), + service('logger')->nullOnInvalid(), + service('.inner'), + abstract_arg('framework.csrf_protection.stateless_token_ids'), + abstract_arg('framework.csrf_protection.check_header'), + abstract_arg('framework.csrf_protection.cookie_name'), + ]) + ->tag('monolog.logger', ['channel' => 'request']) + ->tag('kernel.event_listener', ['event' => 'kernel.response', 'method' => 'onKernelResponse']) ; }; diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index c569a852d..53706d2e0 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -715,13 +715,18 @@ protected static function getBundleDefaultConfig() 'trusted_proxies' => ['%env(default::SYMFONY_TRUSTED_PROXIES)%'], 'trusted_headers' => ['%env(default::SYMFONY_TRUSTED_HEADERS)%'], 'csrf_protection' => [ - 'enabled' => false, + 'enabled' => null, + 'cookie_name' => 'csrf-token', + 'check_header' => false, + 'stateless_token_ids' => [], ], 'form' => [ 'enabled' => !class_exists(FullStack::class), 'csrf_protection' => [ 'enabled' => null, // defaults to csrf_protection.enabled 'field_name' => '_token', + 'field_attr' => ['data-controller' => 'csrf-protection'], + 'token_id' => null, ], ], 'esi' => ['enabled' => false], diff --git a/composer.json b/composer.json index 3613ba8eb..af83a9a13 100644 --- a/composer.json +++ b/composer.json @@ -96,7 +96,7 @@ "symfony/runtime": "<6.4.13|>=7.0,<7.1.6", "symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4", "symfony/serializer": "<6.4", - "symfony/security-csrf": "<6.4", + "symfony/security-csrf": "<7.2", "symfony/security-core": "<6.4", "symfony/stopwatch": "<6.4", "symfony/translation": "<6.4", From d609ce6ce34bb4c295f55c5ea6eea93b4f7954ee Mon Sep 17 00:00:00 2001 From: HypeMC Date: Thu, 10 Oct 2024 10:12:22 +0200 Subject: [PATCH 151/281] [FrameworkBundle] Fix `NullStore` registration --- DependencyInjection/FrameworkExtension.php | 2 +- Tests/DependencyInjection/Fixtures/php/lock_named.php | 1 + Tests/DependencyInjection/Fixtures/xml/lock_named.xml | 1 + Tests/DependencyInjection/Fixtures/yml/lock_named.yml | 1 + Tests/DependencyInjection/FrameworkExtensionTestCase.php | 4 ++++ 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 139379771..62277c9bb 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2029,7 +2029,7 @@ private function registerLockConfiguration(array $config, ContainerBuilder $cont $storeDefinitions = []; foreach ($resourceStores as $resourceStore) { $storeDsn = $container->resolveEnvPlaceholders($resourceStore, null, $usedEnvs); - if (!$usedEnvs && !str_contains($resourceStore, ':') && !\in_array($resourceStore, ['flock', 'semaphore', 'in-memory'], true)) { + if (!$usedEnvs && !str_contains($resourceStore, ':') && !\in_array($resourceStore, ['flock', 'semaphore', 'in-memory', 'null'], true)) { $resourceStore = new Reference($resourceStore); } $storeDefinition = new Definition(PersistingStoreInterface::class); diff --git a/Tests/DependencyInjection/Fixtures/php/lock_named.php b/Tests/DependencyInjection/Fixtures/php/lock_named.php index cbfead830..c03a7fa71 100644 --- a/Tests/DependencyInjection/Fixtures/php/lock_named.php +++ b/Tests/DependencyInjection/Fixtures/php/lock_named.php @@ -14,5 +14,6 @@ 'qux' => '%env(REDIS_DSN)%', 'corge' => 'in-memory', 'grault' => 'mysql:host=localhost;dbname=test', + 'garply' => 'null', ], ]); diff --git a/Tests/DependencyInjection/Fixtures/xml/lock_named.xml b/Tests/DependencyInjection/Fixtures/xml/lock_named.xml index 7c48fa447..b8d4b4a3f 100644 --- a/Tests/DependencyInjection/Fixtures/xml/lock_named.xml +++ b/Tests/DependencyInjection/Fixtures/xml/lock_named.xml @@ -20,6 +20,7 @@ %env(REDIS_DSN)% in-memory mysql:host=localhost;dbname=test + null diff --git a/Tests/DependencyInjection/Fixtures/yml/lock_named.yml b/Tests/DependencyInjection/Fixtures/yml/lock_named.yml index 04ec9fc07..631574030 100644 --- a/Tests/DependencyInjection/Fixtures/yml/lock_named.yml +++ b/Tests/DependencyInjection/Fixtures/yml/lock_named.yml @@ -14,3 +14,4 @@ framework: qux: "%env(REDIS_DSN)%" corge: in-memory grault: mysql:host=localhost;dbname=test + garply: 'null' diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 4bccbb71f..016ae507b 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -2460,6 +2460,10 @@ public function testNamedLocks() self::assertTrue($container->hasDefinition('lock.grault.factory')); $storeDef = $container->getDefinition($container->getDefinition('lock.grault.factory')->getArgument(0)); self::assertSame('mysql:host=localhost;dbname=test', $storeDef->getArgument(0)); + + self::assertTrue($container->hasDefinition('lock.garply.factory')); + $storeDef = $container->getDefinition($container->getDefinition('lock.garply.factory')->getArgument(0)); + self::assertSame('null', $storeDef->getArgument(0)); } public function testLockWithService() From 6800168c2ea7c4b97d027ef06c8f669474773e83 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Thu, 10 Oct 2024 18:09:26 +0800 Subject: [PATCH 152/281] [Notifier] Add LINE Bot bridge --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_transports.php | 1 + 2 files changed, 2 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 139379771..c4ea185b8 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2839,6 +2839,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\JoliNotif\JoliNotifTransportFactory::class => 'notifier.transport_factory.joli-notif', NotifierBridge\KazInfoTeh\KazInfoTehTransportFactory::class => 'notifier.transport_factory.kaz-info-teh', NotifierBridge\LightSms\LightSmsTransportFactory::class => 'notifier.transport_factory.light-sms', + NotifierBridge\LineBot\LineBotTransportFactory::class => 'notifier.transport_factory.line-bot', NotifierBridge\LineNotify\LineNotifyTransportFactory::class => 'notifier.transport_factory.line-notify', NotifierBridge\LinkedIn\LinkedInTransportFactory::class => 'notifier.transport_factory.linked-in', NotifierBridge\Lox24\Lox24TransportFactory::class => 'notifier.transport_factory.lox24', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index 7c8002993..073eda08b 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -32,6 +32,7 @@ 'fake-chat' => Bridge\FakeChat\FakeChatTransportFactory::class, 'firebase' => Bridge\Firebase\FirebaseTransportFactory::class, 'google-chat' => Bridge\GoogleChat\GoogleChatTransportFactory::class, + 'line-bot' => Bridge\LineBot\LineBotTransportFactory::class, 'line-notify' => Bridge\LineNotify\LineNotifyTransportFactory::class, 'linked-in' => Bridge\LinkedIn\LinkedInTransportFactory::class, 'mastodon' => Bridge\Mastodon\MastodonTransportFactory::class, From 0bb13689c1d865c2395d0b2228c240509daedc92 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 17 Oct 2024 13:48:28 +0200 Subject: [PATCH 153/281] [FrameworkBundle] Bump `symfony/http-kernel` --- Tests/Functional/ApiAttributesTest.php | 26 -------------------------- composer.json | 6 +++--- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/Tests/Functional/ApiAttributesTest.php b/Tests/Functional/ApiAttributesTest.php index 350446dc7..cf5c384ba 100644 --- a/Tests/Functional/ApiAttributesTest.php +++ b/Tests/Functional/ApiAttributesTest.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; -use Composer\InstalledVersions; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -165,18 +164,6 @@ public static function mapQueryStringProvider(): iterable } JSON; - $httpKernelVersion = InstalledVersions::getVersion('symfony/http-kernel'); - if ($httpKernelVersion && version_compare($httpKernelVersion, '7.2.0', '<')) { - $expectedResponse = <<<'JSON' - { - "type": "https:\/\/tools.ietf.org\/html\/rfc2616#section-10", - "title": "An error occurred", - "status": 404, - "detail": "Not Found" - } - JSON; - } - yield 'empty query string mapping non-nullable attribute without default value' => [ 'uri' => '/map-query-string-to-non-nullable-attribute-without-default-value.json', 'query' => [], @@ -750,19 +737,6 @@ public static function mapRequestPayloadProvider(): iterable } JSON; - $httpKernelVersion = InstalledVersions::getVersion('symfony/http-kernel'); - if ($httpKernelVersion && version_compare($httpKernelVersion, '7.2.0', '<')) { - $expectedStatusCode = 422; - $expectedResponse = <<<'JSON' - { - "type": "https:\/\/tools.ietf.org\/html\/rfc2616#section-10", - "title": "An error occurred", - "status": 422, - "detail": "Unprocessable Content" - } - JSON; - } - yield 'empty request mapping non-nullable attribute without default value' => [ 'uri' => '/map-request-to-non-nullable-attribute-without-default-value.json', 'format' => 'json', diff --git a/composer.json b/composer.json index af83a9a13..8d97985e7 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ "symfony/error-handler": "^6.4|^7.0", "symfony/event-dispatcher": "^6.4|^7.0", "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", + "symfony/http-kernel": "^7.2", "symfony/polyfill-mbstring": "~1.0", "symfony/filesystem": "^7.1", "symfony/finder": "^6.4|^7.0", @@ -59,7 +59,7 @@ "symfony/scheduler": "^6.4.4|^7.0.4", "symfony/security-bundle": "^6.4|^7.0", "symfony/semaphore": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", + "symfony/serializer": "^7.1", "symfony/stopwatch": "^6.4|^7.0", "symfony/string": "^6.4|^7.0", "symfony/translation": "^6.4|^7.0", @@ -95,9 +95,9 @@ "symfony/property-access": "<6.4", "symfony/runtime": "<6.4.13|>=7.0,<7.1.6", "symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4", - "symfony/serializer": "<6.4", "symfony/security-csrf": "<7.2", "symfony/security-core": "<6.4", + "symfony/serializer": "<7.1", "symfony/stopwatch": "<6.4", "symfony/translation": "<6.4", "symfony/twig-bridge": "<6.4", From 5ee045beeb38e62931be7d0dfdc0194c168da0c3 Mon Sep 17 00:00:00 2001 From: Mathieu Santostefano Date: Sun, 8 Sep 2024 13:40:02 +0200 Subject: [PATCH 154/281] Add Sweego Notifier bridge --- DependencyInjection/FrameworkExtension.php | 2 ++ Resources/config/notifier_transports.php | 1 + Resources/config/notifier_webhook.php | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index c4ea185b8..a19c5a863 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2881,6 +2881,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\SmsSluzba\SmsSluzbaTransportFactory::class => 'notifier.transport_factory.sms-sluzba', NotifierBridge\Smsense\SmsenseTransportFactory::class => 'notifier.transport_factory.smsense', NotifierBridge\SpotHit\SpotHitTransportFactory::class => 'notifier.transport_factory.spot-hit', + NotifierBridge\Sweego\SweegoTransportFactory::class => 'notifier.transport_factory.sweego', NotifierBridge\Telegram\TelegramTransportFactory::class => 'notifier.transport_factory.telegram', NotifierBridge\Telnyx\TelnyxTransportFactory::class => 'notifier.transport_factory.telnyx', NotifierBridge\Termii\TermiiTransportFactory::class => 'notifier.transport_factory.termii', @@ -2948,6 +2949,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ $loader->load('notifier_webhook.php'); $webhookRequestParsers = [ + NotifierBridge\Sweego\Webhook\SweegoRequestParser::class => 'notifier.webhook.request_parser.sweego', NotifierBridge\Twilio\Webhook\TwilioRequestParser::class => 'notifier.webhook.request_parser.twilio', NotifierBridge\Vonage\Webhook\VonageRequestParser::class => 'notifier.webhook.request_parser.vonage', ]; diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index 073eda08b..f28007dec 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -108,6 +108,7 @@ 'smsense' => Bridge\Smsense\SmsenseTransportFactory::class, 'smsmode' => Bridge\Smsmode\SmsmodeTransportFactory::class, 'spot-hit' => Bridge\SpotHit\SpotHitTransportFactory::class, + 'sweego' => Bridge\Sweego\SweegoTransportFactory::class, 'telnyx' => Bridge\Telnyx\TelnyxTransportFactory::class, 'termii' => Bridge\Termii\TermiiTransportFactory::class, 'turbo-sms' => Bridge\TurboSms\TurboSmsTransportFactory::class, diff --git a/Resources/config/notifier_webhook.php b/Resources/config/notifier_webhook.php index fc541fd99..6447f4139 100644 --- a/Resources/config/notifier_webhook.php +++ b/Resources/config/notifier_webhook.php @@ -11,11 +11,15 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; +use Symfony\Component\Notifier\Bridge\Sweego\Webhook\SweegoRequestParser; use Symfony\Component\Notifier\Bridge\Twilio\Webhook\TwilioRequestParser; use Symfony\Component\Notifier\Bridge\Vonage\Webhook\VonageRequestParser; return static function (ContainerConfigurator $container) { $container->services() + ->set('notifier.webhook.request_parser.sweego', SweegoRequestParser::class) + ->alias(SweegoRequestParser::class, 'notifier.webhook.request_parser.sweego') + ->set('notifier.webhook.request_parser.twilio', TwilioRequestParser::class) ->alias(TwilioRequestParser::class, 'notifier.webhook.request_parser.twilio') From 6e695b99d307892e64ccedef0b95e01516092eb2 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 22 Oct 2024 11:46:04 +0200 Subject: [PATCH 155/281] do not access typed properties before initialization --- KernelBrowser.php | 2 +- Tests/KernelBrowserTest.php | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/KernelBrowser.php b/KernelBrowser.php index e06b9a056..a77f97ea0 100644 --- a/KernelBrowser.php +++ b/KernelBrowser.php @@ -56,7 +56,7 @@ public function getKernel(): KernelInterface */ public function getProfile(): HttpProfile|false|null { - if (null === $this->response || !$this->getContainer()->has('profiler')) { + if (!isset($this->response) || !$this->getContainer()->has('profiler')) { return false; } diff --git a/Tests/KernelBrowserTest.php b/Tests/KernelBrowserTest.php index 1e462f7d0..4e62b5ee7 100644 --- a/Tests/KernelBrowserTest.php +++ b/Tests/KernelBrowserTest.php @@ -14,6 +14,7 @@ use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Bundle\FrameworkBundle\Tests\Functional\AbstractWebTestCase; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\KernelInterface; class KernelBrowserTest extends AbstractWebTestCase { @@ -61,6 +62,13 @@ public function testRequestAfterKernelShutdownAndPerformedRequest() $client->request('GET', '/'); } + public function testGetProfileWithoutRequest() + { + $browser = new KernelBrowser($this->createMock(KernelInterface::class)); + + $this->assertFalse($browser->getProfile()); + } + private function getKernelMock() { $mock = $this->getMockBuilder($this->getKernelClass()) From 81bf4e0b09ad76c7a01e4d0abe6785354e5f4e57 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Wed, 14 Aug 2024 15:51:51 +0200 Subject: [PATCH 156/281] [TypeInfo] Redesign Type methods and nullability --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index af83a9a13..fd058671c 100644 --- a/composer.json +++ b/composer.json @@ -64,7 +64,7 @@ "symfony/string": "^6.4|^7.0", "symfony/translation": "^6.4|^7.0", "symfony/twig-bundle": "^6.4|^7.0", - "symfony/type-info": "^7.1", + "symfony/type-info": "^7.2", "symfony/validator": "^6.4|^7.0", "symfony/workflow": "^6.4|^7.0", "symfony/yaml": "^6.4|^7.0", From 47e180938e1468a0d07ffbaca83e9310b5826147 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 18 Oct 2024 16:04:52 +0200 Subject: [PATCH 157/281] Remove always true/false occurrences --- Console/Descriptor/TextDescriptor.php | 4 ++-- Tests/Command/XliffLintCommandTest.php | 4 +--- Tests/Command/YamlLintCommandTest.php | 4 +--- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Console/Descriptor/TextDescriptor.php b/Console/Descriptor/TextDescriptor.php index 6ee8b04a1..5efaab496 100644 --- a/Console/Descriptor/TextDescriptor.php +++ b/Console/Descriptor/TextDescriptor.php @@ -172,7 +172,7 @@ protected function describeContainerService(object $service, array $options = [] $options['output']->table( ['Service ID', 'Class'], [ - [$options['id'] ?? '-', $service::class], + [$options['id'], $service::class], ] ); } @@ -333,7 +333,7 @@ protected function describeContainerDefinition(Definition $definition, array $op $tableRows[] = ['Autoconfigured', $definition->isAutoconfigured() ? 'yes' : 'no']; if ($definition->getFile()) { - $tableRows[] = ['Required File', $definition->getFile() ?: '-']; + $tableRows[] = ['Required File', $definition->getFile()]; } if ($factory = $definition->getFactory()) { diff --git a/Tests/Command/XliffLintCommandTest.php b/Tests/Command/XliffLintCommandTest.php index db32bc19c..d5495ada9 100644 --- a/Tests/Command/XliffLintCommandTest.php +++ b/Tests/Command/XliffLintCommandTest.php @@ -64,9 +64,7 @@ private function createCommandTester($application = null): CommandTester $command = $application->find('lint:xliff'); - if ($application) { - $command->setApplication($application); - } + $command->setApplication($application); return new CommandTester($command); } diff --git a/Tests/Command/YamlLintCommandTest.php b/Tests/Command/YamlLintCommandTest.php index 08f4a7526..ec2093119 100644 --- a/Tests/Command/YamlLintCommandTest.php +++ b/Tests/Command/YamlLintCommandTest.php @@ -112,9 +112,7 @@ private function createCommandTester($application = null): CommandTester $command = $application->find('lint:yaml'); - if ($application) { - $command->setApplication($application); - } + $command->setApplication($application); return new CommandTester($command); } From cd12cd43c0be653c8dfc4beefc1dec687a7e1e98 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 23 Oct 2024 10:11:04 +0200 Subject: [PATCH 158/281] revert allowing Twig 4 As Twig 4 will not be released before Symfony 7.2 we should not claim compatibility before a stable Twig 4 release. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8d97985e7..9b3e7c86e 100644 --- a/composer.json +++ b/composer.json @@ -73,7 +73,7 @@ "symfony/web-link": "^6.4|^7.0", "symfony/webhook": "^7.2", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "twig/twig": "^3.12|^4.0" + "twig/twig": "^3.12" }, "conflict": { "doctrine/persistence": "<1.3", From c2c08eb9aa3b9c73fa2ad5531d4b174f6085da5c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 23 Oct 2024 10:20:11 +0200 Subject: [PATCH 159/281] fix null store handling in XML configs --- DependencyInjection/FrameworkExtension.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index dab5f0c69..a7749cd30 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2028,6 +2028,10 @@ private function registerLockConfiguration(array $config, ContainerBuilder $cont // Generate stores $storeDefinitions = []; foreach ($resourceStores as $resourceStore) { + if (null === $resourceStore) { + $resourceStore = 'null'; + } + $storeDsn = $container->resolveEnvPlaceholders($resourceStore, null, $usedEnvs); if (!$usedEnvs && !str_contains($resourceStore, ':') && !\in_array($resourceStore, ['flock', 'semaphore', 'in-memory', 'null'], true)) { $resourceStore = new Reference($resourceStore); From 255a07d200f0e72fb2f4d90d082fd73c2b613ecc Mon Sep 17 00:00:00 2001 From: jawira Date: Sun, 6 Oct 2024 10:45:55 +0200 Subject: [PATCH 160/281] [FrameworkBundle] Add `--no-fill` option to `translation:extract` command --- CHANGELOG.md | 1 + Command/TranslationUpdateCommand.php | 25 +++++++++--- .../Command/TranslationUpdateCommandTest.php | 40 +++++++++++++++++++ 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d6acb26a..3227eddc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ CHANGELOG * Add the ability to use an existing service as a lock/semaphore resource * Add support for configuring multiple serializer instances via the configuration * Add support for `SYMFONY_TRUSTED_PROXIES`, `SYMFONY_TRUSTED_HEADERS`, `SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER` and `SYMFONY_TRUSTED_HOSTS` env vars + * Add `--no-fill` option to `translation:extract` command 7.1 --- diff --git a/Command/TranslationUpdateCommand.php b/Command/TranslationUpdateCommand.php index 04f812146..b26d3f9ad 100644 --- a/Command/TranslationUpdateCommand.php +++ b/Command/TranslationUpdateCommand.php @@ -19,7 +19,6 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\HttpKernel\KernelInterface; @@ -49,6 +48,7 @@ class TranslationUpdateCommand extends Command 'xlf12' => ['xlf', '1.2'], 'xlf20' => ['xlf', '2.0'], ]; + private const NO_FILL_PREFIX = "\0NoFill\0"; public function __construct( private TranslationWriterInterface $writer, @@ -71,6 +71,7 @@ protected function configure(): void 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('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('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'), @@ -85,7 +86,8 @@ protected function configure(): void the new ones into the translation files. When new translation strings are found it can automatically add a prefix to the translation -message. +message. However, if the --no-fill option is used, the --prefix +option has no effect, since the translation values are left empty. Example running against a Bundle (AcmeBundle) @@ -113,9 +115,6 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $io = new SymfonyStyle($input, $output); - $errorIo = $output instanceof ConsoleOutputInterface ? new SymfonyStyle($input, $output->getErrorOutput()) : $io; - $io = new SymfonyStyle($input, $output); $errorIo = $io->getErrorStyle(); @@ -181,7 +180,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->comment(\sprintf('Generating "%s" translation files for "%s"', $input->getArgument('locale'), $currentName)); $io->comment('Parsing templates...'); - $extractedCatalogue = $this->extractMessages($input->getArgument('locale'), $codePaths, $input->getOption('prefix')); + $prefix = $input->getOption('no-fill') ? self::NO_FILL_PREFIX : $input->getOption('prefix'); + $extractedCatalogue = $this->extractMessages($input->getArgument('locale'), $codePaths, $prefix); $io->comment('Loading translation files...'); $currentCatalogue = $this->loadCurrentMessages($input->getArgument('locale'), $transPaths); @@ -271,6 +271,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int $operationResult = $this->sortCatalogue($operationResult, $sort); } + if (true === $input->getOption('no-fill')) { + $this->removeNoFillTranslations($operationResult); + } + $this->writer->write($operationResult, $format, ['path' => $bundleTransPath, 'default_locale' => $this->defaultLocale, 'xliff_version' => $xliffVersion, 'as_tree' => $input->getOption('as-tree'), 'inline' => $input->getOption('as-tree') ?? 0]); if (true === $input->getOption('dump-messages')) { @@ -485,4 +489,13 @@ private function getRootCodePaths(KernelInterface $kernel): array return $codePaths; } + + private function removeNoFillTranslations(MessageCatalogueInterface $operation): void + { + foreach ($operation->all('messages') as $key => $message) { + if (str_starts_with($message, self::NO_FILL_PREFIX)) { + $operation->set($key, '', 'messages'); + } + } + } } diff --git a/Tests/Command/TranslationUpdateCommandTest.php b/Tests/Command/TranslationUpdateCommandTest.php index 2304e75ad..3a1bd8e37 100644 --- a/Tests/Command/TranslationUpdateCommandTest.php +++ b/Tests/Command/TranslationUpdateCommandTest.php @@ -21,6 +21,7 @@ use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Translation\Extractor\ExtractorInterface; use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; use Symfony\Component\Translation\Reader\TranslationReader; use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\Writer\TranslationWriter; @@ -176,6 +177,45 @@ public function testFilterDuplicateTransPaths() $this->assertEquals($expectedPaths, $filteredTransPaths); } + /** + * @dataProvider removeNoFillProvider + */ + public function testRemoveNoFillTranslationsMethod($noFillCounter, $messages) + { + // Preparing mock + $operation = $this->createMock(MessageCatalogueInterface::class); + $operation + ->method('all') + ->with('messages') + ->willReturn($messages); + $operation + ->expects($this->exactly($noFillCounter)) + ->method('set'); + + // Calling private method + $translationUpdate = $this->createMock(TranslationUpdateCommand::class); + $reflection = new \ReflectionObject($translationUpdate); + $method = $reflection->getMethod('removeNoFillTranslations'); + $method->invokeArgs($translationUpdate, [$operation]); + } + + public function removeNoFillProvider(): array + { + return [ + [0, []], + [0, ['foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz']], + [0, ['foo' => "\0foo"]], + [0, ['foo' => "foo\0NoFill\0"]], + [0, ['foo' => "f\0NoFill\000"]], + [0, ['foo' => 'foo', 'bar' => 'bar']], + [1, ['foo' => "\0NoFill\0foo"]], + [1, ['foo' => "\0NoFill\0foo", 'bar' => 'bar']], + [1, ['foo' => 'foo', 'bar' => "\0NoFill\0bar"]], + [2, ['foo' => "\0NoFill\0foo", 'bar' => "\0NoFill\0bar"]], + [3, ['foo' => "\0NoFill\0foo", 'bar' => "\0NoFill\0bar", 'baz' => "\0NoFill\0baz"]], + ]; + } + protected function setUp(): void { $this->fs = new Filesystem(); From 70a802fd68a97e4864c33262ce50c8ba1af5b227 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 23 Oct 2024 22:23:29 +0200 Subject: [PATCH 161/281] make data provider static --- Tests/Command/TranslationUpdateCommandTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Command/TranslationUpdateCommandTest.php b/Tests/Command/TranslationUpdateCommandTest.php index 3a1bd8e37..f803c2908 100644 --- a/Tests/Command/TranslationUpdateCommandTest.php +++ b/Tests/Command/TranslationUpdateCommandTest.php @@ -199,7 +199,7 @@ public function testRemoveNoFillTranslationsMethod($noFillCounter, $messages) $method->invokeArgs($translationUpdate, [$operation]); } - public function removeNoFillProvider(): array + public static function removeNoFillProvider(): array { return [ [0, []], From 7a46f7de39c92cdc29fd86beeb6f16ad1a3e5dd8 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Wed, 16 Oct 2024 21:38:41 +0200 Subject: [PATCH 162/281] [Routing] Rename annotations to attribute in `AttributeClassLoader` --- Routing/AttributeRouteControllerLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Routing/AttributeRouteControllerLoader.php b/Routing/AttributeRouteControllerLoader.php index 7e43e1af5..1d3d547c8 100644 --- a/Routing/AttributeRouteControllerLoader.php +++ b/Routing/AttributeRouteControllerLoader.php @@ -26,7 +26,7 @@ class AttributeRouteControllerLoader extends AttributeClassLoader /** * Configures the _controller default parameter of a given Route instance. */ - protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot): void + protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $attr): void { if ('__invoke' === $method->getName()) { $route->setDefault('_controller', $class->getName()); From 3bf00830d8737115dcc47143e31e36e49729dd52 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 12 Nov 2024 09:53:35 +0100 Subject: [PATCH 163/281] ensure that the validator.translation_domain parameter is always set --- DependencyInjection/FrameworkExtension.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index a7749cd30..b7d0bfe90 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -219,6 +219,10 @@ public function load(array $configs, ContainerBuilder $container): void throw new \LogicException('Requiring the "symfony/symfony" package is unsupported; replace it with standalone components instead.'); } + if (!ContainerBuilder::willBeAvailable('symfony/validator', Validation::class, ['symfony/framework-bundle', 'symfony/form'])) { + $container->setParameter('validator.translation_domain', 'validators'); + } + $loader->load('web.php'); $loader->load('services.php'); $loader->load('fragment_renderer.php'); @@ -479,8 +483,6 @@ public function load(array $configs, ContainerBuilder $container): void if (ContainerBuilder::willBeAvailable('symfony/validator', Validation::class, ['symfony/framework-bundle', 'symfony/form'])) { $this->writeConfigEnabled('validation', true, $config['validation']); } else { - $container->setParameter('validator.translation_domain', 'validators'); - $container->removeDefinition('form.type_extension.form.validator'); $container->removeDefinition('form.type_guesser.validator'); } From f95434900e37e035585c57dfadd2315c898027c4 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Thu, 14 Nov 2024 13:01:20 +0100 Subject: [PATCH 164/281] [TypeInfo][Serializer][PropertyInfo][Validator] TypeInfo 7.1 compatibility --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b4f474bb9..9b3e7c86e 100644 --- a/composer.json +++ b/composer.json @@ -64,7 +64,7 @@ "symfony/string": "^6.4|^7.0", "symfony/translation": "^6.4|^7.0", "symfony/twig-bundle": "^6.4|^7.0", - "symfony/type-info": "^7.2", + "symfony/type-info": "^7.1", "symfony/validator": "^6.4|^7.0", "symfony/workflow": "^6.4|^7.0", "symfony/yaml": "^6.4|^7.0", From 15df69b03ab9d2d06663f08c2dd6ecc0f4aad76e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 20 Nov 2024 10:52:39 +0100 Subject: [PATCH 165/281] [FrameworkBundle] Don't auto-register form/csrf when the corresponding components are not installed --- DependencyInjection/Configuration.php | 8 +++++-- DependencyInjection/FrameworkExtension.php | 21 ++++++++++++------- .../Fixtures/php/form_csrf_disabled.php | 1 + .../Fixtures/php/form_no_csrf.php | 1 + .../DependencyInjection/Fixtures/php/full.php | 1 + .../DependencyInjection/Fixtures/xml/full.xml | 2 +- .../Fixtures/yml/form_csrf_disabled.yml | 1 + .../Fixtures/yml/form_no_csrf.yml | 1 + .../DependencyInjection/Fixtures/yml/full.yml | 1 + 9 files changed, 26 insertions(+), 11 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 9abd10e73..9754cb078 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -211,7 +211,7 @@ private function addCsrfSection(ArrayNodeDefinition $rootNode): void ->addDefaultsIfNotSet() ->fixXmlConfig('stateless_token_id') ->children() - // defaults to framework.csrf_protection.stateless_token_ids || framework.session.enabled && !class_exists(FullStack::class) && interface_exists(CsrfTokenManagerInterface::class) + // defaults to (framework.csrf_protection.stateless_token_ids || framework.session.enabled) && !class_exists(FullStack::class) && interface_exists(CsrfTokenManagerInterface::class) ->scalarNode('enabled')->defaultNull()->end() ->arrayNode('stateless_token_ids') ->scalarPrototype()->end() @@ -237,8 +237,12 @@ private function addFormSection(ArrayNodeDefinition $rootNode, callable $enableI ->children() ->arrayNode('form') ->info('Form configuration') - ->{$enableIfStandalone('symfony/form', Form::class)}() + ->treatFalseLike(['enabled' => false]) + ->treatTrueLike(['enabled' => true]) + ->treatNullLike(['enabled' => true]) + ->addDefaultsIfNotSet() ->children() + ->scalarNode('enabled')->defaultNull()->end() // defaults to !class_exists(FullStack::class) && class_exists(Form::class) ->arrayNode('csrf_protection') ->treatFalseLike(['enabled' => false]) ->treatTrueLike(['enabled' => true]) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index b7d0bfe90..73101912a 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -278,6 +278,19 @@ public function load(array $configs, ContainerBuilder $container): void $this->readConfigEnabled('profiler', $container, $config['profiler']); $this->readConfigEnabled('workflows', $container, $config['workflows']); + // csrf depends on session or stateless token ids being registered + if (null === $config['csrf_protection']['enabled']) { + $this->writeConfigEnabled('csrf_protection', ($config['csrf_protection']['stateless_token_ids'] || $this->readConfigEnabled('session', $container, $config['session'])) && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']), $config['csrf_protection']); + } + + if (null === $config['form']['enabled']) { + $this->writeConfigEnabled('form', !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/form', Form::class, ['symfony/framework-bundle']), $config['form']); + } + + if (null === $config['form']['csrf_protection']['enabled']) { + $this->writeConfigEnabled('form.csrf_protection', $config['csrf_protection']['enabled'], $config['form']['csrf_protection']); + } + // A translator must always be registered (as support is included by // default in the Form and Validator component). If disabled, an identity // translator will be used and everything will still work as expected. @@ -466,10 +479,6 @@ public function load(array $configs, ContainerBuilder $container): void $container->removeDefinition('test.session.listener'); } - // csrf depends on session being registered - if (null === $config['csrf_protection']['enabled']) { - $this->writeConfigEnabled('csrf_protection', $config['csrf_protection']['stateless_token_ids'] || $this->readConfigEnabled('session', $container, $config['session']) && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']), $config['csrf_protection']); - } $this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader); // form depends on csrf being registered @@ -754,10 +763,6 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont { $loader->load('form.php'); - if (null === $config['form']['csrf_protection']['enabled']) { - $this->writeConfigEnabled('form.csrf_protection', $config['csrf_protection']['enabled'], $config['form']['csrf_protection']); - } - if ($this->readConfigEnabled('form.csrf_protection', $container, $config['form']['csrf_protection'])) { if (!$container->hasDefinition('security.csrf.token_generator')) { throw new \LogicException('To use form CSRF protection, "framework.csrf_protection" must be enabled.'); diff --git a/Tests/DependencyInjection/Fixtures/php/form_csrf_disabled.php b/Tests/DependencyInjection/Fixtures/php/form_csrf_disabled.php index 981498609..809b40be4 100644 --- a/Tests/DependencyInjection/Fixtures/php/form_csrf_disabled.php +++ b/Tests/DependencyInjection/Fixtures/php/form_csrf_disabled.php @@ -4,6 +4,7 @@ 'annotations' => false, 'csrf_protection' => false, 'form' => [ + 'enabled' => true, 'csrf_protection' => true, ], 'http_method_override' => false, diff --git a/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php b/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php index 7c052c9ff..5c63ed068 100644 --- a/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php +++ b/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php @@ -6,6 +6,7 @@ 'handle_all_throwables' => true, 'php_errors' => ['log' => true], 'form' => [ + 'enabled' => true, 'csrf_protection' => [ 'enabled' => false, ], diff --git a/Tests/DependencyInjection/Fixtures/php/full.php b/Tests/DependencyInjection/Fixtures/php/full.php index 0a32ce8b3..a728a4483 100644 --- a/Tests/DependencyInjection/Fixtures/php/full.php +++ b/Tests/DependencyInjection/Fixtures/php/full.php @@ -6,6 +6,7 @@ 'enabled_locales' => ['fr', 'en'], 'csrf_protection' => true, 'form' => [ + 'enabled' => true, 'csrf_protection' => [ 'field_name' => '_csrf', ], diff --git a/Tests/DependencyInjection/Fixtures/xml/full.xml b/Tests/DependencyInjection/Fixtures/xml/full.xml index c01e85783..0957d0cff 100644 --- a/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -10,7 +10,7 @@ fr en - + diff --git a/Tests/DependencyInjection/Fixtures/yml/form_csrf_disabled.yml b/Tests/DependencyInjection/Fixtures/yml/form_csrf_disabled.yml index 20350c9e8..36987869f 100644 --- a/Tests/DependencyInjection/Fixtures/yml/form_csrf_disabled.yml +++ b/Tests/DependencyInjection/Fixtures/yml/form_csrf_disabled.yml @@ -2,6 +2,7 @@ framework: annotations: false csrf_protection: false form: + enabled: true csrf_protection: true http_method_override: false handle_all_throwables: true diff --git a/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml b/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml index a86432f8d..74ee41091 100644 --- a/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml +++ b/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml @@ -5,5 +5,6 @@ framework: php_errors: log: true form: + enabled: true csrf_protection: enabled: false diff --git a/Tests/DependencyInjection/Fixtures/yml/full.yml b/Tests/DependencyInjection/Fixtures/yml/full.yml index 7550749eb..f70458a6c 100644 --- a/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -4,6 +4,7 @@ framework: enabled_locales: ['fr', 'en'] csrf_protection: true form: + enabled: true csrf_protection: field_name: _csrf http_method_override: false From eca6e49826b24c3f1f62d3ede8638aeb8fb41d81 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 20 Nov 2024 12:59:13 +0100 Subject: [PATCH 166/281] Revert "bug #58937 [FrameworkBundle] Don't auto-register form/csrf when the corresponding components are not installed (nicolas-grekas)" This reverts commit 552f7749d2b66485eb424af656827a0818c5bc4f, reversing changes made to e2f2a967158182109faa233b37f26687f6092a96. --- DependencyInjection/Configuration.php | 6 +----- DependencyInjection/FrameworkExtension.php | 21 +++++++------------ .../Fixtures/php/form_csrf_disabled.php | 1 - .../Fixtures/php/form_no_csrf.php | 1 - .../DependencyInjection/Fixtures/php/full.php | 1 - .../Fixtures/xml/form_csrf_disabled.xml | 2 +- .../Fixtures/xml/form_no_csrf.xml | 2 +- .../DependencyInjection/Fixtures/xml/full.xml | 2 +- .../Fixtures/yml/form_csrf_disabled.yml | 1 - .../Fixtures/yml/form_no_csrf.yml | 1 - .../DependencyInjection/Fixtures/yml/full.yml | 1 - 11 files changed, 12 insertions(+), 27 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 9754cb078..678698f4d 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -237,12 +237,8 @@ private function addFormSection(ArrayNodeDefinition $rootNode, callable $enableI ->children() ->arrayNode('form') ->info('Form configuration') - ->treatFalseLike(['enabled' => false]) - ->treatTrueLike(['enabled' => true]) - ->treatNullLike(['enabled' => true]) - ->addDefaultsIfNotSet() + ->{$enableIfStandalone('symfony/form', Form::class)}() ->children() - ->scalarNode('enabled')->defaultNull()->end() // defaults to !class_exists(FullStack::class) && class_exists(Form::class) ->arrayNode('csrf_protection') ->treatFalseLike(['enabled' => false]) ->treatTrueLike(['enabled' => true]) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 73101912a..3febd6337 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -278,19 +278,6 @@ public function load(array $configs, ContainerBuilder $container): void $this->readConfigEnabled('profiler', $container, $config['profiler']); $this->readConfigEnabled('workflows', $container, $config['workflows']); - // csrf depends on session or stateless token ids being registered - if (null === $config['csrf_protection']['enabled']) { - $this->writeConfigEnabled('csrf_protection', ($config['csrf_protection']['stateless_token_ids'] || $this->readConfigEnabled('session', $container, $config['session'])) && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']), $config['csrf_protection']); - } - - if (null === $config['form']['enabled']) { - $this->writeConfigEnabled('form', !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/form', Form::class, ['symfony/framework-bundle']), $config['form']); - } - - if (null === $config['form']['csrf_protection']['enabled']) { - $this->writeConfigEnabled('form.csrf_protection', $config['csrf_protection']['enabled'], $config['form']['csrf_protection']); - } - // A translator must always be registered (as support is included by // default in the Form and Validator component). If disabled, an identity // translator will be used and everything will still work as expected. @@ -479,6 +466,10 @@ public function load(array $configs, ContainerBuilder $container): void $container->removeDefinition('test.session.listener'); } + // csrf depends on session or stateless token ids being registered + if (null === $config['csrf_protection']['enabled']) { + $this->writeConfigEnabled('csrf_protection', ($config['csrf_protection']['stateless_token_ids'] || $this->readConfigEnabled('session', $container, $config['session'])) && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']), $config['csrf_protection']); + } $this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader); // form depends on csrf being registered @@ -763,6 +754,10 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont { $loader->load('form.php'); + if (null === $config['form']['csrf_protection']['enabled']) { + $this->writeConfigEnabled('form.csrf_protection', $config['csrf_protection']['enabled'], $config['form']['csrf_protection']); + } + if ($this->readConfigEnabled('form.csrf_protection', $container, $config['form']['csrf_protection'])) { if (!$container->hasDefinition('security.csrf.token_generator')) { throw new \LogicException('To use form CSRF protection, "framework.csrf_protection" must be enabled.'); diff --git a/Tests/DependencyInjection/Fixtures/php/form_csrf_disabled.php b/Tests/DependencyInjection/Fixtures/php/form_csrf_disabled.php index 809b40be4..981498609 100644 --- a/Tests/DependencyInjection/Fixtures/php/form_csrf_disabled.php +++ b/Tests/DependencyInjection/Fixtures/php/form_csrf_disabled.php @@ -4,7 +4,6 @@ 'annotations' => false, 'csrf_protection' => false, 'form' => [ - 'enabled' => true, 'csrf_protection' => true, ], 'http_method_override' => false, diff --git a/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php b/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php index 5c63ed068..7c052c9ff 100644 --- a/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php +++ b/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php @@ -6,7 +6,6 @@ 'handle_all_throwables' => true, 'php_errors' => ['log' => true], 'form' => [ - 'enabled' => true, 'csrf_protection' => [ 'enabled' => false, ], diff --git a/Tests/DependencyInjection/Fixtures/php/full.php b/Tests/DependencyInjection/Fixtures/php/full.php index a728a4483..0a32ce8b3 100644 --- a/Tests/DependencyInjection/Fixtures/php/full.php +++ b/Tests/DependencyInjection/Fixtures/php/full.php @@ -6,7 +6,6 @@ 'enabled_locales' => ['fr', 'en'], 'csrf_protection' => true, 'form' => [ - 'enabled' => true, 'csrf_protection' => [ 'field_name' => '_csrf', ], diff --git a/Tests/DependencyInjection/Fixtures/xml/form_csrf_disabled.xml b/Tests/DependencyInjection/Fixtures/xml/form_csrf_disabled.xml index ec97dcdd9..fdd02be87 100644 --- a/Tests/DependencyInjection/Fixtures/xml/form_csrf_disabled.xml +++ b/Tests/DependencyInjection/Fixtures/xml/form_csrf_disabled.xml @@ -12,7 +12,7 @@ - + diff --git a/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml b/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml index da8ed8b98..de1418108 100644 --- a/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml +++ b/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml @@ -9,7 +9,7 @@ - + diff --git a/Tests/DependencyInjection/Fixtures/xml/full.xml b/Tests/DependencyInjection/Fixtures/xml/full.xml index 0957d0cff..c01e85783 100644 --- a/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -10,7 +10,7 @@ fr en - + diff --git a/Tests/DependencyInjection/Fixtures/yml/form_csrf_disabled.yml b/Tests/DependencyInjection/Fixtures/yml/form_csrf_disabled.yml index 36987869f..20350c9e8 100644 --- a/Tests/DependencyInjection/Fixtures/yml/form_csrf_disabled.yml +++ b/Tests/DependencyInjection/Fixtures/yml/form_csrf_disabled.yml @@ -2,7 +2,6 @@ framework: annotations: false csrf_protection: false form: - enabled: true csrf_protection: true http_method_override: false handle_all_throwables: true diff --git a/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml b/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml index 74ee41091..a86432f8d 100644 --- a/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml +++ b/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml @@ -5,6 +5,5 @@ framework: php_errors: log: true form: - enabled: true csrf_protection: enabled: false diff --git a/Tests/DependencyInjection/Fixtures/yml/full.yml b/Tests/DependencyInjection/Fixtures/yml/full.yml index f70458a6c..7550749eb 100644 --- a/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -4,7 +4,6 @@ framework: enabled_locales: ['fr', 'en'] csrf_protection: true form: - enabled: true csrf_protection: field_name: _csrf http_method_override: false From 98aa6623769eab93feca7d976442980f158fd4a8 Mon Sep 17 00:00:00 2001 From: Jonas Elfering Date: Wed, 20 Nov 2024 16:19:47 +0100 Subject: [PATCH 167/281] Revert "[FrameworkBundle] Deprecate making `cache.app` adapter taggable" This reverts commit eed5b284618ec95eedbc376fb140b8fc24619372. --- DependencyInjection/FrameworkExtension.php | 5 ----- .../Fixtures/php/cache_cacheapp_tagaware.php | 16 ---------------- .../Fixtures/xml/cache_cacheapp_tagaware.xml | 15 --------------- .../Fixtures/yml/cache_cacheapp_tagaware.yml | 11 ----------- .../FrameworkExtensionTestCase.php | 13 ------------- 5 files changed, 60 deletions(-) delete mode 100644 Tests/DependencyInjection/Fixtures/php/cache_cacheapp_tagaware.php delete mode 100644 Tests/DependencyInjection/Fixtures/xml/cache_cacheapp_tagaware.xml delete mode 100644 Tests/DependencyInjection/Fixtures/yml/cache_cacheapp_tagaware.yml diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index a7749cd30..625158561 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2396,11 +2396,6 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con ]; } foreach ($config['pools'] as $name => $pool) { - if (\in_array('cache.app', $pool['adapters'] ?? [], true) && $pool['tags']) { - trigger_deprecation('symfony/framework-bundle', '7.2', 'Using the "tags" option with the "cache.app" adapter is deprecated. You can use the "cache.app.taggable" adapter instead (aliased to the TagAwareCacheInterface for autowiring).'); - // throw new LogicException('The "tags" option cannot be used with the "cache.app" adapter. You can use the "cache.app.taggable" adapter instead (aliased to the TagAwareCacheInterface for autowiring).'); - } - $pool['adapters'] = $pool['adapters'] ?: ['cache.app']; $isRedisTagAware = ['cache.adapter.redis_tag_aware'] === $pool['adapters']; diff --git a/Tests/DependencyInjection/Fixtures/php/cache_cacheapp_tagaware.php b/Tests/DependencyInjection/Fixtures/php/cache_cacheapp_tagaware.php deleted file mode 100644 index 77606f5b1..000000000 --- a/Tests/DependencyInjection/Fixtures/php/cache_cacheapp_tagaware.php +++ /dev/null @@ -1,16 +0,0 @@ -loadFromExtension('framework', [ - 'annotations' => false, - 'http_method_override' => false, - 'handle_all_throwables' => true, - 'php_errors' => ['log' => true], - 'cache' => [ - 'pools' => [ - 'app.tagaware' => [ - 'adapter' => 'cache.app', - 'tags' => true, - ], - ], - ], -]); diff --git a/Tests/DependencyInjection/Fixtures/xml/cache_cacheapp_tagaware.xml b/Tests/DependencyInjection/Fixtures/xml/cache_cacheapp_tagaware.xml deleted file mode 100644 index 7d59e19d5..000000000 --- a/Tests/DependencyInjection/Fixtures/xml/cache_cacheapp_tagaware.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/Tests/DependencyInjection/Fixtures/yml/cache_cacheapp_tagaware.yml b/Tests/DependencyInjection/Fixtures/yml/cache_cacheapp_tagaware.yml deleted file mode 100644 index 32ef3d49c..000000000 --- a/Tests/DependencyInjection/Fixtures/yml/cache_cacheapp_tagaware.yml +++ /dev/null @@ -1,11 +0,0 @@ -framework: - annotations: false - http_method_override: false - handle_all_throwables: true - php_errors: - log: true - cache: - pools: - app.tagaware: - adapter: cache.app - tags: true diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 016ae507b..798217191 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -13,7 +13,6 @@ use Psr\Cache\CacheItemPoolInterface; use Psr\Log\LoggerAwareInterface; -use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DummyMessage; @@ -96,8 +95,6 @@ abstract class FrameworkExtensionTestCase extends TestCase { - use ExpectUserDeprecationMessageTrait; - private static array $containerCache = []; abstract protected function loadFromFile(ContainerBuilder $container, $file); @@ -1856,16 +1853,6 @@ public function testCacheTaggableTagAppliedToPools() } } - /** - * @group legacy - */ - public function testTaggableCacheAppIsDeprecated() - { - $this->expectUserDeprecationMessage('Since symfony/framework-bundle 7.2: Using the "tags" option with the "cache.app" adapter is deprecated. You can use the "cache.app.taggable" adapter instead (aliased to the TagAwareCacheInterface for autowiring).'); - - $this->createContainerFromFile('cache_cacheapp_tagaware'); - } - /** * @dataProvider appRedisTagAwareConfigProvider */ From 4155d1d9df8f7757304b502a2f0adfbd8fc2ccf3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 4 Dec 2024 11:03:46 +0100 Subject: [PATCH 168/281] [FrameworkBundle] Make uri_signer lazy and improve error when kernel.secret is empty --- DependencyInjection/FrameworkExtension.php | 9 ++++++++- Resources/config/services.php | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 26cae1f30..8e19697f7 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -310,10 +310,17 @@ public function load(array $configs, ContainerBuilder $container): void } } + $emptySecretHint = '"framework.secret" option'; if (isset($config['secret'])) { $container->setParameter('kernel.secret', $config['secret']); + $usedEnvs = []; + $container->resolveEnvPlaceholders($config['secret'], null, $usedEnvs); + + if ($usedEnvs) { + $emptySecretHint = \sprintf('"%s" env var%s', implode('", "', $usedEnvs), 1 === \count($usedEnvs) ? '' : 's'); + } } - $container->parameterCannotBeEmpty('kernel.secret', 'A non-empty value for the parameter "kernel.secret" is required. Did you forget to configure the "framework.secret" option?'); + $container->parameterCannotBeEmpty('kernel.secret', 'A non-empty value for the parameter "kernel.secret" is required. Did you forget to configure the '.$emptySecretHint.'?'); $container->setParameter('kernel.http_method_override', $config['http_method_override']); $container->setParameter('kernel.trust_x_sendfile_type_header', $config['trust_x_sendfile_type_header']); diff --git a/Resources/config/services.php b/Resources/config/services.php index 53856f356..6abe1b6d8 100644 --- a/Resources/config/services.php +++ b/Resources/config/services.php @@ -157,6 +157,7 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : [] ->args([ new Parameter('kernel.secret'), ]) + ->lazy() ->alias(UriSigner::class, 'uri_signer') ->set('config_cache_factory', ResourceCheckerConfigCacheFactory::class) From 85e44d146cfa230353e8638c3fa8e9bfb9c64ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 27 Nov 2024 16:57:40 +0100 Subject: [PATCH 169/281] [AssetMapper] add support for assets pre-compression --- CHANGELOG.md | 5 ++++ DependencyInjection/Configuration.php | 24 +++++++++++++++++++ DependencyInjection/FrameworkExtension.php | 21 ++++++++++++++++ Resources/config/asset_mapper.php | 21 ++++++++++++++++ Resources/config/schema/symfony-1.0.xsd | 11 +++++++++ .../DependencyInjection/ConfigurationTest.php | 11 +++++++++ 6 files changed, 93 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3227eddc2..b0beb9a96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.3 +--- + + * Add support for assets pre-compression + 7.2 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 678698f4d..372da4a5e 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -17,6 +17,7 @@ use Symfony\Bundle\FullStack; use Symfony\Component\Asset\Package; use Symfony\Component\AssetMapper\AssetMapper; +use Symfony\Component\AssetMapper\Compressor\CompressorInterface; use Symfony\Component\Cache\Adapter\DoctrineAdapter; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\NodeBuilder; @@ -924,6 +925,29 @@ private function addAssetMapperSection(ArrayNodeDefinition $rootNode, callable $ ->info('The directory to store JavaScript vendors.') ->defaultValue('%kernel.project_dir%/assets/vendor') ->end() + ->arrayNode('precompress') + ->info('Precompress assets with Brotli, Zstandard and gzip.') + ->canBeEnabled() + ->fixXmlConfig('format') + ->fixXmlConfig('extension') + ->children() + ->arrayNode('formats') + ->info('Array of formats to enable. "brotli", "zstandard" and "gzip" are supported. Defaults to all formats supported by the system. The entire list must be provided.') + ->prototype('scalar')->end() + ->performNoDeepMerging() + ->validate() + ->ifTrue(static fn (array $v) => array_diff($v, ['brotli', 'zstandard', 'gzip'])) + ->thenInvalid('Unsupported format: "brotli", "zstandard" and "gzip" are supported.') + ->end() + ->end() + ->arrayNode('extensions') + ->info('Array of extensions to compress. The entire list must be provided, no merging occurs.') + ->prototype('scalar')->end() + ->performNoDeepMerging() + ->defaultValue(interface_exists(CompressorInterface::class) ? CompressorInterface::DEFAULT_EXTENSIONS : []) + ->end() + ->end() + ->end() ->end() ->end() ->end() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 26cae1f30..805d37367 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -32,6 +32,7 @@ use Symfony\Component\Asset\PackageInterface; use Symfony\Component\AssetMapper\AssetMapper; use Symfony\Component\AssetMapper\Compiler\AssetCompilerInterface; +use Symfony\Component\AssetMapper\Compressor\CompressorInterface; use Symfony\Component\BrowserKit\AbstractBrowser; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; @@ -1372,6 +1373,26 @@ private function registerAssetMapperConfiguration(array $config, ContainerBuilde ->replaceArgument(3, $config['importmap_polyfill']) ->replaceArgument(4, $config['importmap_script_attributes']) ; + + if (interface_exists(CompressorInterface::class)) { + $compressors = []; + foreach ($config['precompress']['formats'] as $format) { + $compressors[$format] = new Reference("asset_mapper.compressor.$format"); + } + + $container->getDefinition('asset_mapper.compressor')->replaceArgument(0, $compressors ?: null); + + if ($config['precompress']['enabled']) { + $container + ->getDefinition('asset_mapper.local_public_assets_filesystem') + ->addArgument(new Reference('asset_mapper.compressor')) + ->addArgument($config['precompress']['extensions']) + ; + } + } else { + $container->removeDefinition('asset_mapper.compressor'); + $container->removeDefinition('asset_mapper.assets.command.compress'); + } } /** diff --git a/Resources/config/asset_mapper.php b/Resources/config/asset_mapper.php index 404e7af18..c18755864 100644 --- a/Resources/config/asset_mapper.php +++ b/Resources/config/asset_mapper.php @@ -17,6 +17,7 @@ use Symfony\Component\AssetMapper\AssetMapperInterface; use Symfony\Component\AssetMapper\AssetMapperRepository; use Symfony\Component\AssetMapper\Command\AssetMapperCompileCommand; +use Symfony\Component\AssetMapper\Command\CompressAssetsCommand; use Symfony\Component\AssetMapper\Command\DebugAssetMapperCommand; use Symfony\Component\AssetMapper\Command\ImportMapAuditCommand; use Symfony\Component\AssetMapper\Command\ImportMapInstallCommand; @@ -28,6 +29,11 @@ use Symfony\Component\AssetMapper\Compiler\CssAssetUrlCompiler; use Symfony\Component\AssetMapper\Compiler\JavaScriptImportPathCompiler; use Symfony\Component\AssetMapper\Compiler\SourceMappingUrlsCompiler; +use Symfony\Component\AssetMapper\Compressor\BrotliCompressor; +use Symfony\Component\AssetMapper\Compressor\ChainCompressor; +use Symfony\Component\AssetMapper\Compressor\CompressorInterface; +use Symfony\Component\AssetMapper\Compressor\GzipCompressor; +use Symfony\Component\AssetMapper\Compressor\ZstandardCompressor; use Symfony\Component\AssetMapper\Factory\CachedMappedAssetFactory; use Symfony\Component\AssetMapper\Factory\MappedAssetFactory; use Symfony\Component\AssetMapper\ImportMap\ImportMapAuditor; @@ -254,5 +260,20 @@ ->set('asset_mapper.importmap.command.outdated', ImportMapOutdatedCommand::class) ->args([service('asset_mapper.importmap.update_checker')]) ->tag('console.command') + + ->set('asset_mapper.compressor.brotli', BrotliCompressor::class) + ->set('asset_mapper.compressor.zstandard', ZstandardCompressor::class) + ->set('asset_mapper.compressor.gzip', GzipCompressor::class) + + ->set('asset_mapper.compressor', ChainCompressor::class) + ->args([ + abstract_arg('compressor'), + service('logger'), + ]) + ->alias(CompressorInterface::class, 'asset_mapper.compressor') + + ->set('asset_mapper.assets.command.compress', CompressAssetsCommand::class) + ->args([service('asset_mapper.compressor')]) + ->tag('console.command') ; }; diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index ed7cc744f..91528b60f 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -206,6 +206,7 @@ + @@ -230,6 +231,16 @@ + + + + + + + + + + diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 53706d2e0..c7113cfb4 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration; use Symfony\Bundle\FullStack; +use Symfony\Component\AssetMapper\Compressor\CompressorInterface; use Symfony\Component\Cache\Adapter\DoctrineAdapter; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\Definition\Processor; @@ -141,6 +142,11 @@ public function testAssetMapperCanBeEnabled() 'vendor_dir' => '%kernel.project_dir%/assets/vendor', 'importmap_script_attributes' => [], 'exclude_dotfiles' => true, + 'precompress' => [ + 'enabled' => false, + 'formats' => [], + 'extensions' => interface_exists(CompressorInterface::class) ? CompressorInterface::DEFAULT_EXTENSIONS : [], + ], ]; $this->assertEquals($defaultConfig, $config['asset_mapper']); @@ -847,6 +853,11 @@ protected static function getBundleDefaultConfig() 'vendor_dir' => '%kernel.project_dir%/assets/vendor', 'importmap_script_attributes' => [], 'exclude_dotfiles' => true, + 'precompress' => [ + 'enabled' => false, + 'formats' => [], + 'extensions' => interface_exists(CompressorInterface::class) ? CompressorInterface::DEFAULT_EXTENSIONS : [], + ], ], 'cache' => [ 'pools' => [], From e56da792d24e84157cf8311c8594b967b4b46516 Mon Sep 17 00:00:00 2001 From: Mathieu Santostefano Date: Fri, 6 Dec 2024 09:30:19 +0100 Subject: [PATCH 170/281] Rename TranslationUpdateCommand to TranslationExtract command to match the command name --- CHANGELOG.md | 1 + Command/TranslationExtractCommand.php | 499 ++++++++++++++++++ Command/TranslationUpdateCommand.php | 473 +---------------- Resources/config/console.php | 4 +- ...anslationExtractCommandCompletionTest.php} | 6 +- ....php => TranslationExtractCommandTest.php} | 12 +- 6 files changed, 514 insertions(+), 481 deletions(-) create mode 100644 Command/TranslationExtractCommand.php rename Tests/Command/{TranslationUpdateCommandCompletionTest.php => TranslationExtractCommandCompletionTest.php} (93%) rename Tests/Command/{TranslationUpdateCommandTest.php => TranslationExtractCommandTest.php} (96%) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0beb9a96..3f05ad7a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add support for assets pre-compression + * Rename `TranslationUpdateCommand` to `TranslationExtractCommand` 7.2 --- diff --git a/Command/TranslationExtractCommand.php b/Command/TranslationExtractCommand.php new file mode 100644 index 000000000..52f8d0c73 --- /dev/null +++ b/Command/TranslationExtractCommand.php @@ -0,0 +1,499 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Translation\Catalogue\MergeOperation; +use Symfony\Component\Translation\Catalogue\TargetOperation; +use Symfony\Component\Translation\Extractor\ExtractorInterface; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; +use Symfony\Component\Translation\Reader\TranslationReaderInterface; +use Symfony\Component\Translation\Writer\TranslationWriterInterface; + +/** + * A command that parses templates to extract translation messages and adds them + * into the translation files. + * + * @author Michel Salib + */ +#[AsCommand(name: 'translation:extract', description: 'Extract missing translations keys from code to translation files')] +class TranslationExtractCommand extends Command +{ + private const ASC = 'asc'; + private const DESC = 'desc'; + private const SORT_ORDERS = [self::ASC, self::DESC]; + private const FORMATS = [ + 'xlf12' => ['xlf', '1.2'], + 'xlf20' => ['xlf', '2.0'], + ]; + private const NO_FILL_PREFIX = "\0NoFill\0"; + + public function __construct( + private TranslationWriterInterface $writer, + private TranslationReaderInterface $reader, + private ExtractorInterface $extractor, + private string $defaultLocale, + private ?string $defaultTransPath = null, + private ?string $defaultViewsPath = null, + private array $transPaths = [], + private array $codePaths = [], + private array $enabledLocales = [], + ) { + parent::__construct(); + } + + protected function configure(): void + { + $this + ->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('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('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'), + ]) + ->setHelp(<<<'EOF' +The %command.name% command extracts translation strings from templates +of a given bundle or the default translations directory. It can display them or merge +the new ones into the translation files. + +When new translation strings are found it can automatically add a prefix to the translation +message. However, if the --no-fill option is used, the --prefix +option has no effect, since the translation values are left empty. + +Example running against a Bundle (AcmeBundle) + + php %command.full_name% --dump-messages en AcmeBundle + php %command.full_name% --force --prefix="new_" fr AcmeBundle + +Example running against default messages directory + + php %command.full_name% --dump-messages en + php %command.full_name% --force --prefix="new_" fr + +You can sort the output with the --sort flag: + + php %command.full_name% --dump-messages --sort=asc en AcmeBundle + php %command.full_name% --force --sort=desc fr + +You can dump a tree-like structure using the yaml format with --as-tree flag: + + php %command.full_name% --force --format=yaml --as-tree=3 en AcmeBundle + +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); + + // check presence of force or dump-message + if (true !== $input->getOption('force') && true !== $input->getOption('dump-messages')) { + $errorIo->error('You must choose one of --force or --dump-messages'); + + return 1; + } + + $format = $input->getOption('format'); + $xliffVersion = '1.2'; + + if (\array_key_exists($format, self::FORMATS)) { + [$format, $xliffVersion] = self::FORMATS[$format]; + } + + // check format + $supportedFormats = $this->writer->getFormats(); + if (!\in_array($format, $supportedFormats, true)) { + $errorIo->error(['Wrong output format', 'Supported formats are: '.implode(', ', $supportedFormats).', xlf12 and xlf20.']); + + return 1; + } + + /** @var KernelInterface $kernel */ + $kernel = $this->getApplication()->getKernel(); + + // Define Root Paths + $transPaths = $this->getRootTransPaths(); + $codePaths = $this->getRootCodePaths($kernel); + + $currentName = 'default directory'; + + // Override with provided Bundle info + if (null !== $input->getArgument('bundle')) { + try { + $foundBundle = $kernel->getBundle($input->getArgument('bundle')); + $bundleDir = $foundBundle->getPath(); + $transPaths = [is_dir($bundleDir.'/Resources/translations') ? $bundleDir.'/Resources/translations' : $bundleDir.'/translations']; + $codePaths = [is_dir($bundleDir.'/Resources/views') ? $bundleDir.'/Resources/views' : $bundleDir.'/templates']; + if ($this->defaultTransPath) { + $transPaths[] = $this->defaultTransPath; + } + if ($this->defaultViewsPath) { + $codePaths[] = $this->defaultViewsPath; + } + $currentName = $foundBundle->getName(); + } catch (\InvalidArgumentException) { + // such a bundle does not exist, so treat the argument as path + $path = $input->getArgument('bundle'); + + $transPaths = [$path.'/translations']; + $codePaths = [$path.'/templates']; + + if (!is_dir($transPaths[0])) { + throw new InvalidArgumentException(\sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0])); + } + } + } + + $io->title('Translation Messages Extractor and Dumper'); + $io->comment(\sprintf('Generating "%s" translation files for "%s"', $input->getArgument('locale'), $currentName)); + + $io->comment('Parsing templates...'); + $prefix = $input->getOption('no-fill') ? self::NO_FILL_PREFIX : $input->getOption('prefix'); + $extractedCatalogue = $this->extractMessages($input->getArgument('locale'), $codePaths, $prefix); + + $io->comment('Loading translation files...'); + $currentCatalogue = $this->loadCurrentMessages($input->getArgument('locale'), $transPaths); + + if (null !== $domain = $input->getOption('domain')) { + $currentCatalogue = $this->filterCatalogue($currentCatalogue, $domain); + $extractedCatalogue = $this->filterCatalogue($extractedCatalogue, $domain); + } + + // process catalogues + $operation = $input->getOption('clean') + ? new TargetOperation($currentCatalogue, $extractedCatalogue) + : new MergeOperation($currentCatalogue, $extractedCatalogue); + + // Exit if no messages found. + if (!\count($operation->getDomains())) { + $errorIo->warning('No translation messages were found.'); + + return 0; + } + + $resultMessage = 'Translation files were successfully updated'; + + $operation->moveMessagesToIntlDomainsIfPossible('new'); + + if ($sort = $input->getOption('sort')) { + $sort = strtolower($sort); + if (!\in_array($sort, self::SORT_ORDERS, true)) { + $errorIo->error(['Wrong sort order', 'Supported formats are: '.implode(', ', self::SORT_ORDERS).'.']); + + return 1; + } + } + + // show compiled list of messages + if (true === $input->getOption('dump-messages')) { + $extractedMessagesCount = 0; + $io->newLine(); + foreach ($operation->getDomains() as $domain) { + $newKeys = array_keys($operation->getNewMessages($domain)); + $allKeys = array_keys($operation->getMessages($domain)); + + $list = array_merge( + array_diff($allKeys, $newKeys), + array_map(fn ($id) => \sprintf('%s', $id), $newKeys), + array_map(fn ($id) => \sprintf('%s', $id), array_keys($operation->getObsoleteMessages($domain))) + ); + + $domainMessagesCount = \count($list); + + if (self::DESC === $sort) { + rsort($list); + } else { + sort($list); + } + + $io->section(\sprintf('Messages extracted for domain "%s" (%d message%s)', $domain, $domainMessagesCount, $domainMessagesCount > 1 ? 's' : '')); + $io->listing($list); + + $extractedMessagesCount += $domainMessagesCount; + } + + if ('xlf' === $format) { + $io->comment(\sprintf('Xliff output version is %s', $xliffVersion)); + } + + $resultMessage = \sprintf('%d message%s successfully extracted', $extractedMessagesCount, $extractedMessagesCount > 1 ? 's were' : ' was'); + } + + // save the files + if (true === $input->getOption('force')) { + $io->comment('Writing files...'); + + $bundleTransPath = false; + foreach ($transPaths as $path) { + if (is_dir($path)) { + $bundleTransPath = $path; + } + } + + if (!$bundleTransPath) { + $bundleTransPath = end($transPaths); + } + + $operationResult = $operation->getResult(); + if ($sort) { + $operationResult = $this->sortCatalogue($operationResult, $sort); + } + + if (true === $input->getOption('no-fill')) { + $this->removeNoFillTranslations($operationResult); + } + + $this->writer->write($operationResult, $format, ['path' => $bundleTransPath, 'default_locale' => $this->defaultLocale, 'xliff_version' => $xliffVersion, 'as_tree' => $input->getOption('as-tree'), 'inline' => $input->getOption('as-tree') ?? 0]); + + if (true === $input->getOption('dump-messages')) { + $resultMessage .= ' and translation files were updated'; + } + } + + $io->success($resultMessage.'.'); + + return 0; + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestArgumentValuesFor('locale')) { + $suggestions->suggestValues($this->enabledLocales); + + return; + } + + /** @var KernelInterface $kernel */ + $kernel = $this->getApplication()->getKernel(); + if ($input->mustSuggestArgumentValuesFor('bundle')) { + $bundles = []; + + foreach ($kernel->getBundles() as $bundle) { + $bundles[] = $bundle->getName(); + if ($bundle->getContainerExtension()) { + $bundles[] = $bundle->getContainerExtension()->getAlias(); + } + } + + $suggestions->suggestValues($bundles); + + return; + } + + if ($input->mustSuggestOptionValuesFor('format')) { + $suggestions->suggestValues(array_merge( + $this->writer->getFormats(), + array_keys(self::FORMATS) + )); + + return; + } + + if ($input->mustSuggestOptionValuesFor('domain') && $locale = $input->getArgument('locale')) { + $extractedCatalogue = $this->extractMessages($locale, $this->getRootCodePaths($kernel), $input->getOption('prefix')); + + $currentCatalogue = $this->loadCurrentMessages($locale, $this->getRootTransPaths()); + + // process catalogues + $operation = $input->getOption('clean') + ? new TargetOperation($currentCatalogue, $extractedCatalogue) + : new MergeOperation($currentCatalogue, $extractedCatalogue); + + $suggestions->suggestValues($operation->getDomains()); + + return; + } + + if ($input->mustSuggestOptionValuesFor('sort')) { + $suggestions->suggestValues(self::SORT_ORDERS); + } + } + + private function filterCatalogue(MessageCatalogue $catalogue, string $domain): MessageCatalogue + { + $filteredCatalogue = new MessageCatalogue($catalogue->getLocale()); + + // extract intl-icu messages only + $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; + if ($intlMessages = $catalogue->all($intlDomain)) { + $filteredCatalogue->add($intlMessages, $intlDomain); + } + + // extract all messages and subtract intl-icu messages + if ($messages = array_diff($catalogue->all($domain), $intlMessages)) { + $filteredCatalogue->add($messages, $domain); + } + foreach ($catalogue->getResources() as $resource) { + $filteredCatalogue->addResource($resource); + } + + if ($metadata = $catalogue->getMetadata('', $intlDomain)) { + foreach ($metadata as $k => $v) { + $filteredCatalogue->setMetadata($k, $v, $intlDomain); + } + } + + if ($metadata = $catalogue->getMetadata('', $domain)) { + foreach ($metadata as $k => $v) { + $filteredCatalogue->setMetadata($k, $v, $domain); + } + } + + return $filteredCatalogue; + } + + private function sortCatalogue(MessageCatalogue $catalogue, string $sort): MessageCatalogue + { + $sortedCatalogue = new MessageCatalogue($catalogue->getLocale()); + + foreach ($catalogue->getDomains() as $domain) { + // extract intl-icu messages only + $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; + if ($intlMessages = $catalogue->all($intlDomain)) { + if (self::DESC === $sort) { + krsort($intlMessages); + } elseif (self::ASC === $sort) { + ksort($intlMessages); + } + + $sortedCatalogue->add($intlMessages, $intlDomain); + } + + // extract all messages and subtract intl-icu messages + if ($messages = array_diff($catalogue->all($domain), $intlMessages)) { + if (self::DESC === $sort) { + krsort($messages); + } elseif (self::ASC === $sort) { + ksort($messages); + } + + $sortedCatalogue->add($messages, $domain); + } + + if ($metadata = $catalogue->getMetadata('', $intlDomain)) { + foreach ($metadata as $k => $v) { + $sortedCatalogue->setMetadata($k, $v, $intlDomain); + } + } + + if ($metadata = $catalogue->getMetadata('', $domain)) { + foreach ($metadata as $k => $v) { + $sortedCatalogue->setMetadata($k, $v, $domain); + } + } + } + + foreach ($catalogue->getResources() as $resource) { + $sortedCatalogue->addResource($resource); + } + + return $sortedCatalogue; + } + + private function extractMessages(string $locale, array $transPaths, string $prefix): MessageCatalogue + { + $extractedCatalogue = new MessageCatalogue($locale); + $this->extractor->setPrefix($prefix); + $transPaths = $this->filterDuplicateTransPaths($transPaths); + foreach ($transPaths as $path) { + if (is_dir($path) || is_file($path)) { + $this->extractor->extract($path, $extractedCatalogue); + } + } + + return $extractedCatalogue; + } + + private function filterDuplicateTransPaths(array $transPaths): array + { + $transPaths = array_filter(array_map('realpath', $transPaths)); + + sort($transPaths); + + $filteredPaths = []; + + foreach ($transPaths as $path) { + foreach ($filteredPaths as $filteredPath) { + if (str_starts_with($path, $filteredPath.\DIRECTORY_SEPARATOR)) { + continue 2; + } + } + + $filteredPaths[] = $path; + } + + return $filteredPaths; + } + + private function loadCurrentMessages(string $locale, array $transPaths): MessageCatalogue + { + $currentCatalogue = new MessageCatalogue($locale); + foreach ($transPaths as $path) { + if (is_dir($path)) { + $this->reader->read($path, $currentCatalogue); + } + } + + return $currentCatalogue; + } + + private function getRootTransPaths(): array + { + $transPaths = $this->transPaths; + if ($this->defaultTransPath) { + $transPaths[] = $this->defaultTransPath; + } + + return $transPaths; + } + + private function getRootCodePaths(KernelInterface $kernel): array + { + $codePaths = $this->codePaths; + $codePaths[] = $kernel->getProjectDir().'/src'; + if ($this->defaultViewsPath) { + $codePaths[] = $this->defaultViewsPath; + } + + return $codePaths; + } + + private function removeNoFillTranslations(MessageCatalogueInterface $operation): void + { + foreach ($operation->all('messages') as $key => $message) { + if (str_starts_with($message, self::NO_FILL_PREFIX)) { + $operation->set($key, '', 'messages'); + } + } + } +} diff --git a/Command/TranslationUpdateCommand.php b/Command/TranslationUpdateCommand.php index b26d3f9ad..de5aa9389 100644 --- a/Command/TranslationUpdateCommand.php +++ b/Command/TranslationUpdateCommand.php @@ -11,45 +11,12 @@ namespace Symfony\Bundle\FrameworkBundle\Command; -use Symfony\Component\Console\Attribute\AsCommand; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Completion\CompletionInput; -use Symfony\Component\Console\Completion\CompletionSuggestions; -use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\HttpKernel\KernelInterface; -use Symfony\Component\Translation\Catalogue\MergeOperation; -use Symfony\Component\Translation\Catalogue\TargetOperation; use Symfony\Component\Translation\Extractor\ExtractorInterface; -use Symfony\Component\Translation\MessageCatalogue; -use Symfony\Component\Translation\MessageCatalogueInterface; use Symfony\Component\Translation\Reader\TranslationReaderInterface; use Symfony\Component\Translation\Writer\TranslationWriterInterface; -/** - * A command that parses templates to extract translation messages and adds them - * into the translation files. - * - * @author Michel Salib - * - * @final - */ -#[AsCommand(name: 'translation:extract', description: 'Extract missing translations keys from code to translation files')] -class TranslationUpdateCommand extends Command +class TranslationUpdateCommand extends TranslationExtractCommand { - private const ASC = 'asc'; - private const DESC = 'desc'; - private const SORT_ORDERS = [self::ASC, self::DESC]; - private const FORMATS = [ - 'xlf12' => ['xlf', '1.2'], - 'xlf20' => ['xlf', '2.0'], - ]; - private const NO_FILL_PREFIX = "\0NoFill\0"; - public function __construct( private TranslationWriterInterface $writer, private TranslationReaderInterface $reader, @@ -61,441 +28,7 @@ public function __construct( private array $codePaths = [], private array $enabledLocales = [], ) { - parent::__construct(); - } - - protected function configure(): void - { - $this - ->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('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('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'), - ]) - ->setHelp(<<<'EOF' -The %command.name% command extracts translation strings from templates -of a given bundle or the default translations directory. It can display them or merge -the new ones into the translation files. - -When new translation strings are found it can automatically add a prefix to the translation -message. However, if the --no-fill option is used, the --prefix -option has no effect, since the translation values are left empty. - -Example running against a Bundle (AcmeBundle) - - php %command.full_name% --dump-messages en AcmeBundle - php %command.full_name% --force --prefix="new_" fr AcmeBundle - -Example running against default messages directory - - php %command.full_name% --dump-messages en - php %command.full_name% --force --prefix="new_" fr - -You can sort the output with the --sort flag: - - php %command.full_name% --dump-messages --sort=asc en AcmeBundle - php %command.full_name% --force --sort=desc fr - -You can dump a tree-like structure using the yaml format with --as-tree flag: - - php %command.full_name% --force --format=yaml --as-tree=3 en AcmeBundle - -EOF - ) - ; - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $io = new SymfonyStyle($input, $output); - $errorIo = $io->getErrorStyle(); - - // check presence of force or dump-message - if (true !== $input->getOption('force') && true !== $input->getOption('dump-messages')) { - $errorIo->error('You must choose one of --force or --dump-messages'); - - return 1; - } - - $format = $input->getOption('format'); - $xliffVersion = '1.2'; - - if (\array_key_exists($format, self::FORMATS)) { - [$format, $xliffVersion] = self::FORMATS[$format]; - } - - // check format - $supportedFormats = $this->writer->getFormats(); - if (!\in_array($format, $supportedFormats, true)) { - $errorIo->error(['Wrong output format', 'Supported formats are: '.implode(', ', $supportedFormats).', xlf12 and xlf20.']); - - return 1; - } - - /** @var KernelInterface $kernel */ - $kernel = $this->getApplication()->getKernel(); - - // Define Root Paths - $transPaths = $this->getRootTransPaths(); - $codePaths = $this->getRootCodePaths($kernel); - - $currentName = 'default directory'; - - // Override with provided Bundle info - if (null !== $input->getArgument('bundle')) { - try { - $foundBundle = $kernel->getBundle($input->getArgument('bundle')); - $bundleDir = $foundBundle->getPath(); - $transPaths = [is_dir($bundleDir.'/Resources/translations') ? $bundleDir.'/Resources/translations' : $bundleDir.'/translations']; - $codePaths = [is_dir($bundleDir.'/Resources/views') ? $bundleDir.'/Resources/views' : $bundleDir.'/templates']; - if ($this->defaultTransPath) { - $transPaths[] = $this->defaultTransPath; - } - if ($this->defaultViewsPath) { - $codePaths[] = $this->defaultViewsPath; - } - $currentName = $foundBundle->getName(); - } catch (\InvalidArgumentException) { - // such a bundle does not exist, so treat the argument as path - $path = $input->getArgument('bundle'); - - $transPaths = [$path.'/translations']; - $codePaths = [$path.'/templates']; - - if (!is_dir($transPaths[0])) { - throw new InvalidArgumentException(\sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0])); - } - } - } - - $io->title('Translation Messages Extractor and Dumper'); - $io->comment(\sprintf('Generating "%s" translation files for "%s"', $input->getArgument('locale'), $currentName)); - - $io->comment('Parsing templates...'); - $prefix = $input->getOption('no-fill') ? self::NO_FILL_PREFIX : $input->getOption('prefix'); - $extractedCatalogue = $this->extractMessages($input->getArgument('locale'), $codePaths, $prefix); - - $io->comment('Loading translation files...'); - $currentCatalogue = $this->loadCurrentMessages($input->getArgument('locale'), $transPaths); - - if (null !== $domain = $input->getOption('domain')) { - $currentCatalogue = $this->filterCatalogue($currentCatalogue, $domain); - $extractedCatalogue = $this->filterCatalogue($extractedCatalogue, $domain); - } - - // process catalogues - $operation = $input->getOption('clean') - ? new TargetOperation($currentCatalogue, $extractedCatalogue) - : new MergeOperation($currentCatalogue, $extractedCatalogue); - - // Exit if no messages found. - if (!\count($operation->getDomains())) { - $errorIo->warning('No translation messages were found.'); - - return 0; - } - - $resultMessage = 'Translation files were successfully updated'; - - $operation->moveMessagesToIntlDomainsIfPossible('new'); - - if ($sort = $input->getOption('sort')) { - $sort = strtolower($sort); - if (!\in_array($sort, self::SORT_ORDERS, true)) { - $errorIo->error(['Wrong sort order', 'Supported formats are: '.implode(', ', self::SORT_ORDERS).'.']); - - return 1; - } - } - - // show compiled list of messages - if (true === $input->getOption('dump-messages')) { - $extractedMessagesCount = 0; - $io->newLine(); - foreach ($operation->getDomains() as $domain) { - $newKeys = array_keys($operation->getNewMessages($domain)); - $allKeys = array_keys($operation->getMessages($domain)); - - $list = array_merge( - array_diff($allKeys, $newKeys), - array_map(fn ($id) => \sprintf('%s', $id), $newKeys), - array_map(fn ($id) => \sprintf('%s', $id), array_keys($operation->getObsoleteMessages($domain))) - ); - - $domainMessagesCount = \count($list); - - if (self::DESC === $sort) { - rsort($list); - } else { - sort($list); - } - - $io->section(\sprintf('Messages extracted for domain "%s" (%d message%s)', $domain, $domainMessagesCount, $domainMessagesCount > 1 ? 's' : '')); - $io->listing($list); - - $extractedMessagesCount += $domainMessagesCount; - } - - if ('xlf' === $format) { - $io->comment(\sprintf('Xliff output version is %s', $xliffVersion)); - } - - $resultMessage = \sprintf('%d message%s successfully extracted', $extractedMessagesCount, $extractedMessagesCount > 1 ? 's were' : ' was'); - } - - // save the files - if (true === $input->getOption('force')) { - $io->comment('Writing files...'); - - $bundleTransPath = false; - foreach ($transPaths as $path) { - if (is_dir($path)) { - $bundleTransPath = $path; - } - } - - if (!$bundleTransPath) { - $bundleTransPath = end($transPaths); - } - - $operationResult = $operation->getResult(); - if ($sort) { - $operationResult = $this->sortCatalogue($operationResult, $sort); - } - - if (true === $input->getOption('no-fill')) { - $this->removeNoFillTranslations($operationResult); - } - - $this->writer->write($operationResult, $format, ['path' => $bundleTransPath, 'default_locale' => $this->defaultLocale, 'xliff_version' => $xliffVersion, 'as_tree' => $input->getOption('as-tree'), 'inline' => $input->getOption('as-tree') ?? 0]); - - if (true === $input->getOption('dump-messages')) { - $resultMessage .= ' and translation files were updated'; - } - } - - $io->success($resultMessage.'.'); - - return 0; - } - - public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void - { - if ($input->mustSuggestArgumentValuesFor('locale')) { - $suggestions->suggestValues($this->enabledLocales); - - return; - } - - /** @var KernelInterface $kernel */ - $kernel = $this->getApplication()->getKernel(); - if ($input->mustSuggestArgumentValuesFor('bundle')) { - $bundles = []; - - foreach ($kernel->getBundles() as $bundle) { - $bundles[] = $bundle->getName(); - if ($bundle->getContainerExtension()) { - $bundles[] = $bundle->getContainerExtension()->getAlias(); - } - } - - $suggestions->suggestValues($bundles); - - return; - } - - if ($input->mustSuggestOptionValuesFor('format')) { - $suggestions->suggestValues(array_merge( - $this->writer->getFormats(), - array_keys(self::FORMATS) - )); - - return; - } - - if ($input->mustSuggestOptionValuesFor('domain') && $locale = $input->getArgument('locale')) { - $extractedCatalogue = $this->extractMessages($locale, $this->getRootCodePaths($kernel), $input->getOption('prefix')); - - $currentCatalogue = $this->loadCurrentMessages($locale, $this->getRootTransPaths()); - - // process catalogues - $operation = $input->getOption('clean') - ? new TargetOperation($currentCatalogue, $extractedCatalogue) - : new MergeOperation($currentCatalogue, $extractedCatalogue); - - $suggestions->suggestValues($operation->getDomains()); - - return; - } - - if ($input->mustSuggestOptionValuesFor('sort')) { - $suggestions->suggestValues(self::SORT_ORDERS); - } - } - - private function filterCatalogue(MessageCatalogue $catalogue, string $domain): MessageCatalogue - { - $filteredCatalogue = new MessageCatalogue($catalogue->getLocale()); - - // extract intl-icu messages only - $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; - if ($intlMessages = $catalogue->all($intlDomain)) { - $filteredCatalogue->add($intlMessages, $intlDomain); - } - - // extract all messages and subtract intl-icu messages - if ($messages = array_diff($catalogue->all($domain), $intlMessages)) { - $filteredCatalogue->add($messages, $domain); - } - foreach ($catalogue->getResources() as $resource) { - $filteredCatalogue->addResource($resource); - } - - if ($metadata = $catalogue->getMetadata('', $intlDomain)) { - foreach ($metadata as $k => $v) { - $filteredCatalogue->setMetadata($k, $v, $intlDomain); - } - } - - if ($metadata = $catalogue->getMetadata('', $domain)) { - foreach ($metadata as $k => $v) { - $filteredCatalogue->setMetadata($k, $v, $domain); - } - } - - return $filteredCatalogue; - } - - private function sortCatalogue(MessageCatalogue $catalogue, string $sort): MessageCatalogue - { - $sortedCatalogue = new MessageCatalogue($catalogue->getLocale()); - - foreach ($catalogue->getDomains() as $domain) { - // extract intl-icu messages only - $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; - if ($intlMessages = $catalogue->all($intlDomain)) { - if (self::DESC === $sort) { - krsort($intlMessages); - } elseif (self::ASC === $sort) { - ksort($intlMessages); - } - - $sortedCatalogue->add($intlMessages, $intlDomain); - } - - // extract all messages and subtract intl-icu messages - if ($messages = array_diff($catalogue->all($domain), $intlMessages)) { - if (self::DESC === $sort) { - krsort($messages); - } elseif (self::ASC === $sort) { - ksort($messages); - } - - $sortedCatalogue->add($messages, $domain); - } - - if ($metadata = $catalogue->getMetadata('', $intlDomain)) { - foreach ($metadata as $k => $v) { - $sortedCatalogue->setMetadata($k, $v, $intlDomain); - } - } - - if ($metadata = $catalogue->getMetadata('', $domain)) { - foreach ($metadata as $k => $v) { - $sortedCatalogue->setMetadata($k, $v, $domain); - } - } - } - - foreach ($catalogue->getResources() as $resource) { - $sortedCatalogue->addResource($resource); - } - - return $sortedCatalogue; - } - - private function extractMessages(string $locale, array $transPaths, string $prefix): MessageCatalogue - { - $extractedCatalogue = new MessageCatalogue($locale); - $this->extractor->setPrefix($prefix); - $transPaths = $this->filterDuplicateTransPaths($transPaths); - foreach ($transPaths as $path) { - if (is_dir($path) || is_file($path)) { - $this->extractor->extract($path, $extractedCatalogue); - } - } - - return $extractedCatalogue; - } - - private function filterDuplicateTransPaths(array $transPaths): array - { - $transPaths = array_filter(array_map('realpath', $transPaths)); - - sort($transPaths); - - $filteredPaths = []; - - foreach ($transPaths as $path) { - foreach ($filteredPaths as $filteredPath) { - if (str_starts_with($path, $filteredPath.\DIRECTORY_SEPARATOR)) { - continue 2; - } - } - - $filteredPaths[] = $path; - } - - return $filteredPaths; - } - - private function loadCurrentMessages(string $locale, array $transPaths): MessageCatalogue - { - $currentCatalogue = new MessageCatalogue($locale); - foreach ($transPaths as $path) { - if (is_dir($path)) { - $this->reader->read($path, $currentCatalogue); - } - } - - return $currentCatalogue; - } - - private function getRootTransPaths(): array - { - $transPaths = $this->transPaths; - if ($this->defaultTransPath) { - $transPaths[] = $this->defaultTransPath; - } - - return $transPaths; - } - - private function getRootCodePaths(KernelInterface $kernel): array - { - $codePaths = $this->codePaths; - $codePaths[] = $kernel->getProjectDir().'/src'; - if ($this->defaultViewsPath) { - $codePaths[] = $this->defaultViewsPath; - } - - return $codePaths; - } - - private function removeNoFillTranslations(MessageCatalogueInterface $operation): void - { - foreach ($operation->all('messages') as $key => $message) { - if (str_starts_with($message, self::NO_FILL_PREFIX)) { - $operation->set($key, '', 'messages'); - } - } + trigger_deprecation('symfony/framework-bundle', '7.3', 'The "%s" class is deprecated, use "%s" instead.', __CLASS__, TranslationExtractCommand::class); + parent::__construct($writer, $reader, $extractor, $defaultLocale, $defaultTransPath, $defaultViewsPath, $transPaths, $codePaths, $enabledLocales); } } diff --git a/Resources/config/console.php b/Resources/config/console.php index 9df82e20e..7168caa4d 100644 --- a/Resources/config/console.php +++ b/Resources/config/console.php @@ -36,7 +36,7 @@ use Symfony\Bundle\FrameworkBundle\Command\SecretsRevealCommand; use Symfony\Bundle\FrameworkBundle\Command\SecretsSetCommand; use Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand; -use Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand; +use Symfony\Bundle\FrameworkBundle\Command\TranslationExtractCommand; use Symfony\Bundle\FrameworkBundle\Command\WorkflowDumpCommand; use Symfony\Bundle\FrameworkBundle\Command\YamlLintCommand; use Symfony\Bundle\FrameworkBundle\Console\Application; @@ -266,7 +266,7 @@ ]) ->tag('console.command') - ->set('console.command.translation_extract', TranslationUpdateCommand::class) + ->set('console.command.translation_extract', TranslationExtractCommand::class) ->args([ service('translation.writer'), service('translation.reader'), diff --git a/Tests/Command/TranslationUpdateCommandCompletionTest.php b/Tests/Command/TranslationExtractCommandCompletionTest.php similarity index 93% rename from Tests/Command/TranslationUpdateCommandCompletionTest.php rename to Tests/Command/TranslationExtractCommandCompletionTest.php index 4627508cb..6d2f22d96 100644 --- a/Tests/Command/TranslationUpdateCommandCompletionTest.php +++ b/Tests/Command/TranslationExtractCommandCompletionTest.php @@ -12,7 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Command; use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand; +use Symfony\Bundle\FrameworkBundle\Command\TranslationExtractCommand; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Component\Console\Tester\CommandCompletionTester; use Symfony\Component\DependencyInjection\Container; @@ -25,7 +25,7 @@ use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\Writer\TranslationWriter; -class TranslationUpdateCommandCompletionTest extends TestCase +class TranslationExtractCommandCompletionTest extends TestCase { private Filesystem $fs; private string $translationDir; @@ -129,7 +129,7 @@ function ($path, $catalogue) use ($loadedMessages) { ->method('getContainer') ->willReturn($container); - $command = new TranslationUpdateCommand($writer, $loader, $extractor, 'en', $this->translationDir.'/translations', $this->translationDir.'/templates', $transPaths, $codePaths, ['en', 'fr']); + $command = new TranslationExtractCommand($writer, $loader, $extractor, 'en', $this->translationDir.'/translations', $this->translationDir.'/templates', $transPaths, $codePaths, ['en', 'fr']); $application = new Application($kernel); $application->add($command); diff --git a/Tests/Command/TranslationUpdateCommandTest.php b/Tests/Command/TranslationExtractCommandTest.php similarity index 96% rename from Tests/Command/TranslationUpdateCommandTest.php rename to Tests/Command/TranslationExtractCommandTest.php index f803c2908..c5e78de12 100644 --- a/Tests/Command/TranslationUpdateCommandTest.php +++ b/Tests/Command/TranslationExtractCommandTest.php @@ -12,7 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Command; use PHPUnit\Framework\TestCase; -use Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand; +use Symfony\Bundle\FrameworkBundle\Command\TranslationExtractCommand; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\DependencyInjection\Container; @@ -26,7 +26,7 @@ use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\Writer\TranslationWriter; -class TranslationUpdateCommandTest extends TestCase +class TranslationExtractCommandTest extends TestCase { private Filesystem $fs; private string $translationDir; @@ -163,9 +163,9 @@ public function testFilterDuplicateTransPaths() } } - $command = $this->createMock(TranslationUpdateCommand::class); + $command = $this->createMock(TranslationExtractCommand::class); - $method = new \ReflectionMethod(TranslationUpdateCommand::class, 'filterDuplicateTransPaths'); + $method = new \ReflectionMethod(TranslationExtractCommand::class, 'filterDuplicateTransPaths'); $filteredTransPaths = $method->invoke($command, $transPaths); @@ -193,7 +193,7 @@ public function testRemoveNoFillTranslationsMethod($noFillCounter, $messages) ->method('set'); // Calling private method - $translationUpdate = $this->createMock(TranslationUpdateCommand::class); + $translationUpdate = $this->createMock(TranslationExtractCommand::class); $reflection = new \ReflectionObject($translationUpdate); $method = $reflection->getMethod('removeNoFillTranslations'); $method->invokeArgs($translationUpdate, [$operation]); @@ -301,7 +301,7 @@ function (MessageCatalogue $catalogue) use ($writerMessages) { ->method('getContainer') ->willReturn($container); - $command = new TranslationUpdateCommand($writer, $loader, $extractor, 'en', $this->translationDir.'/translations', $this->translationDir.'/templates', $transPaths, $codePaths); + $command = new TranslationExtractCommand($writer, $loader, $extractor, 'en', $this->translationDir.'/translations', $this->translationDir.'/templates', $transPaths, $codePaths); $application = new Application($kernel); $application->add($command); From bda64b23c5a42f5054ae6b619e9bce4a08ca9057 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Thu, 5 Dec 2024 00:17:16 +0100 Subject: [PATCH 171/281] chore: fix CS --- Command/AboutCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Command/AboutCommand.php b/Command/AboutCommand.php index f4281cd21..4dc86130a 100644 --- a/Command/AboutCommand.php +++ b/Command/AboutCommand.php @@ -86,7 +86,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int ['Timezone', date_default_timezone_get().' ('.(new \DateTimeImmutable())->format(\DateTimeInterface::W3C).')'], ['OPcache', \extension_loaded('Zend OPcache') ? (filter_var(\ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN) ? 'Enabled' : 'Not enabled') : 'Not installed'], ['APCu', \extension_loaded('apcu') ? (filter_var(\ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? 'Enabled' : 'Not enabled') : 'Not installed'], - ['Xdebug', \extension_loaded('xdebug') ? ($xdebugMode && $xdebugMode !== 'off' ? 'Enabled (' . $xdebugMode . ')' : 'Not enabled') : 'Not installed'], + ['Xdebug', \extension_loaded('xdebug') ? ($xdebugMode && 'off' !== $xdebugMode ? 'Enabled (' . $xdebugMode . ')' : 'Not enabled') : 'Not installed'], ]; $io->table([], $rows); From 10ad5ad527e7371d7d3627b6b2e5c96a99ccf6d8 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 7 Dec 2024 08:58:47 +0100 Subject: [PATCH 172/281] fix translation lint compatibility with the PseudoLocalizationTranslator --- .../Compiler/TranslationLintCommandPass.php | 33 +++++++++++++++++++ FrameworkBundle.php | 3 ++ 2 files changed, 36 insertions(+) create mode 100644 DependencyInjection/Compiler/TranslationLintCommandPass.php diff --git a/DependencyInjection/Compiler/TranslationLintCommandPass.php b/DependencyInjection/Compiler/TranslationLintCommandPass.php new file mode 100644 index 000000000..4756795d1 --- /dev/null +++ b/DependencyInjection/Compiler/TranslationLintCommandPass.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Translation\TranslatorBagInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +final class TranslationLintCommandPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + if (!$container->hasDefinition('console.command.translation_lint') || !$container->has('translator')) { + return; + } + + $translatorClass = $container->getParameterBag()->resolveValue($container->findDefinition('translator')->getClass()); + + if (!is_subclass_of($translatorClass, TranslatorInterface::class) || !is_subclass_of($translatorClass, TranslatorBagInterface::class)) { + $container->removeDefinition('console.command.translation_lint'); + } + } +} diff --git a/FrameworkBundle.php b/FrameworkBundle.php index a1eb059bb..dc7080892 100644 --- a/FrameworkBundle.php +++ b/FrameworkBundle.php @@ -19,6 +19,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RemoveUnusedSessionMarshallingHandlerPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerRealRefPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerWeakRefPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationLintCommandPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\VirtualRequestStackPass; use Symfony\Component\Cache\Adapter\ApcuAdapter; @@ -149,6 +150,8 @@ public function build(ContainerBuilder $container): void $this->addCompilerPassIfExists($container, AddConstraintValidatorsPass::class); $this->addCompilerPassIfExists($container, AddValidatorInitializersPass::class); $this->addCompilerPassIfExists($container, AddConsoleCommandPass::class, PassConfig::TYPE_BEFORE_REMOVING); + // must be registered before the AddConsoleCommandPass + $container->addCompilerPass(new TranslationLintCommandPass(), PassConfig::TYPE_BEFORE_REMOVING, 10); // must be registered as late as possible to get access to all Twig paths registered in // twig.template_iterator definition $this->addCompilerPassIfExists($container, TranslatorPass::class, PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); From 1c630f4697c9bd87b342e8090cc9022071af4d77 Mon Sep 17 00:00:00 2001 From: raphael-geffroy Date: Sat, 7 Dec 2024 12:21:53 +0100 Subject: [PATCH 173/281] fix: notifier channel bus abstract arg --- DependencyInjection/FrameworkExtension.php | 2 +- Resources/config/notifier.php | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 9ddf89ac1..a6d595603 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2784,7 +2784,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ $container->removeDefinition('notifier.channel.email'); } - foreach (['texter', 'chatter', 'notifier.channel.chat', 'notifier.channel.email', 'notifier.channel.sms', 'notifier.channel.push'] as $serviceId) { + foreach (['texter', 'chatter', 'notifier.channel.chat', 'notifier.channel.email', 'notifier.channel.sms', 'notifier.channel.push', 'notifier.channel.desktop'] as $serviceId) { if (!$container->hasDefinition($serviceId)) { continue; } diff --git a/Resources/config/notifier.php b/Resources/config/notifier.php index 71a3cd1fd..28900ad10 100644 --- a/Resources/config/notifier.php +++ b/Resources/config/notifier.php @@ -82,7 +82,10 @@ ->tag('notifier.channel', ['channel' => 'push']) ->set('notifier.channel.desktop', DesktopChannel::class) - ->args([service('texter.transports'), service('messenger.default_bus')->ignoreOnInvalid()]) + ->args([ + service('texter.transports'), + abstract_arg('message bus'), + ]) ->tag('notifier.channel', ['channel' => 'desktop']) ->set('notifier.monolog_handler', NotifierHandler::class) From a2712ce1a8bb200195198c87662886402d8878c6 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Wed, 9 Oct 2024 18:56:29 +0200 Subject: [PATCH 174/281] [FrameworkBundle] [JsonEncoder] Wire services --- CHANGELOG.md | 1 + .../Compiler/UnusedTagsPass.php | 3 + DependencyInjection/Configuration.php | 24 ++++ DependencyInjection/FrameworkExtension.php | 46 ++++++- Resources/config/json_encoder.php | 126 ++++++++++++++++++ Resources/config/schema/symfony-1.0.xsd | 10 ++ .../DependencyInjection/ConfigurationTest.php | 4 + .../Fixtures/php/json_encoder.php | 14 ++ .../DependencyInjection/Fixtures/xml/full.xml | 1 + .../Fixtures/xml/json_encoder.xml | 14 ++ .../DependencyInjection/Fixtures/yml/full.yml | 1 + .../Fixtures/yml/json_encoder.yml | 10 ++ .../FrameworkExtensionTestCase.php | 6 + Tests/Functional/JsonEncoderTest.php | 47 +++++++ .../Functional/app/JsonEncoder/Dto/Dummy.php | 32 +++++ .../app/JsonEncoder/RangeNormalizer.php | 38 ++++++ Tests/Functional/app/JsonEncoder/bundles.php | 18 +++ Tests/Functional/app/JsonEncoder/config.yml | 24 ++++ 18 files changed, 418 insertions(+), 1 deletion(-) create mode 100644 Resources/config/json_encoder.php create mode 100644 Tests/DependencyInjection/Fixtures/php/json_encoder.php create mode 100644 Tests/DependencyInjection/Fixtures/xml/json_encoder.xml create mode 100644 Tests/DependencyInjection/Fixtures/yml/json_encoder.yml create mode 100644 Tests/Functional/JsonEncoderTest.php create mode 100644 Tests/Functional/app/JsonEncoder/Dto/Dummy.php create mode 100644 Tests/Functional/app/JsonEncoder/RangeNormalizer.php create mode 100644 Tests/Functional/app/JsonEncoder/bundles.php create mode 100644 Tests/Functional/app/JsonEncoder/config.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f05ad7a5..d63b01723 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Add support for assets pre-compression * Rename `TranslationUpdateCommand` to `TranslationExtractCommand` + * Add JsonEncoder services and configuration 7.2 --- diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index ae2523e51..45d08a975 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -53,6 +53,9 @@ class UnusedTagsPass implements CompilerPassInterface 'form.type_guesser', 'html_sanitizer', 'http_client.client', + 'json_encoder.denormalizer', + 'json_encoder.encodable', + 'json_encoder.normalizer', 'kernel.cache_clearer', 'kernel.cache_warmer', 'kernel.event_listener', diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 372da4a5e..99592fe49 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -31,6 +31,7 @@ use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\IpUtils; +use Symfony\Component\JsonEncoder\EncoderInterface; use Symfony\Component\Lock\Lock; use Symfony\Component\Lock\Store\SemaphoreStore; use Symfony\Component\Mailer\Mailer; @@ -181,6 +182,7 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addHtmlSanitizerSection($rootNode, $enableIfStandalone); $this->addWebhookSection($rootNode, $enableIfStandalone); $this->addRemoteEventSection($rootNode, $enableIfStandalone); + $this->addJsonEncoderSection($rootNode, $enableIfStandalone); return $treeBuilder; } @@ -2570,4 +2572,26 @@ private function addHtmlSanitizerSection(ArrayNodeDefinition $rootNode, callable ->end() ; } + + private function addJsonEncoderSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone): void + { + $rootNode + ->children() + ->arrayNode('json_encoder') + ->info('JSON encoder configuration') + ->{$enableIfStandalone('symfony/json-encoder', EncoderInterface::class)}() + ->fixXmlConfig('path') + ->children() + ->arrayNode('paths') + ->info('Namespaces and paths of encodable/decodable classes.') + ->normalizeKeys(false) + ->useAttributeAsKey('namespace') + ->scalarPrototype()->end() + ->defaultValue([]) + ->end() + ->end() + ->end() + ->end() + ; + } } diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 3f5157582..862abe3ca 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -99,6 +99,11 @@ use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator; +use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface as JsonEncoderDenormalizerInterface; +use Symfony\Component\JsonEncoder\DecoderInterface as JsonEncoderDecoderInterface; +use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface as JsonEncoderNormalizerInterface; +use Symfony\Component\JsonEncoder\EncoderInterface as JsonEncoderEncoderInterface; +use Symfony\Component\JsonEncoder\JsonEncoder; use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\LockInterface; use Symfony\Component\Lock\PersistingStoreInterface; @@ -176,6 +181,7 @@ use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\TypeResolver\PhpDocAwareReflectionTypeResolver; use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver; +use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface; use Symfony\Component\Uid\Factory\UuidFactory; use Symfony\Component\Uid\UuidV4; use Symfony\Component\Validator\Constraints\ExpressionLanguageProvider; @@ -414,7 +420,7 @@ public function load(array $configs, ContainerBuilder $container): void $container->removeDefinition('console.command.serializer_debug'); } - if ($this->readConfigEnabled('type_info', $container, $config['type_info'])) { + if ($typeInfoEnabled = $this->readConfigEnabled('type_info', $container, $config['type_info'])) { $this->registerTypeInfoConfiguration($container, $loader); } @@ -422,6 +428,14 @@ public function load(array $configs, ContainerBuilder $container): void $this->registerPropertyInfoConfiguration($container, $loader); } + if ($this->readConfigEnabled('json_encoder', $container, $config['json_encoder'])) { + if (!$typeInfoEnabled) { + throw new LogicException('JsonEncoder support cannot be enabled as the TypeInfo component is not '.(interface_exists(TypeResolverInterface::class) ? 'enabled.' : 'installed. Try running "composer require symfony/type-info".')); + } + + $this->registerJsonEncoderConfiguration($config['json_encoder'], $container, $loader); + } + if ($this->readConfigEnabled('lock', $container, $config['lock'])) { $this->registerLockConfiguration($config['lock'], $container, $loader); } @@ -1990,6 +2004,36 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->setParameter('.serializer.named_serializers', $config['named_serializers'] ?? []); } + private function registerJsonEncoderConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void + { + if (!class_exists(JsonEncoder::class)) { + throw new LogicException('JsonEncoder support cannot be enabled as the JsonEncoder component is not installed. Try running "composer require symfony/json-encoder".'); + } + + $container->registerForAutoconfiguration(JsonEncoderNormalizerInterface::class) + ->addTag('json_encoder.normalizer'); + $container->registerForAutoconfiguration(JsonEncoderDenormalizerInterface::class) + ->addTag('json_encoder.denormalizer'); + + $loader->load('json_encoder.php'); + + $container->registerAliasForArgument('json_encoder.encoder', JsonEncoderEncoderInterface::class, 'json.encoder'); + $container->registerAliasForArgument('json_encoder.decoder', JsonEncoderDecoderInterface::class, 'json.decoder'); + + $container->setParameter('.json_encoder.encoders_dir', '%kernel.cache_dir%/json_encoder/encoder'); + $container->setParameter('.json_encoder.decoders_dir', '%kernel.cache_dir%/json_encoder/decoder'); + $container->setParameter('.json_encoder.lazy_ghosts_dir', '%kernel.cache_dir%/json_encoder/lazy_ghost'); + + $encodableDefinition = (new Definition()) + ->setAbstract(true) + ->addTag('container.excluded') + ->addTag('json_encoder.encodable'); + + foreach ($config['paths'] as $namespace => $path) { + $loader->registerClasses($encodableDefinition, $namespace, $path); + } + } + private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void { if (!interface_exists(PropertyInfoExtractorInterface::class)) { diff --git a/Resources/config/json_encoder.php b/Resources/config/json_encoder.php new file mode 100644 index 000000000..b864ed0f9 --- /dev/null +++ b/Resources/config/json_encoder.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\JsonEncoder\CacheWarmer\EncoderDecoderCacheWarmer; +use Symfony\Component\JsonEncoder\CacheWarmer\LazyGhostCacheWarmer; +use Symfony\Component\JsonEncoder\Decode\Denormalizer\DateTimeDenormalizer; +use Symfony\Component\JsonEncoder\Encode\Normalizer\DateTimeNormalizer; +use Symfony\Component\JsonEncoder\JsonDecoder; +use Symfony\Component\JsonEncoder\JsonEncoder; +use Symfony\Component\JsonEncoder\Mapping\Decode\AttributePropertyMetadataLoader as DecodeAttributePropertyMetadataLoader; +use Symfony\Component\JsonEncoder\Mapping\Decode\DateTimeTypePropertyMetadataLoader as DecodeDateTimeTypePropertyMetadataLoader; +use Symfony\Component\JsonEncoder\Mapping\Encode\AttributePropertyMetadataLoader as EncodeAttributePropertyMetadataLoader; +use Symfony\Component\JsonEncoder\Mapping\Encode\DateTimeTypePropertyMetadataLoader as EncodeDateTimeTypePropertyMetadataLoader; +use Symfony\Component\JsonEncoder\Mapping\GenericTypePropertyMetadataLoader; +use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader; + +return static function (ContainerConfigurator $container) { + $container->services() + // encoder/decoder + ->set('json_encoder.encoder', JsonEncoder::class) + ->args([ + tagged_locator('json_encoder.normalizer'), + service('json_encoder.encode.property_metadata_loader'), + param('.json_encoder.encoders_dir'), + false, + ]) + ->set('json_encoder.decoder', JsonDecoder::class) + ->args([ + tagged_locator('json_encoder.denormalizer'), + service('json_encoder.decode.property_metadata_loader'), + param('.json_encoder.decoders_dir'), + param('.json_encoder.lazy_ghosts_dir'), + ]) + ->alias(JsonEncoder::class, 'json_encoder.encoder') + ->alias(JsonDecoder::class, 'json_encoder.decoder') + + // metadata + ->stack('json_encoder.encode.property_metadata_loader', [ + inline_service(EncodeAttributePropertyMetadataLoader::class) + ->args([ + service('.inner'), + tagged_locator('json_encoder.normalizer'), + service('type_info.resolver'), + ]), + inline_service(EncodeDateTimeTypePropertyMetadataLoader::class) + ->args([ + service('.inner'), + ]), + inline_service(GenericTypePropertyMetadataLoader::class) + ->args([ + service('.inner'), + service('type_info.type_context_factory'), + ]), + inline_service(PropertyMetadataLoader::class) + ->args([ + service('type_info.resolver'), + ]), + ]) + + ->stack('json_encoder.decode.property_metadata_loader', [ + inline_service(DecodeAttributePropertyMetadataLoader::class) + ->args([ + service('.inner'), + tagged_locator('json_encoder.denormalizer'), + service('type_info.resolver'), + ]), + inline_service(DecodeDateTimeTypePropertyMetadataLoader::class) + ->args([ + service('.inner'), + ]), + inline_service(GenericTypePropertyMetadataLoader::class) + ->args([ + service('.inner'), + service('type_info.type_context_factory'), + ]), + inline_service(PropertyMetadataLoader::class) + ->args([ + service('type_info.resolver'), + ]), + ]) + + // normalizers/denormalizers + ->set('json_encoder.normalizer.date_time', DateTimeNormalizer::class) + ->tag('json_encoder.normalizer') + ->set('json_encoder.denormalizer.date_time', DateTimeDenormalizer::class) + ->args([ + false, + ]) + ->tag('json_encoder.denormalizer') + ->set('json_encoder.denormalizer.date_time_immutable', DateTimeDenormalizer::class) + ->args([ + true, + ]) + ->tag('json_encoder.denormalizer') + + // cache + ->set('.json_encoder.cache_warmer.encoder_decoder', EncoderDecoderCacheWarmer::class) + ->args([ + tagged_iterator('json_encoder.encodable'), + service('json_encoder.encode.property_metadata_loader'), + service('json_encoder.decode.property_metadata_loader'), + param('.json_encoder.encoders_dir'), + param('.json_encoder.decoders_dir'), + false, + service('logger')->ignoreOnInvalid(), + ]) + ->tag('kernel.cache_warmer') + + ->set('.json_encoder.cache_warmer.lazy_ghost', LazyGhostCacheWarmer::class) + ->args([ + tagged_iterator('json_encoder.encodable'), + param('.json_encoder.lazy_ghosts_dir'), + ]) + ->tag('kernel.cache_warmer') + ; +}; diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index 91528b60f..9cb89207d 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -46,6 +46,7 @@ + @@ -1003,4 +1004,13 @@ + + + + + + + + + diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index c7113cfb4..963cac638 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -970,6 +970,10 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor 'remote-event' => [ 'enabled' => !class_exists(FullStack::class) && class_exists(RemoteEvent::class), ], + 'json_encoder' => [ + 'enabled' => false, + 'paths' => [], + ], ]; } diff --git a/Tests/DependencyInjection/Fixtures/php/json_encoder.php b/Tests/DependencyInjection/Fixtures/php/json_encoder.php new file mode 100644 index 000000000..42204b2cb --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/json_encoder.php @@ -0,0 +1,14 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'type_info' => [ + 'enabled' => true, + ], + 'json_encoder' => [ + 'enabled' => true, + ], +]); diff --git a/Tests/DependencyInjection/Fixtures/xml/full.xml b/Tests/DependencyInjection/Fixtures/xml/full.xml index c01e85783..a3e5cfd88 100644 --- a/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -46,5 +46,6 @@ + diff --git a/Tests/DependencyInjection/Fixtures/xml/json_encoder.xml b/Tests/DependencyInjection/Fixtures/xml/json_encoder.xml new file mode 100644 index 000000000..a20f98567 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/json_encoder.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/Tests/DependencyInjection/Fixtures/yml/full.yml b/Tests/DependencyInjection/Fixtures/yml/full.yml index 7550749eb..8e272d11b 100644 --- a/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -70,3 +70,4 @@ framework: formats: csv: ['text/csv', 'text/plain'] pdf: 'application/pdf' + json_encoder: ~ diff --git a/Tests/DependencyInjection/Fixtures/yml/json_encoder.yml b/Tests/DependencyInjection/Fixtures/yml/json_encoder.yml new file mode 100644 index 000000000..e09f7c7d3 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/json_encoder.yml @@ -0,0 +1,10 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + type_info: + enabled: true + json_encoder: + enabled: true diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 798217191..0446eb5d2 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -2497,6 +2497,12 @@ public function testSemaphoreWithService() self::assertEquals(new Reference('my_service'), $storeDef->getArgument(0)); } + public function testJsonEncoderEnabled() + { + $container = $this->createContainerFromFile('json_encoder'); + $this->assertTrue($container->has('json_encoder.encoder')); + } + protected function createContainer(array $data = []) { return new ContainerBuilder(new EnvPlaceholderParameterBag(array_merge([ diff --git a/Tests/Functional/JsonEncoderTest.php b/Tests/Functional/JsonEncoderTest.php new file mode 100644 index 000000000..0ab66e6c1 --- /dev/null +++ b/Tests/Functional/JsonEncoderTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\Dummy; +use Symfony\Component\JsonEncoder\DecoderInterface; +use Symfony\Component\JsonEncoder\EncoderInterface; +use Symfony\Component\TypeInfo\Type; + +/** + * @author Mathias Arlaud + */ +class JsonEncoderTest extends AbstractWebTestCase +{ + public function testEncode() + { + static::bootKernel(['test_case' => 'JsonEncoder']); + + /** @var EncoderInterface $encoder */ + $encoder = static::getContainer()->get('json_encoder.encoder.alias'); + + $this->assertSame('{"@name":"DUMMY","range":"10..20"}', (string) $encoder->encode(new Dummy(), Type::object(Dummy::class))); + } + + public function testDecode() + { + static::bootKernel(['test_case' => 'JsonEncoder']); + + /** @var DecoderInterface $decoder */ + $decoder = static::getContainer()->get('json_encoder.decoder.alias'); + + $expected = new Dummy(); + $expected->name = 'dummy'; + $expected->range = [0, 1]; + + $this->assertEquals($expected, $decoder->decode('{"@name": "DUMMY", "range": "0..1"}', Type::object(Dummy::class))); + } +} diff --git a/Tests/Functional/app/JsonEncoder/Dto/Dummy.php b/Tests/Functional/app/JsonEncoder/Dto/Dummy.php new file mode 100644 index 000000000..344b9d11c --- /dev/null +++ b/Tests/Functional/app/JsonEncoder/Dto/Dummy.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto; + +use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer; +use Symfony\Component\JsonEncoder\Attribute\Denormalizer; +use Symfony\Component\JsonEncoder\Attribute\EncodedName; +use Symfony\Component\JsonEncoder\Attribute\Normalizer; + +/** + * @author Mathias Arlaud + */ +class Dummy +{ + #[EncodedName('@name')] + #[Normalizer('strtoupper')] + #[Denormalizer('strtolower')] + public string $name = 'dummy'; + + #[Normalizer(RangeNormalizer::class)] + #[Denormalizer(RangeNormalizer::class)] + public array $range = [10, 20]; +} diff --git a/Tests/Functional/app/JsonEncoder/RangeNormalizer.php b/Tests/Functional/app/JsonEncoder/RangeNormalizer.php new file mode 100644 index 000000000..beb9e8188 --- /dev/null +++ b/Tests/Functional/app/JsonEncoder/RangeNormalizer.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder; + +use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface; +use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface; +use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\Type\BuiltinType; + +/** + * @author Mathias Arlaud + */ +class RangeNormalizer implements NormalizerInterface, DenormalizerInterface +{ + public function normalize(mixed $denormalized, array $options = []): string + { + return $denormalized[0].'..'.$denormalized[1]; + } + + public function denormalize(mixed $normalized, array $options = []): array + { + return array_map(static fn (string $v): int => (int) $v, explode('..', $normalized)); + } + + public static function getNormalizedType(): BuiltinType + { + return Type::string(); + } +} diff --git a/Tests/Functional/app/JsonEncoder/bundles.php b/Tests/Functional/app/JsonEncoder/bundles.php new file mode 100644 index 000000000..15ff182c6 --- /dev/null +++ b/Tests/Functional/app/JsonEncoder/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return [ + new FrameworkBundle(), + new TestBundle(), +]; diff --git a/Tests/Functional/app/JsonEncoder/config.yml b/Tests/Functional/app/JsonEncoder/config.yml new file mode 100644 index 000000000..55fdf53f5 --- /dev/null +++ b/Tests/Functional/app/JsonEncoder/config.yml @@ -0,0 +1,24 @@ +imports: + - { resource: ../config/default.yml } + +framework: + http_method_override: false + type_info: ~ + json_encoder: + enabled: true + paths: + Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\: '../../Tests/Functional/app/JsonEncoder/Dto/*' + +services: + _defaults: + autoconfigure: true + + json_encoder.encoder.alias: + alias: json_encoder.encoder + public: true + + json_encoder.decoder.alias: + alias: json_encoder.decoder + public: true + + Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer: ~ From 4bf82367c2efedec338d102ebb1b4e6938b040de Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 11 Dec 2024 13:53:11 +0100 Subject: [PATCH 175/281] [FrameworkBundle] Fix `JsonEncoder` config on low-deps --- Tests/DependencyInjection/ConfigurationTest.php | 3 ++- composer.json | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 963cac638..b4b8eb875 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -22,6 +22,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HtmlSanitizer\HtmlSanitizer; use Symfony\Component\HttpClient\HttpClient; +use Symfony\Component\JsonEncoder\JsonEncoder; use Symfony\Component\Lock\Store\SemaphoreStore; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Messenger\MessageBusInterface; @@ -971,7 +972,7 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor 'enabled' => !class_exists(FullStack::class) && class_exists(RemoteEvent::class), ], 'json_encoder' => [ - 'enabled' => false, + 'enabled' => !class_exists(FullStack::class) && class_exists(JsonEncoder::class), 'paths' => [], ], ]; diff --git a/composer.json b/composer.json index 9b3e7c86e..81dade063 100644 --- a/composer.json +++ b/composer.json @@ -69,6 +69,7 @@ "symfony/workflow": "^6.4|^7.0", "symfony/yaml": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", + "symfony/json-encoder": "^7.3", "symfony/uid": "^6.4|^7.0", "symfony/web-link": "^6.4|^7.0", "symfony/webhook": "^7.2", From d22981663bd17c9d2fa710cdd8cdc464ed8f47be Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Wed, 11 Dec 2024 16:32:39 +0100 Subject: [PATCH 176/281] [JsonEncoder] [FrameworkBundle] Fix service definition --- Resources/config/json_encoder.php | 86 ++++++++++++++++--------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/Resources/config/json_encoder.php b/Resources/config/json_encoder.php index b864ed0f9..421f10c9a 100644 --- a/Resources/config/json_encoder.php +++ b/Resources/config/json_encoder.php @@ -45,49 +45,51 @@ ->alias(JsonDecoder::class, 'json_encoder.decoder') // metadata - ->stack('json_encoder.encode.property_metadata_loader', [ - inline_service(EncodeAttributePropertyMetadataLoader::class) - ->args([ - service('.inner'), - tagged_locator('json_encoder.normalizer'), - service('type_info.resolver'), - ]), - inline_service(EncodeDateTimeTypePropertyMetadataLoader::class) - ->args([ - service('.inner'), - ]), - inline_service(GenericTypePropertyMetadataLoader::class) - ->args([ - service('.inner'), - service('type_info.type_context_factory'), - ]), - inline_service(PropertyMetadataLoader::class) - ->args([ - service('type_info.resolver'), - ]), - ]) + ->set('json_encoder.encode.property_metadata_loader', PropertyMetadataLoader::class) + ->args([ + service('type_info.resolver'), + ]) + ->set('.json_encoder.encode.property_metadata_loader.generic', GenericTypePropertyMetadataLoader::class) + ->decorate('json_encoder.encode.property_metadata_loader') + ->args([ + service('.inner'), + service('type_info.type_context_factory'), + ]) + ->set('.json_encoder.encode.property_metadata_loader.date_time', EncodeDateTimeTypePropertyMetadataLoader::class) + ->decorate('json_encoder.encode.property_metadata_loader') + ->args([ + service('.inner'), + ]) + ->set('.json_encoder.encode.property_metadata_loader.attribute', EncodeAttributePropertyMetadataLoader::class) + ->decorate('json_encoder.encode.property_metadata_loader') + ->args([ + service('.inner'), + tagged_locator('json_encoder.normalizer'), + service('type_info.resolver'), + ]) - ->stack('json_encoder.decode.property_metadata_loader', [ - inline_service(DecodeAttributePropertyMetadataLoader::class) - ->args([ - service('.inner'), - tagged_locator('json_encoder.denormalizer'), - service('type_info.resolver'), - ]), - inline_service(DecodeDateTimeTypePropertyMetadataLoader::class) - ->args([ - service('.inner'), - ]), - inline_service(GenericTypePropertyMetadataLoader::class) - ->args([ - service('.inner'), - service('type_info.type_context_factory'), - ]), - inline_service(PropertyMetadataLoader::class) - ->args([ - service('type_info.resolver'), - ]), - ]) + ->set('json_encoder.decode.property_metadata_loader', PropertyMetadataLoader::class) + ->args([ + service('type_info.resolver'), + ]) + ->set('.json_encoder.decode.property_metadata_loader.generic', GenericTypePropertyMetadataLoader::class) + ->decorate('json_encoder.decode.property_metadata_loader') + ->args([ + service('.inner'), + service('type_info.type_context_factory'), + ]) + ->set('.json_encoder.decode.property_metadata_loader.date_time', DecodeDateTimeTypePropertyMetadataLoader::class) + ->decorate('json_encoder.decode.property_metadata_loader') + ->args([ + service('.inner'), + ]) + ->set('.json_encoder.decode.property_metadata_loader.attribute', DecodeAttributePropertyMetadataLoader::class) + ->decorate('json_encoder.decode.property_metadata_loader') + ->args([ + service('.inner'), + tagged_locator('json_encoder.normalizer'), + service('type_info.resolver'), + ]) // normalizers/denormalizers ->set('json_encoder.normalizer.date_time', DateTimeNormalizer::class) From b77eac006b9314ecb76d63023429b707105fe760 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Wed, 11 Dec 2024 13:35:15 +0100 Subject: [PATCH 177/281] [JsonEncoder] Add native lazyghost support --- DependencyInjection/FrameworkExtension.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 862abe3ca..d911b767d 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2032,6 +2032,10 @@ private function registerJsonEncoderConfiguration(array $config, ContainerBuilde foreach ($config['paths'] as $namespace => $path) { $loader->registerClasses($encodableDefinition, $namespace, $path); } + + if (\PHP_VERSION_ID >= 80400) { + $container->removeDefinition('.json_encoder.cache_warmer.lazy_ghost'); + } } private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void From bea1598816d20bd6a3b9ae92a80b4e793697854e Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 11 Dec 2024 15:33:47 +0100 Subject: [PATCH 178/281] [FrameworkBundle] Fix `symfony/translation` conflict --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 81dade063..afaa9b03b 100644 --- a/composer.json +++ b/composer.json @@ -62,7 +62,7 @@ "symfony/serializer": "^7.1", "symfony/stopwatch": "^6.4|^7.0", "symfony/string": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", + "symfony/translation": "^6.4.3|^7.0", "symfony/twig-bundle": "^6.4|^7.0", "symfony/type-info": "^7.1", "symfony/validator": "^6.4|^7.0", @@ -100,7 +100,7 @@ "symfony/security-core": "<6.4", "symfony/serializer": "<7.1", "symfony/stopwatch": "<6.4", - "symfony/translation": "<6.4", + "symfony/translation": "<6.4.3", "symfony/twig-bridge": "<6.4", "symfony/twig-bundle": "<6.4", "symfony/validator": "<6.4", From 8eb6543e74ff33e1e268867ebc931aae1aef8dea Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Wed, 11 Dec 2024 14:08:35 +0100 Subject: [PATCH 179/281] chore: PHP CS Fixer fixes --- Command/AboutCommand.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Command/AboutCommand.php b/Command/AboutCommand.php index 4dc86130a..0c6899328 100644 --- a/Command/AboutCommand.php +++ b/Command/AboutCommand.php @@ -84,9 +84,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int ['Architecture', (\PHP_INT_SIZE * 8).' bits'], ['Intl locale', class_exists(\Locale::class, false) && \Locale::getDefault() ? \Locale::getDefault() : 'n/a'], ['Timezone', date_default_timezone_get().' ('.(new \DateTimeImmutable())->format(\DateTimeInterface::W3C).')'], - ['OPcache', \extension_loaded('Zend OPcache') ? (filter_var(\ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN) ? 'Enabled' : 'Not enabled') : 'Not installed'], - ['APCu', \extension_loaded('apcu') ? (filter_var(\ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? 'Enabled' : 'Not enabled') : 'Not installed'], - ['Xdebug', \extension_loaded('xdebug') ? ($xdebugMode && 'off' !== $xdebugMode ? 'Enabled (' . $xdebugMode . ')' : 'Not enabled') : 'Not installed'], + ['OPcache', \extension_loaded('Zend OPcache') ? (filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) ? 'Enabled' : 'Not enabled') : 'Not installed'], + ['APCu', \extension_loaded('apcu') ? (filter_var(\ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN) ? 'Enabled' : 'Not enabled') : 'Not installed'], + ['Xdebug', \extension_loaded('xdebug') ? ($xdebugMode && 'off' !== $xdebugMode ? 'Enabled ('.$xdebugMode.')' : 'Not enabled') : 'Not installed'], ]; $io->table([], $rows); From 06e1348872bae94fff34a6f231822b3cf697f492 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 11 Dec 2024 16:44:21 +0100 Subject: [PATCH 180/281] do not allow symfony/json-encoder 7.4 yet The component is experimental. Claiming to be compatible with 7.4 could lead to having to change a stable 7.3 release of the FrameworkBundle if the JsonEncoder component introduced BC breaks in 7.4. --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index afaa9b03b..a7673d9f7 100644 --- a/composer.json +++ b/composer.json @@ -69,7 +69,7 @@ "symfony/workflow": "^6.4|^7.0", "symfony/yaml": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", - "symfony/json-encoder": "^7.3", + "symfony/json-encoder": "7.3.*", "symfony/uid": "^6.4|^7.0", "symfony/web-link": "^6.4|^7.0", "symfony/webhook": "^7.2", @@ -88,6 +88,7 @@ "symfony/dom-crawler": "<6.4", "symfony/http-client": "<6.4", "symfony/form": "<6.4", + "symfony/json-encoder": ">=7.4", "symfony/lock": "<6.4", "symfony/mailer": "<6.4", "symfony/messenger": "<6.4", From 7bb5613289191d80a8e1e60b9eced2e83edfc6ef Mon Sep 17 00:00:00 2001 From: valtzu Date: Wed, 27 Nov 2024 22:28:12 +0200 Subject: [PATCH 181/281] Generate url-safe signatures --- Tests/Functional/FragmentTest.php | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Functional/FragmentTest.php b/Tests/Functional/FragmentTest.php index 6d8966a17..b530d2cbc 100644 --- a/Tests/Functional/FragmentTest.php +++ b/Tests/Functional/FragmentTest.php @@ -50,6 +50,6 @@ public function testGenerateFragmentUri() $client = self::createClient(['test_case' => 'Fragment', 'root_config' => 'config.yml', 'debug' => true]); $client->request('GET', '/fragment_uri'); - $this->assertSame('/_fragment?_hash=CCRGN2D%2FoAJbeGz%2F%2FdoH3bNSPwLCrmwC1zAYCGIKJ0E%3D&_path=_format%3Dhtml%26_locale%3Den%26_controller%3DSymfony%255CBundle%255CFrameworkBundle%255CTests%255CFunctional%255CBundle%255CTestBundle%255CController%255CFragmentController%253A%253AindexAction', $client->getResponse()->getContent()); + $this->assertSame('/_fragment?_hash=CCRGN2D_oAJbeGz__doH3bNSPwLCrmwC1zAYCGIKJ0E&_path=_format%3Dhtml%26_locale%3Den%26_controller%3DSymfony%255CBundle%255CFrameworkBundle%255CTests%255CFunctional%255CBundle%255CTestBundle%255CController%255CFragmentController%253A%253AindexAction', $client->getResponse()->getContent()); } } diff --git a/composer.json b/composer.json index afaa9b03b..ef669713f 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.4|^7.0", "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-foundation": "^7.3", "symfony/http-kernel": "^7.2", "symfony/polyfill-mbstring": "~1.0", "symfony/filesystem": "^7.1", From bc39c5b05c9944dbe1d4dfd50419828bffdafccf Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Sat, 7 Dec 2024 10:46:53 +0100 Subject: [PATCH 182/281] [JsonEncoder] Remove chunk size definition --- Resources/config/json_encoder.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/Resources/config/json_encoder.php b/Resources/config/json_encoder.php index 421f10c9a..24f596fd4 100644 --- a/Resources/config/json_encoder.php +++ b/Resources/config/json_encoder.php @@ -32,7 +32,6 @@ tagged_locator('json_encoder.normalizer'), service('json_encoder.encode.property_metadata_loader'), param('.json_encoder.encoders_dir'), - false, ]) ->set('json_encoder.decoder', JsonDecoder::class) ->args([ @@ -113,7 +112,6 @@ service('json_encoder.decode.property_metadata_loader'), param('.json_encoder.encoders_dir'), param('.json_encoder.decoders_dir'), - false, service('logger')->ignoreOnInvalid(), ]) ->tag('kernel.cache_warmer') From 6623abcaabbf97fbac9f7da993f4cf148d557e94 Mon Sep 17 00:00:00 2001 From: Farhad Hedayatifard Date: Mon, 28 Oct 2024 22:24:40 +0330 Subject: [PATCH 183/281] [Mailer] Add AhaSend Bridge --- DependencyInjection/FrameworkExtension.php | 2 ++ Resources/config/mailer_transports.php | 2 ++ Resources/config/mailer_webhook.php | 7 +++++++ 3 files changed, 11 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index a7749cd30..0c87c4aea 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2670,6 +2670,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co } $classToServices = [ + MailerBridge\AhaSend\Transport\AhaSendTransportFactory::class => 'mailer.transport_factory.ahasend', MailerBridge\Azure\Transport\AzureTransportFactory::class => 'mailer.transport_factory.azure', MailerBridge\Brevo\Transport\BrevoTransportFactory::class => 'mailer.transport_factory.brevo', MailerBridge\Google\Transport\GmailTransportFactory::class => 'mailer.transport_factory.gmail', @@ -2700,6 +2701,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co if ($webhookEnabled) { $webhookRequestParsers = [ + MailerBridge\AhaSend\Webhook\AhaSendRequestParser::class => 'mailer.webhook.request_parser.ahasend', MailerBridge\Brevo\Webhook\BrevoRequestParser::class => 'mailer.webhook.request_parser.brevo', MailerBridge\MailerSend\Webhook\MailerSendRequestParser::class => 'mailer.webhook.request_parser.mailersend', MailerBridge\Mailchimp\Webhook\MailchimpRequestParser::class => 'mailer.webhook.request_parser.mailchimp', diff --git a/Resources/config/mailer_transports.php b/Resources/config/mailer_transports.php index c0e7cc06a..2c79b4d55 100644 --- a/Resources/config/mailer_transports.php +++ b/Resources/config/mailer_transports.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; +use Symfony\Component\Mailer\Bridge\AhaSend\Transport\AhaSendTransportFactory; use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesTransportFactory; use Symfony\Component\Mailer\Bridge\Azure\Transport\AzureTransportFactory; use Symfony\Component\Mailer\Bridge\Brevo\Transport\BrevoTransportFactory; @@ -47,6 +48,7 @@ ->tag('monolog.logger', ['channel' => 'mailer']); $factories = [ + 'ahasend' => AhaSendTransportFactory::class, 'amazon' => SesTransportFactory::class, 'azure' => AzureTransportFactory::class, 'brevo' => BrevoTransportFactory::class, diff --git a/Resources/config/mailer_webhook.php b/Resources/config/mailer_webhook.php index c574324db..b815336b2 100644 --- a/Resources/config/mailer_webhook.php +++ b/Resources/config/mailer_webhook.php @@ -11,6 +11,8 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; +use Symfony\Component\Mailer\Bridge\AhaSend\RemoteEvent\AhaSendPayloadConverter; +use Symfony\Component\Mailer\Bridge\AhaSend\Webhook\AhaSendRequestParser; use Symfony\Component\Mailer\Bridge\Brevo\RemoteEvent\BrevoPayloadConverter; use Symfony\Component\Mailer\Bridge\Brevo\Webhook\BrevoRequestParser; use Symfony\Component\Mailer\Bridge\Mailchimp\RemoteEvent\MailchimpPayloadConverter; @@ -86,6 +88,11 @@ ->args([service('mailer.payload_converter.sweego')]) ->alias(SweegoRequestParser::class, 'mailer.webhook.request_parser.sweego') + ->set('mailer.payload_converter.ahasend', AhaSendPayloadConverter::class) + ->set('mailer.webhook.request_parser.ahasend', AhaSendRequestParser::class) + ->args([service('mailer.payload_converter.ahasend')]) + ->alias(AhaSendRequestParser::class, 'mailer.webhook.request_parser.ahasend') + ->set('mailer.payload_converter.mailchimp', MailchimpPayloadConverter::class) ->set('mailer.webhook.request_parser.mailchimp', MailchimpRequestParser::class) ->args([service('mailer.payload_converter.mailchimp')]) From b36a93239efa717c70b918681c4baf3ed86452b2 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 5 Jan 2025 20:49:23 +0100 Subject: [PATCH 184/281] [Security] OAuth2 Introspection Endpoint (RFC7662) In addition to the excellent work of @vincentchalamon #48272, this PR allows getting the data from the OAuth2 Introspection Endpoint. This endpoint is defined in the [RFC7662](https://datatracker.ietf.org/doc/html/rfc7662). It returns the following information that is used to retrieve the user: * If the access token is active * A set of claims that are similar to the OIDC one, including the `sub` or the `username`. --- DependencyInjection/Compiler/UnusedTagsPass.php | 1 + 1 file changed, 1 insertion(+) diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index 45d08a975..c135538c2 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -85,6 +85,7 @@ class UnusedTagsPass implements CompilerPassInterface 'routing.route_loader', 'scheduler.schedule_provider', 'scheduler.task', + 'security.access_token_handler.oidc.encryption_algorithm', 'security.access_token_handler.oidc.signature_algorithm', 'security.authenticator.login_linker', 'security.expression_language_provider', From b67010785788ca19137b05e176fcf8530bad7358 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Sat, 27 May 2023 16:13:37 +0200 Subject: [PATCH 185/281] [FrameworkBundle][PropertyInfo] Wire the `ConstructorExtractor` class --- CHANGELOG.md | 1 + DependencyInjection/Compiler/UnusedTagsPass.php | 1 + DependencyInjection/Configuration.php | 15 +++++++++++++++ DependencyInjection/FrameworkExtension.php | 13 +++++++++++-- FrameworkBundle.php | 2 ++ Resources/config/property_info.php | 6 ++++++ Resources/config/schema/symfony-1.0.xsd | 1 + Tests/DependencyInjection/ConfigurationTest.php | 2 +- Tests/DependencyInjection/Fixtures/php/full.php | 5 ++++- .../Fixtures/php/property_info.php | 1 + .../property_info_with_constructor_extractor.php | 12 ++++++++++++ .../Fixtures/php/validation_auto_mapping.php | 5 ++++- Tests/DependencyInjection/Fixtures/xml/full.xml | 2 +- .../Fixtures/xml/property_info.xml | 2 +- .../property_info_with_constructor_extractor.xml | 13 +++++++++++++ .../Fixtures/xml/validation_auto_mapping.xml | 2 +- Tests/DependencyInjection/Fixtures/yml/full.yml | 3 ++- .../Fixtures/yml/property_info.yml | 1 + .../property_info_with_constructor_extractor.yml | 9 +++++++++ .../Fixtures/yml/validation_auto_mapping.yml | 4 +++- .../FrameworkExtensionTestCase.php | 8 ++++++++ Tests/Functional/app/ApiAttributesTest/config.yml | 4 +++- Tests/Functional/app/ContainerDump/config.yml | 4 +++- Tests/Functional/app/Serializer/config.yml | 4 +++- 24 files changed, 107 insertions(+), 13 deletions(-) create mode 100644 Tests/DependencyInjection/Fixtures/php/property_info_with_constructor_extractor.php create mode 100644 Tests/DependencyInjection/Fixtures/xml/property_info_with_constructor_extractor.xml create mode 100644 Tests/DependencyInjection/Fixtures/yml/property_info_with_constructor_extractor.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index d63b01723..02bfe6af4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * Add support for assets pre-compression * Rename `TranslationUpdateCommand` to `TranslationExtractCommand` * Add JsonEncoder services and configuration + * Add new `framework.property_info.with_constructor_extractor` option to allow enabling or disabling the constructor extractor integration 7.2 --- diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index 45d08a975..a2a571f83 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -73,6 +73,7 @@ class UnusedTagsPass implements CompilerPassInterface 'monolog.logger', 'notifier.channel', 'property_info.access_extractor', + 'property_info.constructor_extractor', 'property_info.initializable_extractor', 'property_info.list_extractor', 'property_info.type_extractor', diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 99592fe49..cbfe657a6 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1226,8 +1226,23 @@ private function addPropertyInfoSection(ArrayNodeDefinition $rootNode, callable ->arrayNode('property_info') ->info('Property info configuration') ->{$enableIfStandalone('symfony/property-info', PropertyInfoExtractorInterface::class)}() + ->children() + ->booleanNode('with_constructor_extractor') + ->info('Registers the constructor extractor.') + ->end() + ->end() ->end() ->end() + ->validate() + ->ifTrue(fn ($v) => $v['property_info']['enabled'] && !isset($v['property_info']['with_constructor_extractor'])) + ->then(function ($v) { + $v['property_info']['with_constructor_extractor'] = false; + + trigger_deprecation('symfony/property-info', '7.3', 'Not setting the "with_constructor_extractor" option explicitly is deprecated because its default value will change in version 8.0.'); + + return $v; + }) + ->end() ; } diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index eb0838228..5245e8597 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -135,6 +135,7 @@ use Symfony\Component\Notifier\Transport\TransportFactoryInterface as NotifierTransportFactoryInterface; use Symfony\Component\Process\Messenger\RunProcessMessageHandler; use Symfony\Component\PropertyAccess\PropertyAccessor; +use Symfony\Component\PropertyInfo\Extractor\ConstructorArgumentTypeExtractorInterface; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\PhpStanExtractor; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; @@ -427,7 +428,7 @@ public function load(array $configs, ContainerBuilder $container): void } if ($propertyInfoEnabled) { - $this->registerPropertyInfoConfiguration($container, $loader); + $this->registerPropertyInfoConfiguration($config['property_info'], $container, $loader); } if ($this->readConfigEnabled('json_encoder', $container, $config['json_encoder'])) { @@ -657,6 +658,8 @@ public function load(array $configs, ContainerBuilder $container): void ->addTag('property_info.list_extractor'); $container->registerForAutoconfiguration(PropertyTypeExtractorInterface::class) ->addTag('property_info.type_extractor'); + $container->registerForAutoconfiguration(ConstructorArgumentTypeExtractorInterface::class) + ->addTag('property_info.constructor_extractor'); $container->registerForAutoconfiguration(PropertyDescriptionExtractorInterface::class) ->addTag('property_info.description_extractor'); $container->registerForAutoconfiguration(PropertyAccessExtractorInterface::class) @@ -2040,7 +2043,7 @@ private function registerJsonEncoderConfiguration(array $config, ContainerBuilde } } - private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void + private function registerPropertyInfoConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void { if (!interface_exists(PropertyInfoExtractorInterface::class)) { throw new LogicException('PropertyInfo support cannot be enabled as the PropertyInfo component is not installed. Try running "composer require symfony/property-info".'); @@ -2048,18 +2051,24 @@ private function registerPropertyInfoConfiguration(ContainerBuilder $container, $loader->load('property_info.php'); + if (!$config['with_constructor_extractor']) { + $container->removeDefinition('property_info.constructor_extractor'); + } + if ( ContainerBuilder::willBeAvailable('phpstan/phpdoc-parser', PhpDocParser::class, ['symfony/framework-bundle', 'symfony/property-info']) && ContainerBuilder::willBeAvailable('phpdocumentor/type-resolver', ContextFactory::class, ['symfony/framework-bundle', 'symfony/property-info']) ) { $definition = $container->register('property_info.phpstan_extractor', PhpStanExtractor::class); $definition->addTag('property_info.type_extractor', ['priority' => -1000]); + $definition->addTag('property_info.constructor_extractor', ['priority' => -1000]); } if (ContainerBuilder::willBeAvailable('phpdocumentor/reflection-docblock', DocBlockFactoryInterface::class, ['symfony/framework-bundle', 'symfony/property-info'], true)) { $definition = $container->register('property_info.php_doc_extractor', PhpDocExtractor::class); $definition->addTag('property_info.description_extractor', ['priority' => -1000]); $definition->addTag('property_info.type_extractor', ['priority' => -1001]); + $definition->addTag('property_info.constructor_extractor', ['priority' => -1001]); } if ($container->getParameter('kernel.debug')) { diff --git a/FrameworkBundle.php b/FrameworkBundle.php index e83c4dfe6..ecd0fe682 100644 --- a/FrameworkBundle.php +++ b/FrameworkBundle.php @@ -56,6 +56,7 @@ use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Messenger\DependencyInjection\MessengerPass; use Symfony\Component\Mime\DependencyInjection\AddMimeTypeGuesserPass; +use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoConstructorPass; use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass; use Symfony\Component\Routing\DependencyInjection\AddExpressionLanguageProvidersPass; use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass; @@ -164,6 +165,7 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new FragmentRendererPass()); $this->addCompilerPassIfExists($container, SerializerPass::class); $this->addCompilerPassIfExists($container, PropertyInfoPass::class); + $this->addCompilerPassIfExists($container, PropertyInfoConstructorPass::class); $container->addCompilerPass(new ControllerArgumentValueResolverPass()); $container->addCompilerPass(new CachePoolPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 32); $container->addCompilerPass(new CachePoolClearerPass(), PassConfig::TYPE_AFTER_REMOVING); diff --git a/Resources/config/property_info.php b/Resources/config/property_info.php index 90587839d..f45d6ce2b 100644 --- a/Resources/config/property_info.php +++ b/Resources/config/property_info.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; +use Symfony\Component\PropertyInfo\Extractor\ConstructorExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface; @@ -43,9 +44,14 @@ ->set('property_info.reflection_extractor', ReflectionExtractor::class) ->tag('property_info.list_extractor', ['priority' => -1000]) ->tag('property_info.type_extractor', ['priority' => -1002]) + ->tag('property_info.constructor_extractor', ['priority' => -1002]) ->tag('property_info.access_extractor', ['priority' => -1000]) ->tag('property_info.initializable_extractor', ['priority' => -1000]) + ->set('property_info.constructor_extractor', ConstructorExtractor::class) + ->args([[]]) + ->tag('property_info.type_extractor', ['priority' => -999]) + ->alias(PropertyReadInfoExtractorInterface::class, 'property_info.reflection_extractor') ->alias(PropertyWriteInfoExtractorInterface::class, 'property_info.reflection_extractor') ; diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index 9cb89207d..b44f1ccc4 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -370,6 +370,7 @@ + diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index b4b8eb875..f1e88b11b 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -808,7 +808,7 @@ protected static function getBundleDefaultConfig() ], 'property_info' => [ 'enabled' => !class_exists(FullStack::class), - ], + ] + (!class_exists(FullStack::class) ? ['with_constructor_extractor' => false] : []), 'router' => [ 'enabled' => false, 'default_uri' => null, diff --git a/Tests/DependencyInjection/Fixtures/php/full.php b/Tests/DependencyInjection/Fixtures/php/full.php index 0a32ce8b3..cb7762829 100644 --- a/Tests/DependencyInjection/Fixtures/php/full.php +++ b/Tests/DependencyInjection/Fixtures/php/full.php @@ -74,7 +74,10 @@ ], ], ], - 'property_info' => true, + 'property_info' => [ + 'enabled' => true, + 'with_constructor_extractor' => true, + ], 'type_info' => true, 'ide' => 'file%%link%%format', 'request' => [ diff --git a/Tests/DependencyInjection/Fixtures/php/property_info.php b/Tests/DependencyInjection/Fixtures/php/property_info.php index b234c4527..e2437e2c2 100644 --- a/Tests/DependencyInjection/Fixtures/php/property_info.php +++ b/Tests/DependencyInjection/Fixtures/php/property_info.php @@ -7,5 +7,6 @@ 'php_errors' => ['log' => true], 'property_info' => [ 'enabled' => true, + 'with_constructor_extractor' => false, ], ]); diff --git a/Tests/DependencyInjection/Fixtures/php/property_info_with_constructor_extractor.php b/Tests/DependencyInjection/Fixtures/php/property_info_with_constructor_extractor.php new file mode 100644 index 000000000..fa143d2e1 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/property_info_with_constructor_extractor.php @@ -0,0 +1,12 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'property_info' => [ + 'enabled' => true, + 'with_constructor_extractor' => true, + ], +]); diff --git a/Tests/DependencyInjection/Fixtures/php/validation_auto_mapping.php b/Tests/DependencyInjection/Fixtures/php/validation_auto_mapping.php index ae5bea2ea..67bac4a32 100644 --- a/Tests/DependencyInjection/Fixtures/php/validation_auto_mapping.php +++ b/Tests/DependencyInjection/Fixtures/php/validation_auto_mapping.php @@ -5,7 +5,10 @@ 'http_method_override' => false, 'handle_all_throwables' => true, 'php_errors' => ['log' => true], - 'property_info' => ['enabled' => true], + 'property_info' => [ + 'enabled' => true, + 'with_constructor_extractor' => true, + ], 'validation' => [ 'email_validation_mode' => 'html5', 'auto_mapping' => [ diff --git a/Tests/DependencyInjection/Fixtures/xml/full.xml b/Tests/DependencyInjection/Fixtures/xml/full.xml index a3e5cfd88..23d325e61 100644 --- a/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -44,7 +44,7 @@ - + diff --git a/Tests/DependencyInjection/Fixtures/xml/property_info.xml b/Tests/DependencyInjection/Fixtures/xml/property_info.xml index 5f49aabaa..19bac44d9 100644 --- a/Tests/DependencyInjection/Fixtures/xml/property_info.xml +++ b/Tests/DependencyInjection/Fixtures/xml/property_info.xml @@ -8,6 +8,6 @@ - + diff --git a/Tests/DependencyInjection/Fixtures/xml/property_info_with_constructor_extractor.xml b/Tests/DependencyInjection/Fixtures/xml/property_info_with_constructor_extractor.xml new file mode 100644 index 000000000..df8dabe0b --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/property_info_with_constructor_extractor.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/Tests/DependencyInjection/Fixtures/xml/validation_auto_mapping.xml b/Tests/DependencyInjection/Fixtures/xml/validation_auto_mapping.xml index c60691b0b..966598091 100644 --- a/Tests/DependencyInjection/Fixtures/xml/validation_auto_mapping.xml +++ b/Tests/DependencyInjection/Fixtures/xml/validation_auto_mapping.xml @@ -6,7 +6,7 @@ - + foo diff --git a/Tests/DependencyInjection/Fixtures/yml/full.yml b/Tests/DependencyInjection/Fixtures/yml/full.yml index 8e272d11b..28c4336d9 100644 --- a/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -64,7 +64,8 @@ framework: default_context: enable_max_depth: false type_info: ~ - property_info: ~ + property_info: + with_constructor_extractor: true ide: file%%link%%format request: formats: diff --git a/Tests/DependencyInjection/Fixtures/yml/property_info.yml b/Tests/DependencyInjection/Fixtures/yml/property_info.yml index de05e6bb7..4fde73710 100644 --- a/Tests/DependencyInjection/Fixtures/yml/property_info.yml +++ b/Tests/DependencyInjection/Fixtures/yml/property_info.yml @@ -6,3 +6,4 @@ framework: log: true property_info: enabled: true + with_constructor_extractor: false diff --git a/Tests/DependencyInjection/Fixtures/yml/property_info_with_constructor_extractor.yml b/Tests/DependencyInjection/Fixtures/yml/property_info_with_constructor_extractor.yml new file mode 100644 index 000000000..a43762df3 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/property_info_with_constructor_extractor.yml @@ -0,0 +1,9 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + property_info: + enabled: true + with_constructor_extractor: true diff --git a/Tests/DependencyInjection/Fixtures/yml/validation_auto_mapping.yml b/Tests/DependencyInjection/Fixtures/yml/validation_auto_mapping.yml index 55a43886f..e81203e24 100644 --- a/Tests/DependencyInjection/Fixtures/yml/validation_auto_mapping.yml +++ b/Tests/DependencyInjection/Fixtures/yml/validation_auto_mapping.yml @@ -4,7 +4,9 @@ framework: handle_all_throwables: true php_errors: log: true - property_info: { enabled: true } + property_info: + enabled: true + with_constructor_extractor: true validation: email_validation_mode: html5 auto_mapping: diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 0446eb5d2..f5c93cefd 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -1676,6 +1676,14 @@ public function testPropertyInfoEnabled() { $container = $this->createContainerFromFile('property_info'); $this->assertTrue($container->has('property_info')); + $this->assertFalse($container->has('property_info.constructor_extractor')); + } + + public function testPropertyInfoWithConstructorExtractorEnabled() + { + $container = $this->createContainerFromFile('property_info_with_constructor_extractor'); + $this->assertTrue($container->has('property_info')); + $this->assertTrue($container->has('property_info.constructor_extractor')); } public function testPropertyInfoCacheActivated() diff --git a/Tests/Functional/app/ApiAttributesTest/config.yml b/Tests/Functional/app/ApiAttributesTest/config.yml index 8b218d48c..00bdd8ab9 100644 --- a/Tests/Functional/app/ApiAttributesTest/config.yml +++ b/Tests/Functional/app/ApiAttributesTest/config.yml @@ -5,4 +5,6 @@ framework: serializer: enabled: true validation: true - property_info: { enabled: true } + property_info: + enabled: true + with_constructor_extractor: true diff --git a/Tests/Functional/app/ContainerDump/config.yml b/Tests/Functional/app/ContainerDump/config.yml index 3efa5f950..48bff3240 100644 --- a/Tests/Functional/app/ContainerDump/config.yml +++ b/Tests/Functional/app/ContainerDump/config.yml @@ -15,6 +15,8 @@ framework: translator: true validation: true serializer: true - property_info: true + property_info: + enabled: true + with_constructor_extractor: true csrf_protection: true form: true diff --git a/Tests/Functional/app/Serializer/config.yml b/Tests/Functional/app/Serializer/config.yml index 2f20dab9e..3c0c35417 100644 --- a/Tests/Functional/app/Serializer/config.yml +++ b/Tests/Functional/app/Serializer/config.yml @@ -10,7 +10,9 @@ framework: max_depth_handler: Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serializer\MaxDepthHandler default_context: enable_max_depth: true - property_info: { enabled: true } + property_info: + enabled: true + with_constructor_extractor: true services: serializer.alias: From c20716a0ab4907729974a20c182a188a0c9b138c Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Mon, 23 Dec 2024 11:21:00 +0100 Subject: [PATCH 186/281] [JsonEncoder] Fix retrieving encodable classes --- DependencyInjection/Configuration.php | 10 ---------- DependencyInjection/FrameworkExtension.php | 9 --------- FrameworkBundle.php | 2 ++ Resources/config/json_encoder.php | 4 ++-- Resources/config/schema/symfony-1.0.xsd | 6 +----- Tests/DependencyInjection/ConfigurationTest.php | 1 - Tests/Functional/app/JsonEncoder/config.yml | 5 +---- 7 files changed, 6 insertions(+), 31 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 99592fe49..7f97199f7 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -2580,16 +2580,6 @@ private function addJsonEncoderSection(ArrayNodeDefinition $rootNode, callable $ ->arrayNode('json_encoder') ->info('JSON encoder configuration') ->{$enableIfStandalone('symfony/json-encoder', EncoderInterface::class)}() - ->fixXmlConfig('path') - ->children() - ->arrayNode('paths') - ->info('Namespaces and paths of encodable/decodable classes.') - ->normalizeKeys(false) - ->useAttributeAsKey('namespace') - ->scalarPrototype()->end() - ->defaultValue([]) - ->end() - ->end() ->end() ->end() ; diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 3116116f2..7b40e1cfa 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2026,15 +2026,6 @@ private function registerJsonEncoderConfiguration(array $config, ContainerBuilde $container->setParameter('.json_encoder.decoders_dir', '%kernel.cache_dir%/json_encoder/decoder'); $container->setParameter('.json_encoder.lazy_ghosts_dir', '%kernel.cache_dir%/json_encoder/lazy_ghost'); - $encodableDefinition = (new Definition()) - ->setAbstract(true) - ->addTag('container.excluded') - ->addTag('json_encoder.encodable'); - - foreach ($config['paths'] as $namespace => $path) { - $loader->registerClasses($encodableDefinition, $namespace, $path); - } - if (\PHP_VERSION_ID >= 80400) { $container->removeDefinition('.json_encoder.cache_warmer.lazy_ghost'); } diff --git a/FrameworkBundle.php b/FrameworkBundle.php index e83c4dfe6..843d3e00e 100644 --- a/FrameworkBundle.php +++ b/FrameworkBundle.php @@ -54,6 +54,7 @@ use Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass; use Symfony\Component\HttpKernel\DependencyInjection\ResettableServicePass; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\JsonEncoder\DependencyInjection\EncodablePass; use Symfony\Component\Messenger\DependencyInjection\MessengerPass; use Symfony\Component\Mime\DependencyInjection\AddMimeTypeGuesserPass; use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass; @@ -186,6 +187,7 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new ErrorLoggerCompilerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); $container->addCompilerPass(new VirtualRequestStackPass()); $container->addCompilerPass(new TranslationUpdateCommandPass(), PassConfig::TYPE_BEFORE_REMOVING); + $container->addCompilerPass(new EncodablePass()); if ($container->getParameter('kernel.debug')) { $container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 2); diff --git a/Resources/config/json_encoder.php b/Resources/config/json_encoder.php index 24f596fd4..67cb25d0a 100644 --- a/Resources/config/json_encoder.php +++ b/Resources/config/json_encoder.php @@ -107,7 +107,7 @@ // cache ->set('.json_encoder.cache_warmer.encoder_decoder', EncoderDecoderCacheWarmer::class) ->args([ - tagged_iterator('json_encoder.encodable'), + abstract_arg('encodable class names'), service('json_encoder.encode.property_metadata_loader'), service('json_encoder.decode.property_metadata_loader'), param('.json_encoder.encoders_dir'), @@ -118,7 +118,7 @@ ->set('.json_encoder.cache_warmer.lazy_ghost', LazyGhostCacheWarmer::class) ->args([ - tagged_iterator('json_encoder.encodable'), + abstract_arg('encodable class names'), param('.json_encoder.lazy_ghosts_dir'), ]) ->tag('kernel.cache_warmer') diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index 9cb89207d..ba26d85d1 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -1006,11 +1006,7 @@ - - - - - + diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index b4b8eb875..13f3d8e1d 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -973,7 +973,6 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor ], 'json_encoder' => [ 'enabled' => !class_exists(FullStack::class) && class_exists(JsonEncoder::class), - 'paths' => [], ], ]; } diff --git a/Tests/Functional/app/JsonEncoder/config.yml b/Tests/Functional/app/JsonEncoder/config.yml index 55fdf53f5..a92aa3969 100644 --- a/Tests/Functional/app/JsonEncoder/config.yml +++ b/Tests/Functional/app/JsonEncoder/config.yml @@ -4,10 +4,7 @@ imports: framework: http_method_override: false type_info: ~ - json_encoder: - enabled: true - paths: - Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\: '../../Tests/Functional/app/JsonEncoder/Dto/*' + json_encoder: ~ services: _defaults: From 1dea3d3896ab9cad87c2f2048ca6356a8a2f49c2 Mon Sep 17 00:00:00 2001 From: Florian Merle Date: Mon, 16 Dec 2024 15:20:00 +0100 Subject: [PATCH 187/281] [FrameworkBundle] Always display service arguments & deprecate `--show-arguments` option for `debug:container` --- CHANGELOG.md | 1 + Command/ContainerDebugCommand.php | 5 +- Console/Descriptor/JsonDescriptor.php | 25 ++++--- Console/Descriptor/MarkdownDescriptor.php | 7 +- Console/Descriptor/TextDescriptor.php | 3 +- Console/Descriptor/XmlDescriptor.php | 30 ++++----- .../Descriptor/AbstractDescriptorTestCase.php | 4 +- .../Descriptor/alias_with_definition_1.json | 65 +++++++++++++++++++ .../Descriptor/alias_with_definition_1.md | 1 + .../Descriptor/alias_with_definition_1.txt | 42 +++++++----- .../Descriptor/alias_with_definition_1.xml | 20 ++++++ .../Descriptor/alias_with_definition_2.json | 1 + .../Descriptor/alias_with_definition_2.md | 1 + .../Fixtures/Descriptor/builder_1_public.json | 63 ++++++++++++++++++ Tests/Fixtures/Descriptor/builder_1_public.md | 3 + .../Fixtures/Descriptor/builder_1_public.xml | 20 ++++++ .../Descriptor/builder_1_services.json | 2 + .../Fixtures/Descriptor/builder_1_services.md | 2 + Tests/Fixtures/Descriptor/builder_1_tag1.json | 1 + Tests/Fixtures/Descriptor/builder_1_tag1.md | 1 + Tests/Fixtures/Descriptor/builder_1_tags.json | 3 + Tests/Fixtures/Descriptor/builder_1_tags.md | 3 + .../Descriptor/builder_priority_tag.json | 4 ++ .../Descriptor/builder_priority_tag.md | 4 ++ Tests/Fixtures/Descriptor/definition_1.json | 61 +++++++++++++++++ Tests/Fixtures/Descriptor/definition_1.md | 1 + Tests/Fixtures/Descriptor/definition_1.txt | 43 +++++++----- Tests/Fixtures/Descriptor/definition_1.xml | 20 ++++++ Tests/Fixtures/Descriptor/definition_2.json | 1 + Tests/Fixtures/Descriptor/definition_2.md | 1 + Tests/Fixtures/Descriptor/definition_3.json | 1 + Tests/Fixtures/Descriptor/definition_3.md | 1 + .../Descriptor/definition_without_class.json | 1 + .../Descriptor/definition_without_class.md | 1 + .../Descriptor/existing_class_def_1.json | 1 + .../Descriptor/existing_class_def_1.md | 1 + .../Descriptor/existing_class_def_2.json | 1 + .../Descriptor/existing_class_def_2.md | 1 + .../Functional/ContainerDebugCommandTest.php | 18 +++++ 39 files changed, 389 insertions(+), 75 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02bfe6af4..97fa33a3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG * Rename `TranslationUpdateCommand` to `TranslationExtractCommand` * Add JsonEncoder services and configuration * Add new `framework.property_info.with_constructor_extractor` option to allow enabling or disabling the constructor extractor integration + * Deprecate the `--show-arguments` option of the `container:debug` command, as arguments are now always shown 7.2 --- diff --git a/Command/ContainerDebugCommand.php b/Command/ContainerDebugCommand.php index 46cdca9ab..ca3aacf73 100644 --- a/Command/ContainerDebugCommand.php +++ b/Command/ContainerDebugCommand.php @@ -151,6 +151,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int $tag = $this->findProperTagName($input, $errorIo, $object, $tag); $options = ['tag' => $tag]; } elseif ($name = $input->getArgument('name')) { + if ($input->getOption('show-arguments')) { + $errorIo->warning('The "--show-arguments" option is deprecated.'); + } + $name = $this->findProperServiceName($input, $errorIo, $object, $name, $input->getOption('show-hidden')); $options = ['id' => $name]; } elseif ($input->getOption('deprecations')) { @@ -161,7 +165,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $helper = new DescriptorHelper(); $options['format'] = $input->getOption('format'); - $options['show_arguments'] = $input->getOption('show-arguments'); $options['show_hidden'] = $input->getOption('show-hidden'); $options['raw_text'] = $input->getOption('raw'); $options['output'] = $io; diff --git a/Console/Descriptor/JsonDescriptor.php b/Console/Descriptor/JsonDescriptor.php index 5b83f0746..c7705a1a0 100644 --- a/Console/Descriptor/JsonDescriptor.php +++ b/Console/Descriptor/JsonDescriptor.php @@ -63,7 +63,7 @@ protected function describeContainerTags(ContainerBuilder $container, array $opt foreach ($this->findDefinitionsByTag($container, $showHidden) as $tag => $definitions) { $data[$tag] = []; foreach ($definitions as $definition) { - $data[$tag][] = $this->getContainerDefinitionData($definition, true, false, $container, $options['id'] ?? null); + $data[$tag][] = $this->getContainerDefinitionData($definition, true, $container, $options['id'] ?? null); } } @@ -79,7 +79,7 @@ protected function describeContainerService(object $service, array $options = [] if ($service instanceof Alias) { $this->describeContainerAlias($service, $options, $container); } elseif ($service instanceof Definition) { - $this->writeData($this->getContainerDefinitionData($service, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $container, $options['id']), $options); + $this->writeData($this->getContainerDefinitionData($service, isset($options['omit_tags']) && $options['omit_tags'], $container, $options['id']), $options); } else { $this->writeData($service::class, $options); } @@ -92,7 +92,6 @@ protected function describeContainerServices(ContainerBuilder $container, array : $this->sortServiceIds($container->getServiceIds()); $showHidden = isset($options['show_hidden']) && $options['show_hidden']; $omitTags = isset($options['omit_tags']) && $options['omit_tags']; - $showArguments = isset($options['show_arguments']) && $options['show_arguments']; $data = ['definitions' => [], 'aliases' => [], 'services' => []]; if (isset($options['filter'])) { @@ -112,7 +111,7 @@ protected function describeContainerServices(ContainerBuilder $container, array if ($service->hasTag('container.excluded')) { continue; } - $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $showArguments, $container, $serviceId); + $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $container, $serviceId); } else { $data['services'][$serviceId] = $service::class; } @@ -123,7 +122,7 @@ protected function describeContainerServices(ContainerBuilder $container, array protected function describeContainerDefinition(Definition $definition, array $options = [], ?ContainerBuilder $container = null): void { - $this->writeData($this->getContainerDefinitionData($definition, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $container, $options['id'] ?? null), $options); + $this->writeData($this->getContainerDefinitionData($definition, isset($options['omit_tags']) && $options['omit_tags'], $container, $options['id'] ?? null), $options); } protected function describeContainerAlias(Alias $alias, array $options = [], ?ContainerBuilder $container = null): void @@ -135,7 +134,7 @@ protected function describeContainerAlias(Alias $alias, array $options = [], ?Co } $this->writeData( - [$this->getContainerAliasData($alias), $this->getContainerDefinitionData($container->getDefinition((string) $alias), isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $container, (string) $alias)], + [$this->getContainerAliasData($alias), $this->getContainerDefinitionData($container->getDefinition((string) $alias), isset($options['omit_tags']) && $options['omit_tags'], $container, (string) $alias)], array_merge($options, ['id' => (string) $alias]) ); } @@ -245,7 +244,7 @@ protected function sortParameters(ParameterBag $parameters): array return $sortedParameters; } - private function getContainerDefinitionData(Definition $definition, bool $omitTags = false, bool $showArguments = false, ?ContainerBuilder $container = null, ?string $id = null): array + private function getContainerDefinitionData(Definition $definition, bool $omitTags = false, ?ContainerBuilder $container = null, ?string $id = null): array { $data = [ 'class' => (string) $definition->getClass(), @@ -269,9 +268,7 @@ private function getContainerDefinitionData(Definition $definition, bool $omitTa $data['description'] = $classDescription; } - if ($showArguments) { - $data['arguments'] = $this->describeValue($definition->getArguments(), $omitTags, $showArguments, $container, $id); - } + $data['arguments'] = $this->describeValue($definition->getArguments(), $omitTags, $container, $id); $data['file'] = $definition->getFile(); @@ -418,12 +415,12 @@ private function getCallableData(mixed $callable): array throw new \InvalidArgumentException('Callable is not describable.'); } - private function describeValue($value, bool $omitTags, bool $showArguments, ?ContainerBuilder $container = null, ?string $id = null): mixed + private function describeValue($value, bool $omitTags, ?ContainerBuilder $container = null, ?string $id = null): mixed { if (\is_array($value)) { $data = []; foreach ($value as $k => $v) { - $data[$k] = $this->describeValue($v, $omitTags, $showArguments, $container, $id); + $data[$k] = $this->describeValue($v, $omitTags, $container, $id); } return $data; @@ -445,11 +442,11 @@ private function describeValue($value, bool $omitTags, bool $showArguments, ?Con } if ($value instanceof ArgumentInterface) { - return $this->describeValue($value->getValues(), $omitTags, $showArguments, $container, $id); + return $this->describeValue($value->getValues(), $omitTags, $container, $id); } if ($value instanceof Definition) { - return $this->getContainerDefinitionData($value, $omitTags, $showArguments, $container, $id); + return $this->getContainerDefinitionData($value, $omitTags, $container, $id); } return $value; diff --git a/Console/Descriptor/MarkdownDescriptor.php b/Console/Descriptor/MarkdownDescriptor.php index 5203d14c3..d057c598d 100644 --- a/Console/Descriptor/MarkdownDescriptor.php +++ b/Console/Descriptor/MarkdownDescriptor.php @@ -155,7 +155,6 @@ protected function describeContainerServices(ContainerBuilder $container, array $serviceIds = isset($options['tag']) && $options['tag'] ? $this->sortTaggedServicesByPriority($container->findTaggedServiceIds($options['tag'])) : $this->sortServiceIds($container->getServiceIds()); - $showArguments = isset($options['show_arguments']) && $options['show_arguments']; $services = ['definitions' => [], 'aliases' => [], 'services' => []]; if (isset($options['filter'])) { @@ -185,7 +184,7 @@ protected function describeContainerServices(ContainerBuilder $container, array $this->write("\n\nDefinitions\n-----------\n"); foreach ($services['definitions'] as $id => $service) { $this->write("\n"); - $this->describeContainerDefinition($service, ['id' => $id, 'show_arguments' => $showArguments], $container); + $this->describeContainerDefinition($service, ['id' => $id], $container); } } @@ -231,9 +230,7 @@ protected function describeContainerDefinition(Definition $definition, array $op $output .= "\n".'- Deprecated: no'; } - if (isset($options['show_arguments']) && $options['show_arguments']) { - $output .= "\n".'- Arguments: '.($definition->getArguments() ? 'yes' : 'no'); - } + $output .= "\n".'- Arguments: '.($definition->getArguments() ? 'yes' : 'no'); if ($definition->getFile()) { $output .= "\n".'- File: `'.$definition->getFile().'`'; diff --git a/Console/Descriptor/TextDescriptor.php b/Console/Descriptor/TextDescriptor.php index 5efaab496..12b345411 100644 --- a/Console/Descriptor/TextDescriptor.php +++ b/Console/Descriptor/TextDescriptor.php @@ -351,9 +351,8 @@ protected function describeContainerDefinition(Definition $definition, array $op } } - $showArguments = isset($options['show_arguments']) && $options['show_arguments']; $argumentsInformation = []; - if ($showArguments && ($arguments = $definition->getArguments())) { + if ($arguments = $definition->getArguments()) { foreach ($arguments as $argument) { if ($argument instanceof ServiceClosureArgument) { $argument = $argument->getValues()[0]; diff --git a/Console/Descriptor/XmlDescriptor.php b/Console/Descriptor/XmlDescriptor.php index c41ac296f..8daa61d2a 100644 --- a/Console/Descriptor/XmlDescriptor.php +++ b/Console/Descriptor/XmlDescriptor.php @@ -59,17 +59,17 @@ protected function describeContainerService(object $service, array $options = [] throw new \InvalidArgumentException('An "id" option must be provided.'); } - $this->writeDocument($this->getContainerServiceDocument($service, $options['id'], $container, isset($options['show_arguments']) && $options['show_arguments'])); + $this->writeDocument($this->getContainerServiceDocument($service, $options['id'], $container)); } protected function describeContainerServices(ContainerBuilder $container, array $options = []): void { - $this->writeDocument($this->getContainerServicesDocument($container, $options['tag'] ?? null, isset($options['show_hidden']) && $options['show_hidden'], isset($options['show_arguments']) && $options['show_arguments'], $options['filter'] ?? null)); + $this->writeDocument($this->getContainerServicesDocument($container, $options['tag'] ?? null, isset($options['show_hidden']) && $options['show_hidden'], $options['filter'] ?? null)); } protected function describeContainerDefinition(Definition $definition, array $options = [], ?ContainerBuilder $container = null): void { - $this->writeDocument($this->getContainerDefinitionDocument($definition, $options['id'] ?? null, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $container)); + $this->writeDocument($this->getContainerDefinitionDocument($definition, $options['id'] ?? null, isset($options['omit_tags']) && $options['omit_tags'], $container)); } protected function describeContainerAlias(Alias $alias, array $options = [], ?ContainerBuilder $container = null): void @@ -83,7 +83,7 @@ protected function describeContainerAlias(Alias $alias, array $options = [], ?Co return; } - $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($container->getDefinition((string) $alias), (string) $alias, false, false, $container)->childNodes->item(0), true)); + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($container->getDefinition((string) $alias), (string) $alias, false, $container)->childNodes->item(0), true)); $this->writeDocument($dom); } @@ -260,7 +260,7 @@ private function getContainerTagsDocument(ContainerBuilder $container, bool $sho $tagXML->setAttribute('name', $tag); foreach ($definitions as $serviceId => $definition) { - $definitionXML = $this->getContainerDefinitionDocument($definition, $serviceId, true, false, $container); + $definitionXML = $this->getContainerDefinitionDocument($definition, $serviceId, true, $container); $tagXML->appendChild($dom->importNode($definitionXML->childNodes->item(0), true)); } } @@ -268,17 +268,17 @@ private function getContainerTagsDocument(ContainerBuilder $container, bool $sho return $dom; } - private function getContainerServiceDocument(object $service, string $id, ?ContainerBuilder $container = null, bool $showArguments = false): \DOMDocument + private function getContainerServiceDocument(object $service, string $id, ?ContainerBuilder $container = null): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); if ($service instanceof Alias) { $dom->appendChild($dom->importNode($this->getContainerAliasDocument($service, $id)->childNodes->item(0), true)); if ($container) { - $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($container->getDefinition((string) $service), (string) $service, false, $showArguments, $container)->childNodes->item(0), true)); + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($container->getDefinition((string) $service), (string) $service, false, $container)->childNodes->item(0), true)); } } elseif ($service instanceof Definition) { - $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($service, $id, false, $showArguments, $container)->childNodes->item(0), true)); + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($service, $id, false, $container)->childNodes->item(0), true)); } else { $dom->appendChild($serviceXML = $dom->createElement('service')); $serviceXML->setAttribute('id', $id); @@ -288,7 +288,7 @@ private function getContainerServiceDocument(object $service, string $id, ?Conta return $dom; } - private function getContainerServicesDocument(ContainerBuilder $container, ?string $tag = null, bool $showHidden = false, bool $showArguments = false, ?callable $filter = null): \DOMDocument + private function getContainerServicesDocument(ContainerBuilder $container, ?string $tag = null, bool $showHidden = false, ?callable $filter = null): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($containerXML = $dom->createElement('container')); @@ -311,14 +311,14 @@ private function getContainerServicesDocument(ContainerBuilder $container, ?stri continue; } - $serviceXML = $this->getContainerServiceDocument($service, $serviceId, null, $showArguments); + $serviceXML = $this->getContainerServiceDocument($service, $serviceId, null); $containerXML->appendChild($containerXML->ownerDocument->importNode($serviceXML->childNodes->item(0), true)); } return $dom; } - private function getContainerDefinitionDocument(Definition $definition, ?string $id = null, bool $omitTags = false, bool $showArguments = false, ?ContainerBuilder $container = null): \DOMDocument + private function getContainerDefinitionDocument(Definition $definition, ?string $id = null, bool $omitTags = false, ?ContainerBuilder $container = null): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($serviceXML = $dom->createElement('definition')); @@ -378,10 +378,8 @@ private function getContainerDefinitionDocument(Definition $definition, ?string } } - if ($showArguments) { - foreach ($this->getArgumentNodes($definition->getArguments(), $dom, $container) as $node) { - $serviceXML->appendChild($node); - } + foreach ($this->getArgumentNodes($definition->getArguments(), $dom, $container) as $node) { + $serviceXML->appendChild($node); } if (!$omitTags) { @@ -443,7 +441,7 @@ private function getArgumentNodes(array $arguments, \DOMDocument $dom, ?Containe $argumentXML->appendChild($childArgumentXML); } } elseif ($argument instanceof Definition) { - $argumentXML->appendChild($dom->importNode($this->getContainerDefinitionDocument($argument, null, false, true, $container)->childNodes->item(0), true)); + $argumentXML->appendChild($dom->importNode($this->getContainerDefinitionDocument($argument, null, false, $container)->childNodes->item(0), true)); } elseif ($argument instanceof AbstractArgument) { $argumentXML->setAttribute('type', 'abstract'); $argumentXML->appendChild(new \DOMText($argument->getText())); diff --git a/Tests/Console/Descriptor/AbstractDescriptorTestCase.php b/Tests/Console/Descriptor/AbstractDescriptorTestCase.php index dde1f000b..477bd1014 100644 --- a/Tests/Console/Descriptor/AbstractDescriptorTestCase.php +++ b/Tests/Console/Descriptor/AbstractDescriptorTestCase.php @@ -110,7 +110,7 @@ public static function getDescribeContainerDefinitionTestData(): array /** @dataProvider getDescribeContainerDefinitionWithArgumentsShownTestData */ public function testDescribeContainerDefinitionWithArgumentsShown(Definition $definition, $expectedDescription) { - $this->assertDescription($expectedDescription, $definition, ['show_arguments' => true]); + $this->assertDescription($expectedDescription, $definition, []); } public static function getDescribeContainerDefinitionWithArgumentsShownTestData(): array @@ -307,7 +307,7 @@ private static function getContainerBuilderDescriptionTestData(array $objects): 'public' => ['show_hidden' => false], 'tag1' => ['show_hidden' => true, 'tag' => 'tag1'], 'tags' => ['group_by' => 'tags', 'show_hidden' => true], - 'arguments' => ['show_hidden' => false, 'show_arguments' => true], + 'arguments' => ['show_hidden' => false], ]; $data = []; diff --git a/Tests/Fixtures/Descriptor/alias_with_definition_1.json b/Tests/Fixtures/Descriptor/alias_with_definition_1.json index a2c015faa..e4acc2a83 100644 --- a/Tests/Fixtures/Descriptor/alias_with_definition_1.json +++ b/Tests/Fixtures/Descriptor/alias_with_definition_1.json @@ -13,6 +13,71 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [ + { + "type": "service", + "id": ".definition_2" + }, + "%parameter%", + { + "class": "inline_service", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "deprecated": false, + "arguments": [ + "arg1", + "arg2" + ], + "file": null, + "tags": [], + "usages": [ + "alias_1" + ] + }, + [ + "foo", + { + "type": "service", + "id": ".definition_2" + }, + { + "class": "inline_service", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "deprecated": false, + "arguments": [], + "file": null, + "tags": [], + "usages": [ + "alias_1" + ] + } + ], + [ + { + "type": "service", + "id": "definition_1" + }, + { + "type": "service", + "id": ".definition_2" + } + ], + { + "type": "abstract", + "text": "placeholder" + } + ], "file": null, "factory_class": "Full\\Qualified\\FactoryClass", "factory_method": "get", diff --git a/Tests/Fixtures/Descriptor/alias_with_definition_1.md b/Tests/Fixtures/Descriptor/alias_with_definition_1.md index c92c8435f..fd94e43e9 100644 --- a/Tests/Fixtures/Descriptor/alias_with_definition_1.md +++ b/Tests/Fixtures/Descriptor/alias_with_definition_1.md @@ -14,6 +14,7 @@ - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: yes - Factory Class: `Full\Qualified\FactoryClass` - Factory Method: `get` - Usages: alias_1 diff --git a/Tests/Fixtures/Descriptor/alias_with_definition_1.txt b/Tests/Fixtures/Descriptor/alias_with_definition_1.txt index 7883d51c0..eea6c70b1 100644 --- a/Tests/Fixtures/Descriptor/alias_with_definition_1.txt +++ b/Tests/Fixtures/Descriptor/alias_with_definition_1.txt @@ -3,20 +3,28 @@ Information for Service "service_1" =================================== - ---------------- ----------------------------- -  Option   Value  - ---------------- ----------------------------- - Service ID service_1 - Class Full\Qualified\Class1 - Tags - - Public yes - Synthetic no - Lazy yes - Shared yes - Abstract yes - Autowired no - Autoconfigured no - Factory Class Full\Qualified\FactoryClass - Factory Method get - Usages alias_1 - ---------------- ----------------------------- + ---------------- --------------------------------- +  Option   Value  + ---------------- --------------------------------- + Service ID service_1 + Class Full\Qualified\Class1 + Tags - + Public yes + Synthetic no + Lazy yes + Shared yes + Abstract yes + Autowired no + Autoconfigured no + Factory Class Full\Qualified\FactoryClass + Factory Method get + Arguments Service(.definition_2) + %parameter% + Inlined Service + Array (3 element(s)) + Iterator (2 element(s)) + - Service(definition_1) + - Service(.definition_2) + Abstract argument (placeholder) + Usages alias_1 + ---------------- --------------------------------- diff --git a/Tests/Fixtures/Descriptor/alias_with_definition_1.xml b/Tests/Fixtures/Descriptor/alias_with_definition_1.xml index 06c8406da..3eab915ab 100644 --- a/Tests/Fixtures/Descriptor/alias_with_definition_1.xml +++ b/Tests/Fixtures/Descriptor/alias_with_definition_1.xml @@ -2,6 +2,26 @@ + + %parameter% + + + arg1 + arg2 + + + + foo + + + + + + + + + + placeholder alias_1 diff --git a/Tests/Fixtures/Descriptor/alias_with_definition_2.json b/Tests/Fixtures/Descriptor/alias_with_definition_2.json index f3b930983..e59ff8524 100644 --- a/Tests/Fixtures/Descriptor/alias_with_definition_2.json +++ b/Tests/Fixtures/Descriptor/alias_with_definition_2.json @@ -13,6 +13,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", diff --git a/Tests/Fixtures/Descriptor/alias_with_definition_2.md b/Tests/Fixtures/Descriptor/alias_with_definition_2.md index 3ec9516a3..045da01b0 100644 --- a/Tests/Fixtures/Descriptor/alias_with_definition_2.md +++ b/Tests/Fixtures/Descriptor/alias_with_definition_2.md @@ -14,6 +14,7 @@ - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` diff --git a/Tests/Fixtures/Descriptor/builder_1_public.json b/Tests/Fixtures/Descriptor/builder_1_public.json index 0d6198b07..28d64c611 100644 --- a/Tests/Fixtures/Descriptor/builder_1_public.json +++ b/Tests/Fixtures/Descriptor/builder_1_public.json @@ -10,6 +10,67 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [ + { + "type": "service", + "id": ".definition_2" + }, + "%parameter%", + { + "class": "inline_service", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "deprecated": false, + "arguments": [ + "arg1", + "arg2" + ], + "file": null, + "tags": [], + "usages": [] + }, + [ + "foo", + { + "type": "service", + "id": ".definition_2" + }, + { + "class": "inline_service", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "deprecated": false, + "arguments": [], + "file": null, + "tags": [], + "usages": [] + } + ], + [ + { + "type": "service", + "id": "definition_1" + }, + { + "type": "service", + "id": ".definition_2" + } + ], + { + "type": "abstract", + "text": "placeholder" + } + ], "file": null, "factory_class": "Full\\Qualified\\FactoryClass", "factory_method": "get", @@ -26,6 +87,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": null, "tags": [], "usages": [] @@ -41,6 +103,7 @@ "autoconfigure": false, "deprecated": false, "description": "ContainerInterface is the interface implemented by service container classes.", + "arguments": [], "file": null, "tags": [], "usages": [] diff --git a/Tests/Fixtures/Descriptor/builder_1_public.md b/Tests/Fixtures/Descriptor/builder_1_public.md index 2532a2c4e..57a209ecb 100644 --- a/Tests/Fixtures/Descriptor/builder_1_public.md +++ b/Tests/Fixtures/Descriptor/builder_1_public.md @@ -15,6 +15,7 @@ Definitions - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: yes - Factory Class: `Full\Qualified\FactoryClass` - Factory Method: `get` - Usages: none @@ -30,6 +31,7 @@ Definitions - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - Usages: none ### service_container @@ -44,6 +46,7 @@ Definitions - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - Usages: none diff --git a/Tests/Fixtures/Descriptor/builder_1_public.xml b/Tests/Fixtures/Descriptor/builder_1_public.xml index 3b13b7264..fdddad653 100644 --- a/Tests/Fixtures/Descriptor/builder_1_public.xml +++ b/Tests/Fixtures/Descriptor/builder_1_public.xml @@ -3,6 +3,26 @@ + + %parameter% + + + arg1 + arg2 + + + + foo + + + + + + + + + + placeholder diff --git a/Tests/Fixtures/Descriptor/builder_1_services.json b/Tests/Fixtures/Descriptor/builder_1_services.json index ac6d122ce..473709247 100644 --- a/Tests/Fixtures/Descriptor/builder_1_services.json +++ b/Tests/Fixtures/Descriptor/builder_1_services.json @@ -10,6 +10,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", @@ -65,6 +66,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "factory_service": "inline factory service (Full\\Qualified\\FactoryClass)", "factory_method": "get", diff --git a/Tests/Fixtures/Descriptor/builder_1_services.md b/Tests/Fixtures/Descriptor/builder_1_services.md index 6dfab327d..64801e03b 100644 --- a/Tests/Fixtures/Descriptor/builder_1_services.md +++ b/Tests/Fixtures/Descriptor/builder_1_services.md @@ -15,6 +15,7 @@ Definitions - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` @@ -40,6 +41,7 @@ Definitions - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Factory Service: inline factory service (`Full\Qualified\FactoryClass`) - Factory Method: `get` diff --git a/Tests/Fixtures/Descriptor/builder_1_tag1.json b/Tests/Fixtures/Descriptor/builder_1_tag1.json index 5e60f26d1..cead51aa9 100644 --- a/Tests/Fixtures/Descriptor/builder_1_tag1.json +++ b/Tests/Fixtures/Descriptor/builder_1_tag1.json @@ -10,6 +10,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", diff --git a/Tests/Fixtures/Descriptor/builder_1_tag1.md b/Tests/Fixtures/Descriptor/builder_1_tag1.md index aeae0d9f2..8e9229349 100644 --- a/Tests/Fixtures/Descriptor/builder_1_tag1.md +++ b/Tests/Fixtures/Descriptor/builder_1_tag1.md @@ -15,6 +15,7 @@ Definitions - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` diff --git a/Tests/Fixtures/Descriptor/builder_1_tags.json b/Tests/Fixtures/Descriptor/builder_1_tags.json index 518f694ea..6775a0e36 100644 --- a/Tests/Fixtures/Descriptor/builder_1_tags.json +++ b/Tests/Fixtures/Descriptor/builder_1_tags.json @@ -10,6 +10,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", @@ -30,6 +31,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", @@ -50,6 +52,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", diff --git a/Tests/Fixtures/Descriptor/builder_1_tags.md b/Tests/Fixtures/Descriptor/builder_1_tags.md index 80da2ddaf..cc0496e28 100644 --- a/Tests/Fixtures/Descriptor/builder_1_tags.md +++ b/Tests/Fixtures/Descriptor/builder_1_tags.md @@ -15,6 +15,7 @@ tag1 - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` @@ -36,6 +37,7 @@ tag2 - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` @@ -57,6 +59,7 @@ tag3 - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` diff --git a/Tests/Fixtures/Descriptor/builder_priority_tag.json b/Tests/Fixtures/Descriptor/builder_priority_tag.json index 75d893297..d9c3d050c 100644 --- a/Tests/Fixtures/Descriptor/builder_priority_tag.json +++ b/Tests/Fixtures/Descriptor/builder_priority_tag.json @@ -10,6 +10,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "tags": [ { @@ -40,6 +41,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", @@ -77,6 +79,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "tags": [ { @@ -98,6 +101,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "tags": [ { diff --git a/Tests/Fixtures/Descriptor/builder_priority_tag.md b/Tests/Fixtures/Descriptor/builder_priority_tag.md index 7137e1b1d..90ef56ee4 100644 --- a/Tests/Fixtures/Descriptor/builder_priority_tag.md +++ b/Tests/Fixtures/Descriptor/builder_priority_tag.md @@ -15,6 +15,7 @@ Definitions - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Tag: `tag1` - Attr3: val3 @@ -36,6 +37,7 @@ Definitions - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` @@ -59,6 +61,7 @@ Definitions - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Tag: `tag1` - Priority: 0 @@ -75,6 +78,7 @@ Definitions - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Tag: `tag1` - Attr1: val1 diff --git a/Tests/Fixtures/Descriptor/definition_1.json b/Tests/Fixtures/Descriptor/definition_1.json index 735b3df47..b0a612030 100644 --- a/Tests/Fixtures/Descriptor/definition_1.json +++ b/Tests/Fixtures/Descriptor/definition_1.json @@ -8,6 +8,67 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [ + { + "type": "service", + "id": ".definition_2" + }, + "%parameter%", + { + "class": "inline_service", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "deprecated": false, + "arguments": [ + "arg1", + "arg2" + ], + "file": null, + "tags": [], + "usages": [] + }, + [ + "foo", + { + "type": "service", + "id": ".definition_2" + }, + { + "class": "inline_service", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "deprecated": false, + "arguments": [], + "file": null, + "tags": [], + "usages": [] + } + ], + [ + { + "type": "service", + "id": "definition_1" + }, + { + "type": "service", + "id": ".definition_2" + } + ], + { + "type": "abstract", + "text": "placeholder" + } + ], "file": null, "factory_class": "Full\\Qualified\\FactoryClass", "factory_method": "get", diff --git a/Tests/Fixtures/Descriptor/definition_1.md b/Tests/Fixtures/Descriptor/definition_1.md index c7ad62954..b99162bbf 100644 --- a/Tests/Fixtures/Descriptor/definition_1.md +++ b/Tests/Fixtures/Descriptor/definition_1.md @@ -7,6 +7,7 @@ - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: yes - Factory Class: `Full\Qualified\FactoryClass` - Factory Method: `get` - Usages: none diff --git a/Tests/Fixtures/Descriptor/definition_1.txt b/Tests/Fixtures/Descriptor/definition_1.txt index 8ec7be868..775a04c84 100644 --- a/Tests/Fixtures/Descriptor/definition_1.txt +++ b/Tests/Fixtures/Descriptor/definition_1.txt @@ -1,18 +1,25 @@ - ---------------- ----------------------------- -  Option   Value  - ---------------- ----------------------------- - Service ID - - Class Full\Qualified\Class1 - Tags - - Public yes - Synthetic no - Lazy yes - Shared yes - Abstract yes - Autowired no - Autoconfigured no - Factory Class Full\Qualified\FactoryClass - Factory Method get - Usages none - ---------------- ----------------------------- - + ---------------- --------------------------------- +  Option   Value  + ---------------- --------------------------------- + Service ID - + Class Full\Qualified\Class1 + Tags - + Public yes + Synthetic no + Lazy yes + Shared yes + Abstract yes + Autowired no + Autoconfigured no + Factory Class Full\Qualified\FactoryClass + Factory Method get + Arguments Service(.definition_2) + %parameter% + Inlined Service + Array (3 element(s)) + Iterator (2 element(s)) + - Service(definition_1) + - Service(.definition_2) + Abstract argument (placeholder) + Usages none + ---------------- --------------------------------- diff --git a/Tests/Fixtures/Descriptor/definition_1.xml b/Tests/Fixtures/Descriptor/definition_1.xml index be2b16b57..eba7e7bbd 100644 --- a/Tests/Fixtures/Descriptor/definition_1.xml +++ b/Tests/Fixtures/Descriptor/definition_1.xml @@ -1,4 +1,24 @@ + + %parameter% + + + arg1 + arg2 + + + + foo + + + + + + + + + + placeholder diff --git a/Tests/Fixtures/Descriptor/definition_2.json b/Tests/Fixtures/Descriptor/definition_2.json index a661428c9..eeeb6f44a 100644 --- a/Tests/Fixtures/Descriptor/definition_2.json +++ b/Tests/Fixtures/Descriptor/definition_2.json @@ -8,6 +8,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "factory_service": "factory.service", "factory_method": "get", diff --git a/Tests/Fixtures/Descriptor/definition_2.md b/Tests/Fixtures/Descriptor/definition_2.md index 486f35fb7..5b427bff5 100644 --- a/Tests/Fixtures/Descriptor/definition_2.md +++ b/Tests/Fixtures/Descriptor/definition_2.md @@ -7,6 +7,7 @@ - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Factory Service: `factory.service` - Factory Method: `get` diff --git a/Tests/Fixtures/Descriptor/definition_3.json b/Tests/Fixtures/Descriptor/definition_3.json index 11768d0de..c96c06d63 100644 --- a/Tests/Fixtures/Descriptor/definition_3.json +++ b/Tests/Fixtures/Descriptor/definition_3.json @@ -8,6 +8,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": "\/path\/to\/file", "factory_service": "inline factory service (Full\\Qualified\\FactoryClass)", "factory_method": "get", diff --git a/Tests/Fixtures/Descriptor/definition_3.md b/Tests/Fixtures/Descriptor/definition_3.md index 8a9651641..5bfafe3d0 100644 --- a/Tests/Fixtures/Descriptor/definition_3.md +++ b/Tests/Fixtures/Descriptor/definition_3.md @@ -7,6 +7,7 @@ - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - File: `/path/to/file` - Factory Service: inline factory service (`Full\Qualified\FactoryClass`) - Factory Method: `get` diff --git a/Tests/Fixtures/Descriptor/definition_without_class.json b/Tests/Fixtures/Descriptor/definition_without_class.json index 078f7cdca..c1305ac0c 100644 --- a/Tests/Fixtures/Descriptor/definition_without_class.json +++ b/Tests/Fixtures/Descriptor/definition_without_class.json @@ -8,6 +8,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": null, "tags": [], "usages": [] diff --git a/Tests/Fixtures/Descriptor/definition_without_class.md b/Tests/Fixtures/Descriptor/definition_without_class.md index be221535f..7c7bad74d 100644 --- a/Tests/Fixtures/Descriptor/definition_without_class.md +++ b/Tests/Fixtures/Descriptor/definition_without_class.md @@ -7,4 +7,5 @@ - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - Usages: none diff --git a/Tests/Fixtures/Descriptor/existing_class_def_1.json b/Tests/Fixtures/Descriptor/existing_class_def_1.json index c6de89ce5..00c8a5be0 100644 --- a/Tests/Fixtures/Descriptor/existing_class_def_1.json +++ b/Tests/Fixtures/Descriptor/existing_class_def_1.json @@ -9,6 +9,7 @@ "autoconfigure": false, "deprecated": false, "description": "This is a class with a doc comment.", + "arguments": [], "file": null, "tags": [], "usages": [] diff --git a/Tests/Fixtures/Descriptor/existing_class_def_1.md b/Tests/Fixtures/Descriptor/existing_class_def_1.md index 132147324..907f69460 100644 --- a/Tests/Fixtures/Descriptor/existing_class_def_1.md +++ b/Tests/Fixtures/Descriptor/existing_class_def_1.md @@ -8,4 +8,5 @@ - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - Usages: none diff --git a/Tests/Fixtures/Descriptor/existing_class_def_2.json b/Tests/Fixtures/Descriptor/existing_class_def_2.json index 7b387fd86..88a59851a 100644 --- a/Tests/Fixtures/Descriptor/existing_class_def_2.json +++ b/Tests/Fixtures/Descriptor/existing_class_def_2.json @@ -8,6 +8,7 @@ "autowire": false, "autoconfigure": false, "deprecated": false, + "arguments": [], "file": null, "tags": [], "usages": [] diff --git a/Tests/Fixtures/Descriptor/existing_class_def_2.md b/Tests/Fixtures/Descriptor/existing_class_def_2.md index 0526ba117..8fd89fb0f 100644 --- a/Tests/Fixtures/Descriptor/existing_class_def_2.md +++ b/Tests/Fixtures/Descriptor/existing_class_def_2.md @@ -7,4 +7,5 @@ - Autowired: no - Autoconfigured: no - Deprecated: no +- Arguments: no - Usages: none diff --git a/Tests/Functional/ContainerDebugCommandTest.php b/Tests/Functional/ContainerDebugCommandTest.php index bb80a4484..b5d395a23 100644 --- a/Tests/Functional/ContainerDebugCommandTest.php +++ b/Tests/Functional/ContainerDebugCommandTest.php @@ -341,4 +341,22 @@ public static function provideCompletionSuggestions(): iterable ['txt', 'xml', 'json', 'md'], ]; } + + public function testShowArgumentsNotProvidedShouldTriggerDeprecation() + { + static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => true]); + $path = sprintf('%s/%sDeprecations.log', static::$kernel->getContainer()->getParameter('kernel.build_dir'), static::$kernel->getContainer()->getParameter('kernel.container_class')); + @unlink($path); + + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + @unlink(static::getContainer()->getParameter('debug.container.dump')); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'debug:container', 'name' => 'router', '--show-arguments' => true]); + + $tester->assertCommandIsSuccessful(); + $this->assertStringContainsString('[WARNING] The "--show-arguments" option is deprecated.', $tester->getDisplay()); + } } From 27fee457545372bb298d36ce6d1d84079bf1751a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 7 Jan 2025 14:24:39 +0100 Subject: [PATCH 188/281] add compiler pass only if it exists --- FrameworkBundle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FrameworkBundle.php b/FrameworkBundle.php index c16f9b9c1..89d9744f5 100644 --- a/FrameworkBundle.php +++ b/FrameworkBundle.php @@ -189,7 +189,7 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new ErrorLoggerCompilerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); $container->addCompilerPass(new VirtualRequestStackPass()); $container->addCompilerPass(new TranslationUpdateCommandPass(), PassConfig::TYPE_BEFORE_REMOVING); - $container->addCompilerPass(new EncodablePass()); + $this->addCompilerPassIfExists($container, EncodablePass::class); if ($container->getParameter('kernel.debug')) { $container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 2); From 9e4794e73f2a2eb8230ff08bc3bc6926921945dd Mon Sep 17 00:00:00 2001 From: HypeMC Date: Tue, 7 Jan 2025 15:57:51 +0100 Subject: [PATCH 189/281] [PropertyAccess] Move dependency definitions outside of Extension --- DependencyInjection/FrameworkExtension.php | 4 ---- Resources/config/property_access.php | 6 ++++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 79c876161..5a86acb54 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -143,9 +143,7 @@ use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface; use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; -use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; -use Symfony\Component\PropertyInfo\PropertyWriteInfoExtractorInterface; use Symfony\Component\RateLimiter\LimiterInterface; use Symfony\Component\RateLimiter\RateLimiterFactory; use Symfony\Component\RateLimiter\Storage\CacheStorage; @@ -1810,8 +1808,6 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui ->getDefinition('property_accessor') ->replaceArgument(0, $magicMethods) ->replaceArgument(1, $throw) - ->replaceArgument(3, new Reference(PropertyReadInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE)) - ->replaceArgument(4, new Reference(PropertyWriteInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE)) ; } diff --git a/Resources/config/property_access.php b/Resources/config/property_access.php index 85ab9f18e..4c9feb660 100644 --- a/Resources/config/property_access.php +++ b/Resources/config/property_access.php @@ -13,6 +13,8 @@ use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyWriteInfoExtractorInterface; return static function (ContainerConfigurator $container) { $container->services() @@ -21,8 +23,8 @@ abstract_arg('magic methods allowed, set by the extension'), abstract_arg('throw exceptions, set by the extension'), service('cache.property_access')->ignoreOnInvalid(), - abstract_arg('propertyReadInfoExtractor, set by the extension'), - abstract_arg('propertyWriteInfoExtractor, set by the extension'), + service(PropertyReadInfoExtractorInterface::class)->nullOnInvalid(), + service(PropertyWriteInfoExtractorInterface::class)->nullOnInvalid(), ]) ->alias(PropertyAccessorInterface::class, 'property_accessor') From 9c85df68bfc4b8b37635a772498b75c36ee84230 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 8 Jan 2025 08:31:12 +0100 Subject: [PATCH 190/281] rename test to match what's actually tested --- Tests/Functional/ContainerDebugCommandTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Functional/ContainerDebugCommandTest.php b/Tests/Functional/ContainerDebugCommandTest.php index b5d395a23..1037074c1 100644 --- a/Tests/Functional/ContainerDebugCommandTest.php +++ b/Tests/Functional/ContainerDebugCommandTest.php @@ -342,10 +342,10 @@ public static function provideCompletionSuggestions(): iterable ]; } - public function testShowArgumentsNotProvidedShouldTriggerDeprecation() + public function testShowArgumentsProvidedShouldTriggerDeprecation() { static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => true]); - $path = sprintf('%s/%sDeprecations.log', static::$kernel->getContainer()->getParameter('kernel.build_dir'), static::$kernel->getContainer()->getParameter('kernel.container_class')); + $path = \sprintf('%s/%sDeprecations.log', static::$kernel->getContainer()->getParameter('kernel.build_dir'), static::$kernel->getContainer()->getParameter('kernel.container_class')); @unlink($path); $application = new Application(static::$kernel); From f2c4d746119824062a2252675cacca8b8f0e2e18 Mon Sep 17 00:00:00 2001 From: Faizan Akram Date: Sun, 8 Dec 2024 10:46:43 +0100 Subject: [PATCH 191/281] fix(dependency-injection): reset env vars with kernel.reset - fixes #59128 --- Resources/config/services.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/config/services.php b/Resources/config/services.php index c85ccf5d0..a7e1c5198 100644 --- a/Resources/config/services.php +++ b/Resources/config/services.php @@ -195,6 +195,7 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : [] tagged_iterator('container.env_var_loader'), ]) ->tag('container.env_var_processor') + ->tag('kernel.reset', ['method' => 'reset']) ->set('slugger', AsciiSlugger::class) ->args([ From b6d4072a132dd50f57307a3598a18f8c7599646c Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Tue, 31 Dec 2024 13:49:42 -0500 Subject: [PATCH 192/281] Add support for invokable commands and input attributes --- DependencyInjection/FrameworkExtension.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 5a86acb54..38d37c7fc 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -49,6 +49,7 @@ use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\ResourceCheckerInterface; use Symfony\Component\Console\Application; +use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\DataCollector\CommandDataCollector; use Symfony\Component\Console\Debug\CliRequest; @@ -608,6 +609,9 @@ public function load(array $configs, ContainerBuilder $container): void ->addTag('assets.package'); $container->registerForAutoconfiguration(AssetCompilerInterface::class) ->addTag('asset_mapper.compiler'); + $container->registerAttributeForAutoconfiguration(AsCommand::class, static function (ChildDefinition $definition, AsCommand $attribute, \ReflectionClass $reflector): void { + $definition->addTag('console.command', ['command' => $attribute->name, 'description' => $attribute->description ?? $reflector->getName()]); + }); $container->registerForAutoconfiguration(Command::class) ->addTag('console.command'); $container->registerForAutoconfiguration(ResourceCheckerInterface::class) From d76bcc720ef65fe22781fa2cd4c9b5a1f7e8a829 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Fri, 10 Jan 2025 15:17:09 +0100 Subject: [PATCH 193/281] chore: PHP CS Fixer fixes --- Command/TranslationExtractCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Command/TranslationExtractCommand.php b/Command/TranslationExtractCommand.php index c794b20d6..d7967bbe8 100644 --- a/Command/TranslationExtractCommand.php +++ b/Command/TranslationExtractCommand.php @@ -62,7 +62,7 @@ public function __construct( parent::__construct(); if (!method_exists($writer, 'getFormats')) { - throw new \InvalidArgumentException(sprintf('The writer class "%s" does not implement the "getFormats()" method.', $writer::class)); + throw new \InvalidArgumentException(\sprintf('The writer class "%s" does not implement the "getFormats()" method.', $writer::class)); } } From 271adae1e05979f3fdf516a883326b4ef6799f21 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Tue, 7 Jan 2025 15:21:25 +0100 Subject: [PATCH 194/281] [JsonEncoder] Add `JsonEncodable` attribute --- .../Compiler/UnusedTagsPass.php | 1 - DependencyInjection/FrameworkExtension.php | 5 ++++ Tests/Functional/JsonEncoderTest.php | 26 ++++++++++++++++--- .../Functional/app/JsonEncoder/Dto/Dummy.php | 2 ++ Tests/Functional/app/JsonEncoder/config.yml | 5 ++++ 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index a2a571f83..b6a4c8a7c 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -54,7 +54,6 @@ class UnusedTagsPass implements CompilerPassInterface 'html_sanitizer', 'http_client.client', 'json_encoder.denormalizer', - 'json_encoder.encodable', 'json_encoder.normalizer', 'kernel.cache_clearer', 'kernel.cache_warmer', diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 38d37c7fc..6d20ca653 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -100,6 +100,7 @@ use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator; +use Symfony\Component\JsonEncoder\Attribute\JsonEncodable; use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface as JsonEncoderDenormalizerInterface; use Symfony\Component\JsonEncoder\DecoderInterface as JsonEncoderDecoderInterface; use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface as JsonEncoderNormalizerInterface; @@ -745,6 +746,10 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu } ); } + $container->registerAttributeForAutoconfiguration(JsonEncodable::class, static function (ChildDefinition $definition): void { + $definition->addTag('json_encoder.encodable'); + $definition->addTag('container.excluded'); + }); if (!$container->getParameter('kernel.debug')) { // remove tagged iterator argument for resource checkers diff --git a/Tests/Functional/JsonEncoderTest.php b/Tests/Functional/JsonEncoderTest.php index 0ab66e6c1..93ca1fd6d 100644 --- a/Tests/Functional/JsonEncoderTest.php +++ b/Tests/Functional/JsonEncoderTest.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\Dummy; +use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\JsonEncoder\DecoderInterface; use Symfony\Component\JsonEncoder\EncoderInterface; use Symfony\Component\TypeInfo\Type; @@ -21,10 +22,13 @@ */ class JsonEncoderTest extends AbstractWebTestCase { - public function testEncode() + protected function setUp(): void { static::bootKernel(['test_case' => 'JsonEncoder']); + } + public function testEncode() + { /** @var EncoderInterface $encoder */ $encoder = static::getContainer()->get('json_encoder.encoder.alias'); @@ -33,8 +37,6 @@ public function testEncode() public function testDecode() { - static::bootKernel(['test_case' => 'JsonEncoder']); - /** @var DecoderInterface $decoder */ $decoder = static::getContainer()->get('json_encoder.decoder.alias'); @@ -44,4 +46,22 @@ public function testDecode() $this->assertEquals($expected, $decoder->decode('{"@name": "DUMMY", "range": "0..1"}', Type::object(Dummy::class))); } + + public function testWarmupEncodableClasses() + { + /** @var Filesystem $fs */ + $fs = static::getContainer()->get('filesystem'); + + $encodersDir = \sprintf('%s/json_encoder/encoder/', static::getContainer()->getParameter('kernel.cache_dir')); + + // clear already created encoders + if ($fs->exists($encodersDir)) { + $fs->remove($encodersDir); + } + + static::getContainer()->get('json_encoder.cache_warmer.encoder_decoder.alias')->warmUp(static::getContainer()->getParameter('kernel.cache_dir')); + + $this->assertFileExists($encodersDir); + $this->assertCount(1, glob($encodersDir.'/*')); + } } diff --git a/Tests/Functional/app/JsonEncoder/Dto/Dummy.php b/Tests/Functional/app/JsonEncoder/Dto/Dummy.php index 344b9d11c..8610de049 100644 --- a/Tests/Functional/app/JsonEncoder/Dto/Dummy.php +++ b/Tests/Functional/app/JsonEncoder/Dto/Dummy.php @@ -14,11 +14,13 @@ use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer; use Symfony\Component\JsonEncoder\Attribute\Denormalizer; use Symfony\Component\JsonEncoder\Attribute\EncodedName; +use Symfony\Component\JsonEncoder\Attribute\JsonEncodable; use Symfony\Component\JsonEncoder\Attribute\Normalizer; /** * @author Mathias Arlaud */ +#[JsonEncodable] class Dummy { #[EncodedName('@name')] diff --git a/Tests/Functional/app/JsonEncoder/config.yml b/Tests/Functional/app/JsonEncoder/config.yml index a92aa3969..13b68adef 100644 --- a/Tests/Functional/app/JsonEncoder/config.yml +++ b/Tests/Functional/app/JsonEncoder/config.yml @@ -18,4 +18,9 @@ services: alias: json_encoder.decoder public: true + json_encoder.cache_warmer.encoder_decoder.alias: + alias: .json_encoder.cache_warmer.encoder_decoder + public: true + + Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\Dummy: ~ Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer: ~ From 2185b8c3659337c012699f0aae47c33e031c15d7 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Mon, 13 Jan 2025 23:30:21 +0100 Subject: [PATCH 195/281] [PropertyInfo] Move aliases under service definition --- Resources/config/property_info.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/config/property_info.php b/Resources/config/property_info.php index f45d6ce2b..505dda6f4 100644 --- a/Resources/config/property_info.php +++ b/Resources/config/property_info.php @@ -48,11 +48,11 @@ ->tag('property_info.access_extractor', ['priority' => -1000]) ->tag('property_info.initializable_extractor', ['priority' => -1000]) + ->alias(PropertyReadInfoExtractorInterface::class, 'property_info.reflection_extractor') + ->alias(PropertyWriteInfoExtractorInterface::class, 'property_info.reflection_extractor') + ->set('property_info.constructor_extractor', ConstructorExtractor::class) ->args([[]]) ->tag('property_info.type_extractor', ['priority' => -999]) - - ->alias(PropertyReadInfoExtractorInterface::class, 'property_info.reflection_extractor') - ->alias(PropertyWriteInfoExtractorInterface::class, 'property_info.reflection_extractor') ; }; From 80ee7565588d8c8ef7bbc60276c4643a0ff33c31 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 20 Nov 2024 11:25:30 +0100 Subject: [PATCH 196/281] [RateLimiter] Add `RateLimiterFactoryInterface` --- CHANGELOG.md | 1 + Resources/config/rate_limiter.php | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97fa33a3c..9c9aecbd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ CHANGELOG * Add JsonEncoder services and configuration * Add new `framework.property_info.with_constructor_extractor` option to allow enabling or disabling the constructor extractor integration * Deprecate the `--show-arguments` option of the `container:debug` command, as arguments are now always shown + * Add `RateLimiterFactoryInterface` as an alias of the `limiter` service 7.2 --- diff --git a/Resources/config/rate_limiter.php b/Resources/config/rate_limiter.php index 727a1f636..90af4d758 100644 --- a/Resources/config/rate_limiter.php +++ b/Resources/config/rate_limiter.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Component\RateLimiter\RateLimiterFactory; +use Symfony\Component\RateLimiter\RateLimiterFactoryInterface; return static function (ContainerConfigurator $container) { $container->services() @@ -27,4 +28,9 @@ null, ]) ; + + if (interface_exists(RateLimiterFactoryInterface::class)) { + $container->services() + ->alias(RateLimiterFactoryInterface::class, 'limiter'); + } }; From d9fb99fc9c0e1837167d997ef1821dc9d0086e28 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Mon, 13 Jan 2025 10:35:32 -0500 Subject: [PATCH 197/281] [Console] Invokable command adjustments --- DependencyInjection/FrameworkExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 38d37c7fc..deb3d6957 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -610,7 +610,7 @@ public function load(array $configs, ContainerBuilder $container): void $container->registerForAutoconfiguration(AssetCompilerInterface::class) ->addTag('asset_mapper.compiler'); $container->registerAttributeForAutoconfiguration(AsCommand::class, static function (ChildDefinition $definition, AsCommand $attribute, \ReflectionClass $reflector): void { - $definition->addTag('console.command', ['command' => $attribute->name, 'description' => $attribute->description ?? $reflector->getName()]); + $definition->addTag('console.command', ['command' => $attribute->name, 'description' => $attribute->description]); }); $container->registerForAutoconfiguration(Command::class) ->addTag('console.command'); From 1d0686b29824e72d964cd4d6ebae9088756c053c Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Fri, 10 Jan 2025 15:50:54 -0500 Subject: [PATCH 198/281] [Console] Add broader support for command "help" definition --- DependencyInjection/FrameworkExtension.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index d435c83e0..9ef918161 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -611,7 +611,11 @@ public function load(array $configs, ContainerBuilder $container): void $container->registerForAutoconfiguration(AssetCompilerInterface::class) ->addTag('asset_mapper.compiler'); $container->registerAttributeForAutoconfiguration(AsCommand::class, static function (ChildDefinition $definition, AsCommand $attribute, \ReflectionClass $reflector): void { - $definition->addTag('console.command', ['command' => $attribute->name, 'description' => $attribute->description]); + $definition->addTag('console.command', [ + 'command' => $attribute->name, + 'description' => $attribute->description, + 'help' => $attribute->help, + ]); }); $container->registerForAutoconfiguration(Command::class) ->addTag('console.command'); From 3b39194257a46b11451070ec8c9a359854b46b8e Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Fri, 17 Jan 2025 09:45:41 +0100 Subject: [PATCH 199/281] [JsonEncoder] Allow to warm up item and list --- DependencyInjection/FrameworkExtension.php | 7 +++++-- Tests/Functional/JsonEncoderTest.php | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 6d20ca653..3ef5de070 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -746,8 +746,11 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu } ); } - $container->registerAttributeForAutoconfiguration(JsonEncodable::class, static function (ChildDefinition $definition): void { - $definition->addTag('json_encoder.encodable'); + $container->registerAttributeForAutoconfiguration(JsonEncodable::class, static function (ChildDefinition $definition, JsonEncodable $attribute): void { + $definition->addTag('json_encoder.encodable', [ + 'object' => $attribute->asObject, + 'list' => $attribute->asList, + ]); $definition->addTag('container.excluded'); }); diff --git a/Tests/Functional/JsonEncoderTest.php b/Tests/Functional/JsonEncoderTest.php index 93ca1fd6d..b5410e1e1 100644 --- a/Tests/Functional/JsonEncoderTest.php +++ b/Tests/Functional/JsonEncoderTest.php @@ -62,6 +62,6 @@ public function testWarmupEncodableClasses() static::getContainer()->get('json_encoder.cache_warmer.encoder_decoder.alias')->warmUp(static::getContainer()->getParameter('kernel.cache_dir')); $this->assertFileExists($encodersDir); - $this->assertCount(1, glob($encodersDir.'/*')); + $this->assertCount(2, glob($encodersDir.'/*')); } } From 2f93db123f4efd5ea4c025dae91a5fe52dc088ae Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 27 Jan 2025 13:38:29 +0100 Subject: [PATCH 200/281] [FrameworkBundle] Add support for info on `ArrayNodeDefinition::canBeEnabled()` and `ArrayNodeDefinition::canBeDisabled()` --- DependencyInjection/Configuration.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 265fba9fd..c765f22ce 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1054,12 +1054,8 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e ->end() ->end() ->arrayNode('not_compromised_password') - ->canBeDisabled() + ->canBeDisabled('When disabled, compromised passwords will be accepted as valid.') ->children() - ->booleanNode('enabled') - ->defaultTrue() - ->info('When disabled, compromised passwords will be accepted as valid.') - ->end() ->scalarNode('endpoint') ->defaultNull() ->info('API endpoint for the NotCompromisedPassword Validator.') From feda5a4301d8dd65ba4b7c3119155d480dfc6cef Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 29 Jan 2025 16:47:19 +0100 Subject: [PATCH 201/281] bump symfony/config requirement to 7.3 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f4c482356..03707eea3 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "composer-runtime-api": ">=2.1", "ext-xml": "*", "symfony/cache": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", + "symfony/config": "^7.3", "symfony/dependency-injection": "^7.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.4|^7.0", From 2f045752a53f1a154954bff13aa8b6019835998f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 2 Feb 2025 13:02:23 +0100 Subject: [PATCH 202/281] fix package name in deprecation --- DependencyInjection/Configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index c765f22ce..33d84dd18 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1234,7 +1234,7 @@ private function addPropertyInfoSection(ArrayNodeDefinition $rootNode, callable ->then(function ($v) { $v['property_info']['with_constructor_extractor'] = false; - trigger_deprecation('symfony/property-info', '7.3', 'Not setting the "with_constructor_extractor" option explicitly is deprecated because its default value will change in version 8.0.'); + trigger_deprecation('symfony/framework-bundle', '7.3', 'Not setting the "with_constructor_extractor" option explicitly is deprecated because its default value will change in version 8.0.'); return $v; }) From 4abc95f58ee6069fe4c0870f07646843ac9d68be Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 2 Feb 2025 21:58:22 +0100 Subject: [PATCH 203/281] fix compat with AsCommand attributes from symfony/console < 7.3 FrameworkBundle 7.3 can be used with older releases of the Console component which do not provide help information through the AsCommand attribute. --- DependencyInjection/FrameworkExtension.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 88fe69ec3..f6160d493 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -610,11 +610,11 @@ public function load(array $configs, ContainerBuilder $container): void ->addTag('assets.package'); $container->registerForAutoconfiguration(AssetCompilerInterface::class) ->addTag('asset_mapper.compiler'); - $container->registerAttributeForAutoconfiguration(AsCommand::class, static function (ChildDefinition $definition, AsCommand $attribute, \ReflectionClass $reflector): void { + $container->registerAttributeForAutoconfiguration(AsCommand::class, static function (ChildDefinition $definition, AsCommand $attribute): void { $definition->addTag('console.command', [ 'command' => $attribute->name, 'description' => $attribute->description, - 'help' => $attribute->help, + 'help' => $attribute->help ?? null, ]); }); $container->registerForAutoconfiguration(Command::class) From 24b0b4e35b862ad3ffdc9af6201fdac289548564 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 3 Feb 2025 11:05:40 +0100 Subject: [PATCH 204/281] Deduplicate Middleware --- DependencyInjection/FrameworkExtension.php | 8 +++ Resources/config/messenger.php | 6 ++ .../FrameworkExtensionTestCase.php | 64 ++++++++++++++----- 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index f6160d493..83f9011ac 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -120,6 +120,7 @@ use Symfony\Component\Messenger\Handler\BatchHandlerInterface; use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\Messenger\Middleware\DeduplicateMiddleware; use Symfony\Component\Messenger\Middleware\RouterContextMiddleware; use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface; use Symfony\Component\Messenger\Transport\TransportFactoryInterface as MessengerTransportFactoryInterface; @@ -2266,6 +2267,13 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder ['id' => 'handle_message'], ], ]; + + if (class_exists(DeduplicateMiddleware::class) && class_exists(LockFactory::class)) { + $defaultMiddleware['before'][] = ['id' => 'deduplicate_middleware']; + } else { + $container->removeDefinition('messenger.middleware.deduplicate_middleware'); + } + foreach ($config['buses'] as $busId => $bus) { $middleware = $bus['middleware']; diff --git a/Resources/config/messenger.php b/Resources/config/messenger.php index 40f5b84ca..8798d5f2e 100644 --- a/Resources/config/messenger.php +++ b/Resources/config/messenger.php @@ -25,6 +25,7 @@ use Symfony\Component\Messenger\EventListener\StopWorkerOnRestartSignalListener; use Symfony\Component\Messenger\Handler\RedispatchMessageHandler; use Symfony\Component\Messenger\Middleware\AddBusNameStampMiddleware; +use Symfony\Component\Messenger\Middleware\DeduplicateMiddleware; use Symfony\Component\Messenger\Middleware\DispatchAfterCurrentBusMiddleware; use Symfony\Component\Messenger\Middleware\FailedMessageProcessingMiddleware; use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; @@ -86,6 +87,11 @@ ->tag('monolog.logger', ['channel' => 'messenger']) ->call('setLogger', [service('logger')->ignoreOnInvalid()]) + ->set('messenger.middleware.deduplicate_middleware', DeduplicateMiddleware::class) + ->args([ + service('lock.factory'), + ]) + ->set('messenger.middleware.add_bus_name_stamp_middleware', AddBusNameStampMiddleware::class) ->abstract() diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 1f4daac5d..2535707f5 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -62,6 +62,7 @@ use Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpTransportFactory; use Symfony\Component\Messenger\Bridge\Beanstalkd\Transport\BeanstalkdTransportFactory; use Symfony\Component\Messenger\Bridge\Redis\Transport\RedisTransportFactory; +use Symfony\Component\Messenger\Middleware\DeduplicateMiddleware; use Symfony\Component\Messenger\Transport\TransportFactory; use Symfony\Component\Notifier\ChatterInterface; use Symfony\Component\Notifier\TexterInterface; @@ -1061,25 +1062,54 @@ public function testMessengerWithMultipleBuses() $this->assertTrue($container->has('messenger.bus.commands')); $this->assertSame([], $container->getDefinition('messenger.bus.commands')->getArgument(0)); - $this->assertEquals([ - ['id' => 'add_bus_name_stamp_middleware', 'arguments' => ['messenger.bus.commands']], - ['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.commands.middleware')); + + if (class_exists(DeduplicateMiddleware::class)) { + $this->assertEquals([ + ['id' => 'add_bus_name_stamp_middleware', 'arguments' => ['messenger.bus.commands']], + ['id' => 'reject_redelivered_message_middleware'], + ['id' => 'dispatch_after_current_bus'], + ['id' => 'failed_message_processing_middleware'], + ['id' => 'deduplicate_middleware'], + ['id' => 'send_message', 'arguments' => [true]], + ['id' => 'handle_message', 'arguments' => [false]], + ], $container->getParameter('messenger.bus.commands.middleware')); + } else { + $this->assertEquals([ + ['id' => 'add_bus_name_stamp_middleware', 'arguments' => ['messenger.bus.commands']], + ['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.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' => 'with_factory', 'arguments' => ['foo', true, ['bar' => 'baz']]], - ['id' => 'send_message', 'arguments' => [true]], - ['id' => 'handle_message', 'arguments' => [false]], - ], $container->getParameter('messenger.bus.events.middleware')); + + if (class_exists(DeduplicateMiddleware::class)) { + $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' => 'deduplicate_middleware'], + ['id' => 'with_factory', 'arguments' => ['foo', true, ['bar' => 'baz']]], + ['id' => 'send_message', 'arguments' => [true]], + ['id' => 'handle_message', 'arguments' => [false]], + ], $container->getParameter('messenger.bus.events.middleware')); + } else { + $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' => 'with_factory', 'arguments' => ['foo', true, ['bar' => 'baz']]], + ['id' => 'send_message', 'arguments' => [true]], + ['id' => 'handle_message', 'arguments' => [false]], + ], $container->getParameter('messenger.bus.events.middleware')); + } + $this->assertTrue($container->has('messenger.bus.queries')); $this->assertSame([], $container->getDefinition('messenger.bus.queries')->getArgument(0)); $this->assertEquals([ From ac168b622265da8e9ebe029c8c0aa1fd390fb7fe Mon Sep 17 00:00:00 2001 From: valtzu Date: Sat, 1 Feb 2025 18:47:33 +0200 Subject: [PATCH 205/281] Add `NumberNormalizer` --- DependencyInjection/FrameworkExtension.php | 6 ++++++ Resources/config/serializer.php | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index f6160d493..51699f298 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -170,6 +170,7 @@ use Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NumberNormalizer; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Stopwatch\Stopwatch; use Symfony\Component\String\LazyString; @@ -1933,6 +1934,11 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->removeDefinition('serializer.normalizer.mime_message'); } + // BC layer Serializer < 7.3 + if (!class_exists(NumberNormalizer::class)) { + $container->removeDefinition('serializer.normalizer.number'); + } + // BC layer Serializer < 7.2 if (!class_exists(SnakeCaseToCamelCaseNameConverter::class)) { $container->removeDefinition('serializer.name_converter.snake_case_to_camel_case'); diff --git a/Resources/config/serializer.php b/Resources/config/serializer.php index 4686a88f6..01b9a3eb0 100644 --- a/Resources/config/serializer.php +++ b/Resources/config/serializer.php @@ -44,6 +44,7 @@ use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer; use Symfony\Component\Serializer\Normalizer\MimeMessageNormalizer; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NumberNormalizer; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Normalizer\ProblemNormalizer; use Symfony\Component\Serializer\Normalizer\PropertyNormalizer; @@ -221,5 +222,8 @@ ->set('serializer.normalizer.backed_enum', BackedEnumNormalizer::class) ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915]) + + ->set('serializer.normalizer.number', NumberNormalizer::class) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915]) ; }; From 94e246ba9183b8910debde9b380b335a3f8ac6be Mon Sep 17 00:00:00 2001 From: valtzu Date: Mon, 3 Feb 2025 17:55:58 +0200 Subject: [PATCH 206/281] Normalize `TriggerInterface` as `string` --- Resources/config/scheduler.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Resources/config/scheduler.php b/Resources/config/scheduler.php index 7b2856d82..4cbfb73b5 100644 --- a/Resources/config/scheduler.php +++ b/Resources/config/scheduler.php @@ -13,6 +13,7 @@ use Symfony\Component\Scheduler\EventListener\DispatchSchedulerEventListener; use Symfony\Component\Scheduler\Messenger\SchedulerTransportFactory; +use Symfony\Component\Scheduler\Messenger\Serializer\Normalizer\SchedulerTriggerNormalizer; use Symfony\Component\Scheduler\Messenger\ServiceCallMessageHandler; return static function (ContainerConfigurator $container) { @@ -34,5 +35,7 @@ service('event_dispatcher'), ]) ->tag('kernel.event_subscriber') + ->set('serializer.normalizer.scheduler_trigger', SchedulerTriggerNormalizer::class) + ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -880]) ; }; From 56807dbf418106639b418c46e3562b12fdb90088 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 27 Jun 2023 16:01:55 +0200 Subject: [PATCH 207/281] [FrameworkBundle][Validator] Add `framework.validation.disable_translation` config --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 3 +++ DependencyInjection/FrameworkExtension.php | 4 ++++ Resources/config/schema/symfony-1.0.xsd | 1 + Tests/DependencyInjection/ConfigurationTest.php | 1 + 5 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c9aecbd5..49f8d634d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * Add new `framework.property_info.with_constructor_extractor` option to allow enabling or disabling the constructor extractor integration * Deprecate the `--show-arguments` option of the `container:debug` command, as arguments are now always shown * Add `RateLimiterFactoryInterface` as an alias of the `limiter` service + * Add `framework.validation.disable_translation` option 7.2 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 33d84dd18..7f77b4941 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1062,6 +1062,9 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e ->end() ->end() ->end() + ->booleanNode('disable_translation') + ->defaultFalse() + ->end() ->arrayNode('auto_mapping') ->info('A collection of namespaces for which auto-mapping will be enabled by default, or null to opt-in with the EnableAutoMapping constraint.') ->example([ diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index bd04b0dbf..c1b777ad8 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -1724,6 +1724,10 @@ private function registerValidationConfiguration(array $config, ContainerBuilder $validatorBuilder->addMethodCall('setMappingCache', [new Reference('validator.mapping.cache.adapter')]); } + if ($config['disable_translation'] ?? false) { + $validatorBuilder->addMethodCall('disableTranslation'); + } + $container->setParameter('validator.auto_mapping', $config['auto_mapping']); if (!$propertyInfoEnabled || !class_exists(PropertyInfoLoader::class)) { $container->removeDefinition('validator.property_info_loader'); diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index 0f90f1cfc..41c5d8557 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -298,6 +298,7 @@ + diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index e8ca9873f..4ede2c07e 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -775,6 +775,7 @@ protected static function getBundleDefaultConfig() 'enable_attributes' => !class_exists(FullStack::class), 'static_method' => ['loadValidatorMetadata'], 'translation_domain' => 'validators', + 'disable_translation' => false, 'mapping' => [ 'paths' => [], ], From 4bd05f1d17826f74d3bdd777794c5921bce23c1f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 7 Feb 2025 14:16:34 +0100 Subject: [PATCH 208/281] fix compatibility with symfony/scheduler < 7.3 --- DependencyInjection/FrameworkExtension.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index c1b777ad8..10d703744 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -156,6 +156,7 @@ use Symfony\Component\Scheduler\Attribute\AsPeriodicTask; use Symfony\Component\Scheduler\Attribute\AsSchedule; use Symfony\Component\Scheduler\Messenger\SchedulerTransportFactory; +use Symfony\Component\Scheduler\Messenger\Serializer\Normalizer\SchedulerTriggerNormalizer; use Symfony\Component\Security\Core\AuthenticationEvents; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; @@ -2220,6 +2221,11 @@ private function registerSchedulerConfiguration(ContainerBuilder $container, Php if (!$this->hasConsole()) { $container->removeDefinition('console.command.scheduler_debug'); } + + // BC layer Scheduler < 7.3 + if (!class_exists(SchedulerTriggerNormalizer::class)) { + $container->removeDefinition('serializer.normalizer.scheduler_trigger'); + } } private function registerMessengerConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $validationEnabled): void From 06b9c6f486aa1bc1a1f4b9d00b036f79e3ac3994 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 7 Feb 2025 19:13:17 +0100 Subject: [PATCH 209/281] [Form][FrameworkBundle] Use auto-configuration to make the default CSRF token id apply only to the app; not to bundles --- DependencyInjection/FrameworkExtension.php | 6 ++---- Resources/config/form_csrf.php | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 556a0cff6..98dd074f4 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -615,7 +615,7 @@ public function load(array $configs, ContainerBuilder $container): void $container->registerForAutoconfiguration(DataCollectorInterface::class) ->addTag('data_collector'); $container->registerForAutoconfiguration(FormTypeInterface::class) - ->addTag('form.type'); + ->addTag('form.type', ['csrf_token_id' => '%.form.type_extension.csrf.token_id%']); $container->registerForAutoconfiguration(FormTypeGuesserInterface::class) ->addTag('form.type_guesser'); $container->registerForAutoconfiguration(FormTypeExtensionInterface::class) @@ -777,9 +777,7 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont $container->setParameter('form.type_extension.csrf.enabled', true); $container->setParameter('form.type_extension.csrf.field_name', $config['form']['csrf_protection']['field_name']); $container->setParameter('form.type_extension.csrf.field_attr', $config['form']['csrf_protection']['field_attr']); - - $container->getDefinition('form.type_extension.csrf') - ->replaceArgument(7, $config['form']['csrf_protection']['token_id']); + $container->setParameter('.form.type_extension.csrf.token_id', $config['form']['csrf_protection']['token_id']); } else { $container->setParameter('form.type_extension.csrf.enabled', false); } diff --git a/Resources/config/form_csrf.php b/Resources/config/form_csrf.php index c63d087c8..a86bb7c60 100644 --- a/Resources/config/form_csrf.php +++ b/Resources/config/form_csrf.php @@ -24,7 +24,7 @@ param('validator.translation_domain'), service('form.server_params'), param('form.type_extension.csrf.field_attr'), - abstract_arg('framework.form.csrf_protection.token_id'), + param('.form.type_extension.csrf.token_id'), ]) ->tag('form.type_extension') ; From 889f88f81f1b405b700fe6200ba455e8ce87f4e6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 8 Feb 2025 15:39:35 +0100 Subject: [PATCH 210/281] the DeduplicateMiddleware requires the lock feature to be enabled If the `lock` feature is not enabled, no `lock.factory` service will be registered. --- DependencyInjection/FrameworkExtension.php | 6 +- ...iple_buses_with_deduplicate_middleware.php | 27 +++++ ..._buses_without_deduplicate_middleware.php} | 0 ...iple_buses_with_deduplicate_middleware.xml | 29 +++++ ..._buses_without_deduplicate_middleware.xml} | 0 ...iple_buses_with_deduplicate_middleware.yml | 19 ++++ ..._buses_without_deduplicate_middleware.yml} | 0 .../FrameworkExtensionTestCase.php | 103 ++++++++++-------- 8 files changed, 136 insertions(+), 48 deletions(-) create mode 100644 Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_with_deduplicate_middleware.php rename Tests/DependencyInjection/Fixtures/php/{messenger_multiple_buses.php => messenger_multiple_buses_without_deduplicate_middleware.php} (100%) create mode 100644 Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_with_deduplicate_middleware.xml rename Tests/DependencyInjection/Fixtures/xml/{messenger_multiple_buses.xml => messenger_multiple_buses_without_deduplicate_middleware.xml} (100%) create mode 100644 Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_with_deduplicate_middleware.yml rename Tests/DependencyInjection/Fixtures/yml/{messenger_multiple_buses.yml => messenger_multiple_buses_without_deduplicate_middleware.yml} (100%) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 10d703744..96bd5924b 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -538,7 +538,7 @@ public function load(array $configs, ContainerBuilder $container): void // messenger depends on validation being registered if ($messengerEnabled) { - $this->registerMessengerConfiguration($config['messenger'], $container, $loader, $this->readConfigEnabled('validation', $container, $config['validation'])); + $this->registerMessengerConfiguration($config['messenger'], $container, $loader, $this->readConfigEnabled('validation', $container, $config['validation']), $this->readConfigEnabled('lock', $container, $config['lock'])); } else { $container->removeDefinition('console.command.messenger_consume_messages'); $container->removeDefinition('console.command.messenger_stats'); @@ -2228,7 +2228,7 @@ private function registerSchedulerConfiguration(ContainerBuilder $container, Php } } - private function registerMessengerConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $validationEnabled): void + private function registerMessengerConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $validationEnabled, bool $lockEnabled): void { if (!interface_exists(MessageBusInterface::class)) { throw new LogicException('Messenger support cannot be enabled as the Messenger component is not installed. Try running "composer require symfony/messenger".'); @@ -2284,7 +2284,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder ], ]; - if (class_exists(DeduplicateMiddleware::class) && class_exists(LockFactory::class)) { + if ($lockEnabled && class_exists(DeduplicateMiddleware::class) && class_exists(LockFactory::class)) { $defaultMiddleware['before'][] = ['id' => 'deduplicate_middleware']; } else { $container->removeDefinition('messenger.middleware.deduplicate_middleware'); diff --git a/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_with_deduplicate_middleware.php b/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_with_deduplicate_middleware.php new file mode 100644 index 000000000..f9b3767c0 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_with_deduplicate_middleware.php @@ -0,0 +1,27 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'lock' => null, + 'messenger' => [ + 'default_bus' => 'messenger.bus.commands', + 'buses' => [ + 'messenger.bus.commands' => null, + 'messenger.bus.events' => [ + 'middleware' => [ + ['with_factory' => ['foo', true, ['bar' => 'baz']]], + ], + ], + 'messenger.bus.queries' => [ + 'default_middleware' => false, + 'middleware' => [ + 'send_message', + 'handle_message', + ], + ], + ], + ], +]); diff --git a/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses.php b/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_without_deduplicate_middleware.php similarity index 100% rename from Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses.php rename to Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_without_deduplicate_middleware.php diff --git a/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_with_deduplicate_middleware.xml b/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_with_deduplicate_middleware.xml new file mode 100644 index 000000000..67decad20 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_with_deduplicate_middleware.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + foo + true + + baz + + + + + + + + + + diff --git a/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses.xml b/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_without_deduplicate_middleware.xml similarity index 100% rename from Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses.xml rename to Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_without_deduplicate_middleware.xml diff --git a/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_with_deduplicate_middleware.yml b/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_with_deduplicate_middleware.yml new file mode 100644 index 000000000..ed52564c7 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_with_deduplicate_middleware.yml @@ -0,0 +1,19 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + lock: ~ + messenger: + default_bus: messenger.bus.commands + buses: + messenger.bus.commands: ~ + messenger.bus.events: + middleware: + - with_factory: [foo, true, { bar: baz }] + messenger.bus.queries: + default_middleware: false + middleware: + - "send_message" + - "handle_message" diff --git a/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses.yml b/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_without_deduplicate_middleware.yml similarity index 100% rename from Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses.yml rename to Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_without_deduplicate_middleware.yml diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 2535707f5..a50070858 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -1056,60 +1056,73 @@ public function testMessengerTransportConfiguration() $this->assertSame(['enable_max_depth' => true], $serializerTransportDefinition->getArgument(2)); } - public function testMessengerWithMultipleBuses() + public function testMessengerWithMultipleBusesWithoutDeduplicateMiddleware() { - $container = $this->createContainerFromFile('messenger_multiple_buses'); + $container = $this->createContainerFromFile('messenger_multiple_buses_without_deduplicate_middleware'); $this->assertTrue($container->has('messenger.bus.commands')); $this->assertSame([], $container->getDefinition('messenger.bus.commands')->getArgument(0)); - - if (class_exists(DeduplicateMiddleware::class)) { - $this->assertEquals([ - ['id' => 'add_bus_name_stamp_middleware', 'arguments' => ['messenger.bus.commands']], - ['id' => 'reject_redelivered_message_middleware'], - ['id' => 'dispatch_after_current_bus'], - ['id' => 'failed_message_processing_middleware'], - ['id' => 'deduplicate_middleware'], - ['id' => 'send_message', 'arguments' => [true]], - ['id' => 'handle_message', 'arguments' => [false]], - ], $container->getParameter('messenger.bus.commands.middleware')); - } else { - $this->assertEquals([ - ['id' => 'add_bus_name_stamp_middleware', 'arguments' => ['messenger.bus.commands']], - ['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.commands.middleware')); - } - + $this->assertEquals([ + ['id' => 'add_bus_name_stamp_middleware', 'arguments' => ['messenger.bus.commands']], + ['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.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' => 'with_factory', 'arguments' => ['foo', true, ['bar' => 'baz']]], + ['id' => 'send_message', 'arguments' => [true]], + ['id' => 'handle_message', 'arguments' => [false]], + ], $container->getParameter('messenger.bus.events.middleware')); + $this->assertTrue($container->has('messenger.bus.queries')); + $this->assertSame([], $container->getDefinition('messenger.bus.queries')->getArgument(0)); + $this->assertEquals([ + ['id' => 'send_message', 'arguments' => []], + ['id' => 'handle_message', 'arguments' => []], + ], $container->getParameter('messenger.bus.queries.middleware')); - if (class_exists(DeduplicateMiddleware::class)) { - $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' => 'deduplicate_middleware'], - ['id' => 'with_factory', 'arguments' => ['foo', true, ['bar' => 'baz']]], - ['id' => 'send_message', 'arguments' => [true]], - ['id' => 'handle_message', 'arguments' => [false]], - ], $container->getParameter('messenger.bus.events.middleware')); - } else { - $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' => 'with_factory', 'arguments' => ['foo', true, ['bar' => 'baz']]], - ['id' => 'send_message', 'arguments' => [true]], - ['id' => 'handle_message', 'arguments' => [false]], - ], $container->getParameter('messenger.bus.events.middleware')); + $this->assertTrue($container->hasAlias('messenger.default_bus')); + $this->assertSame('messenger.bus.commands', (string) $container->getAlias('messenger.default_bus')); + } + + public function testMessengerWithMultipleBusesWithDeduplicateMiddleware() + { + if (!class_exists(DeduplicateMiddleware::class)) { + $this->markTestSkipped('DeduplicateMiddleware not available.'); } + $container = $this->createContainerFromFile('messenger_multiple_buses_with_deduplicate_middleware'); + + $this->assertTrue($container->has('messenger.bus.commands')); + $this->assertSame([], $container->getDefinition('messenger.bus.commands')->getArgument(0)); + $this->assertEquals([ + ['id' => 'add_bus_name_stamp_middleware', 'arguments' => ['messenger.bus.commands']], + ['id' => 'reject_redelivered_message_middleware'], + ['id' => 'dispatch_after_current_bus'], + ['id' => 'failed_message_processing_middleware'], + ['id' => 'deduplicate_middleware'], + ['id' => 'send_message', 'arguments' => [true]], + ['id' => 'handle_message', 'arguments' => [false]], + ], $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' => 'deduplicate_middleware'], + ['id' => 'with_factory', 'arguments' => ['foo', true, ['bar' => 'baz']]], + ['id' => 'send_message', 'arguments' => [true]], + ['id' => 'handle_message', 'arguments' => [false]], + ], $container->getParameter('messenger.bus.events.middleware')); $this->assertTrue($container->has('messenger.bus.queries')); $this->assertSame([], $container->getDefinition('messenger.bus.queries')->getArgument(0)); $this->assertEquals([ From 6ca24561727edcb5e251a04bf73508fdd8165c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?El=C3=ADas=20Fern=C3=A1ndez?= Date: Tue, 8 Oct 2024 18:14:29 +0200 Subject: [PATCH 211/281] [Mailer] Add configuration for dkim and smime signers --- DependencyInjection/Configuration.php | 82 +++++++++++++++++++ DependencyInjection/FrameworkExtension.php | 45 ++++++++++ Resources/config/mailer.php | 48 +++++++++++ Resources/config/schema/symfony-1.0.xsd | 27 ++++++ .../DependencyInjection/ConfigurationTest.php | 21 +++++ 5 files changed, 223 insertions(+) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 33d84dd18..04e747bc7 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -2236,6 +2236,88 @@ private function addMailerSection(ArrayNodeDefinition $rootNode, callable $enabl ->end() ->end() ->end() + ->arrayNode('dkim_signer') + ->addDefaultsIfNotSet() + ->fixXmlConfig('option') + ->canBeEnabled() + ->info('DKIM signer configuration') + ->children() + ->scalarNode('key') + ->info('Key content, or path to key (in PEM format with the `file://` prefix)') + ->defaultValue('') + ->cannotBeEmpty() + ->end() + ->scalarNode('domain')->defaultValue('')->end() + ->scalarNode('select')->defaultValue('')->end() + ->scalarNode('passphrase') + ->info('The private key passphrase') + ->defaultValue('') + ->end() + ->arrayNode('options') + ->performNoDeepMerging() + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->prototype('variable')->end() + ->end() + ->end() + ->end() + ->arrayNode('smime_signer') + ->addDefaultsIfNotSet() + ->canBeEnabled() + ->info('S/MIME signer configuration') + ->children() + ->scalarNode('key') + ->info('Path to key (in PEM format)') + ->defaultValue('') + ->cannotBeEmpty() + ->end() + ->scalarNode('certificate') + ->info('Path to certificate (in PEM format without the `file://` prefix)') + ->defaultValue('') + ->cannotBeEmpty() + ->end() + ->scalarNode('passphrase') + ->info('The private key passphrase') + ->defaultNull() + ->end() + ->scalarNode('extra_certificates')->defaultNull()->end() + ->integerNode('sign_options')->defaultNull()->end() + ->end() + ->end() + ->arrayNode('smime_encrypter') + ->addDefaultsIfNotSet() + ->canBeEnabled() + ->info('S/MIME encrypter configuration') + ->children() + ->scalarNode('certificate') + ->info('Path to certificate (in PEM format without the `file://` prefix)') + ->defaultValue('') + ->cannotBeEmpty() + ->end() + ->integerNode('cipher') + ->info('A set of algorithms used to encrypt the message') + ->defaultNull() + ->beforeNormalization() + ->always(function ($v): ?int { + if (null === $v) { + return null; + } + if (\defined('OPENSSL_CIPHER_'.$v)) { + return \constant('OPENSSL_CIPHER_'.$v); + } + + throw new \InvalidArgumentException(\sprintf('"%s" is not a valid OPENSSL cipher.', $v)); + }) + ->end() + ->validate() + ->ifTrue(function ($v) { + return \extension_loaded('openssl') && null !== $v && !\defined('OPENSSL_CIPHER_'.$v); + }) + ->thenInvalid('You must provide a valid cipher.') + ->end() + ->end() + ->end() + ->end() ->end() ->end() ->end() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index f6160d493..56cba363b 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -112,7 +112,10 @@ use Symfony\Component\Lock\Store\StoreFactory; use Symfony\Component\Mailer\Bridge as MailerBridge; use Symfony\Component\Mailer\Command\MailerTestCommand; +use Symfony\Component\Mailer\EventListener\DkimSignedMessageListener; use Symfony\Component\Mailer\EventListener\MessengerTransportListener; +use Symfony\Component\Mailer\EventListener\SmimeEncryptedMessageListener; +use Symfony\Component\Mailer\EventListener\SmimeSignedMessageListener; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mercure\HubRegistry; use Symfony\Component\Messenger\Attribute\AsMessageHandler; @@ -2837,6 +2840,48 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co $container->removeDefinition('mailer.messenger_transport_listener'); } + if ($config['dkim_signer']['enabled']) { + if (!class_exists(DkimSignedMessageListener::class)) { + throw new LogicException('DKIM signed messages support cannot be enabled as this version of the Mailer component does not support it.'); + } + $dkimSigner = $container->getDefinition('mailer.dkim_signer'); + $dkimSigner->setArgument(0, $config['dkim_signer']['key']); + $dkimSigner->setArgument(1, $config['dkim_signer']['domain']); + $dkimSigner->setArgument(2, $config['dkim_signer']['select']); + $dkimSigner->setArgument(3, $config['dkim_signer']['options']); + $dkimSigner->setArgument(4, $config['dkim_signer']['passphrase']); + } else { + $container->removeDefinition('mailer.dkim_signer'); + $container->removeDefinition('mailer.dkim_signer.listener'); + } + + if ($config['smime_signer']['enabled']) { + if (!class_exists(SmimeSignedMessageListener::class)) { + throw new LogicException('SMIME signed messages support cannot be enabled as this version of the Mailer component does not support it.'); + } + $smimeSigner = $container->getDefinition('mailer.smime_signer'); + $smimeSigner->setArgument(0, $config['smime_signer']['key']); + $smimeSigner->setArgument(1, $config['smime_signer']['certificate']); + $smimeSigner->setArgument(2, $config['smime_signer']['passphrase']); + $smimeSigner->setArgument(3, $config['smime_signer']['extra_certificates']); + $smimeSigner->setArgument(4, $config['smime_signer']['sign_options']); + } else { + $container->removeDefinition('mailer.smime_signer'); + $container->removeDefinition('mailer.smime_signer.listener'); + } + + if ($config['smime_encrypter']['enabled']) { + if (!class_exists(SmimeEncryptedMessageListener::class)) { + throw new LogicException('S/MIME encrypted messages support cannot be enabled as this version of the Mailer component does not support it.'); + } + $smimeDecrypter = $container->getDefinition('mailer.smime_encrypter'); + $smimeDecrypter->setArgument(0, $config['smime_encrypter']['certificate']); + $smimeDecrypter->setArgument(1, $config['smime_encrypter']['cipher']); + } else { + $container->removeDefinition('mailer.smime_encrypter'); + $container->removeDefinition('mailer.smime_encrypter.listener'); + } + if ($webhookEnabled) { $loader->load('mailer_webhook.php'); } diff --git a/Resources/config/mailer.php b/Resources/config/mailer.php index 9eb545ca2..25b3fefdb 100644 --- a/Resources/config/mailer.php +++ b/Resources/config/mailer.php @@ -12,16 +12,22 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Component\Mailer\Command\MailerTestCommand; +use Symfony\Component\Mailer\EventListener\DkimSignedMessageListener; use Symfony\Component\Mailer\EventListener\EnvelopeListener; use Symfony\Component\Mailer\EventListener\MessageListener; use Symfony\Component\Mailer\EventListener\MessageLoggerListener; use Symfony\Component\Mailer\EventListener\MessengerTransportListener; +use Symfony\Component\Mailer\EventListener\SmimeEncryptedMessageListener; +use Symfony\Component\Mailer\EventListener\SmimeSignedMessageListener; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mailer\Messenger\MessageHandler; use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Transport\TransportInterface; use Symfony\Component\Mailer\Transport\Transports; +use Symfony\Component\Mime\Crypto\DkimSigner; +use Symfony\Component\Mime\Crypto\SMimeEncrypter; +use Symfony\Component\Mime\Crypto\SMimeSigner; return static function (ContainerConfigurator $container) { $container->services() @@ -78,6 +84,48 @@ ->set('mailer.messenger_transport_listener', MessengerTransportListener::class) ->tag('kernel.event_subscriber') + ->set('mailer.dkim_signer', DkimSigner::class) + ->args([ + abstract_arg('key'), + abstract_arg('domain'), + abstract_arg('select'), + abstract_arg('options'), + abstract_arg('passphrase'), + ]) + + ->set('mailer.smime_signer', SMimeSigner::class) + ->args([ + abstract_arg('key'), + abstract_arg('certificate'), + abstract_arg('passphrase'), + abstract_arg('extraCertificates'), + abstract_arg('signOptions'), + ]) + + ->set('mailer.smime_encrypter', SMimeEncrypter::class) + ->args([ + abstract_arg('certificate'), + abstract_arg('cipher'), + ]) + + ->set('mailer.dkim_signer.listener', DkimSignedMessageListener::class) + ->args([ + service(DkimSigner::class), + ]) + ->tag('kernel.event_subscriber') + + ->set('mailer.smime_signer.listener', SmimeSignedMessageListener::class) + ->args([ + service('mailer.smime_signer'), + ]) + ->tag('kernel.event_subscriber') + + ->set('mailer.smime_encrypter.listener', SmimeEncryptedMessageListener::class) + ->args([ + service('mailer.smime_encrypter'), + ]) + ->tag('kernel.event_subscriber') + ->set('console.command.mailer_test', MailerTestCommand::class) ->args([ service('mailer.transports'), diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index 0f90f1cfc..e2edb6aa0 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -789,6 +789,9 @@ + + + @@ -811,6 +814,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index e8ca9873f..69557cc8f 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -922,6 +922,27 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor 'enabled' => !class_exists(FullStack::class) && class_exists(Mailer::class), 'message_bus' => null, 'headers' => [], + 'dkim_signer' => [ + 'enabled' => false, + 'options' => [], + 'key' => '', + 'domain' => '', + 'select' => '', + 'passphrase' => '', + ], + 'smime_signer' => [ + 'enabled' => false, + 'key' => '', + 'certificate' => '', + 'passphrase' => null, + 'extra_certificates' => null, + 'sign_options' => null, + ], + 'smime_encrypter' => [ + 'enabled' => false, + 'certificate' => '', + 'cipher' => null, + ], ], 'notifier' => [ 'enabled' => !class_exists(FullStack::class) && class_exists(Notifier::class), From 582d165582e05480e8a9a5626086f1af6596af95 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 20 Mar 2023 22:09:31 +0100 Subject: [PATCH 212/281] [FrameworkBundle] Add support for signal plain name in the `messenger.stop_worker_on_signals` configuration --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 22 +++++++++++++++++++++- Resources/config/schema/symfony-1.0.xsd | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49f8d634d..55eeaf1fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ CHANGELOG * Deprecate the `--show-arguments` option of the `container:debug` command, as arguments are now always shown * Add `RateLimiterFactoryInterface` as an alias of the `limiter` service * Add `framework.validation.disable_translation` option + * Add support for signal plain name in the `messenger.stop_worker_on_signals` configuration 7.2 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 7b1cafac5..f5279c419 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1572,6 +1572,7 @@ private function addMessengerSection(ArrayNodeDefinition $rootNode, callable $en ->{$enableIfStandalone('symfony/messenger', MessageBusInterface::class)}() ->fixXmlConfig('transport') ->fixXmlConfig('bus', 'buses') + ->fixXmlConfig('stop_worker_on_signal') ->validate() ->ifTrue(fn ($v) => isset($v['buses']) && \count($v['buses']) > 1 && null === $v['default_bus']) ->thenInvalid('You must specify the "default_bus" if you define more than one bus.') @@ -1704,7 +1705,26 @@ function ($a) { ->arrayNode('stop_worker_on_signals') ->defaultValue([]) ->info('A list of signals that should stop the worker; defaults to SIGTERM and SIGINT.') - ->integerPrototype()->end() + ->beforeNormalization() + ->always(function ($signals) { + if (!\is_array($signals)) { + throw new InvalidConfigurationException('The "stop_worker_on_signals" option must be an array in messenger configuration.'); + } + + return array_map(static function ($v) { + if (\is_string($v) && str_starts_with($v, 'SIG') && \array_key_exists($v, get_defined_constants(true)['pcntl'])) { + return \constant($v); + } + + if (!\is_int($v)) { + throw new InvalidConfigurationException('The "stop_worker_on_signals" option must be an array of pcntl signals in messenger configuration.'); + } + + return $v; + }, $signals); + }) + ->end() + ->scalarPrototype()->end() ->end() ->scalarNode('default_bus')->defaultNull()->end() ->arrayNode('buses') diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index 2dc46beb9..b47de331a 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -609,6 +609,7 @@ + From b9fbf4e0fc27ff441b0d3d0f07b478d10cfb0241 Mon Sep 17 00:00:00 2001 From: alanzarli Date: Tue, 21 Jan 2025 10:32:54 +0100 Subject: [PATCH 213/281] [Notifier][Webhook] Add Smsbox support --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_webhook.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index bc1d623e7..bff1fcfda 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -3117,6 +3117,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ $loader->load('notifier_webhook.php'); $webhookRequestParsers = [ + NotifierBridge\Smsbox\Webhook\SmsboxRequestParser::class => 'notifier.webhook.request_parser.smsbox', NotifierBridge\Sweego\Webhook\SweegoRequestParser::class => 'notifier.webhook.request_parser.sweego', NotifierBridge\Twilio\Webhook\TwilioRequestParser::class => 'notifier.webhook.request_parser.twilio', NotifierBridge\Vonage\Webhook\VonageRequestParser::class => 'notifier.webhook.request_parser.vonage', diff --git a/Resources/config/notifier_webhook.php b/Resources/config/notifier_webhook.php index 6447f4139..0b30c33e2 100644 --- a/Resources/config/notifier_webhook.php +++ b/Resources/config/notifier_webhook.php @@ -11,12 +11,16 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; +use Symfony\Component\Notifier\Bridge\Smsbox\Webhook\SmsboxRequestParser; use Symfony\Component\Notifier\Bridge\Sweego\Webhook\SweegoRequestParser; use Symfony\Component\Notifier\Bridge\Twilio\Webhook\TwilioRequestParser; use Symfony\Component\Notifier\Bridge\Vonage\Webhook\VonageRequestParser; return static function (ContainerConfigurator $container) { $container->services() + ->set('notifier.webhook.request_parser.smsbox', SmsboxRequestParser::class) + ->alias(SmsboxRequestParser::class, 'notifier.webhook.request_parser.smsbox') + ->set('notifier.webhook.request_parser.sweego', SweegoRequestParser::class) ->alias(SweegoRequestParser::class, 'notifier.webhook.request_parser.sweego') From e7ba416984e0b5c53e9bc3d4b4a3245b12803ff3 Mon Sep 17 00:00:00 2001 From: Frank Schulze Date: Mon, 6 Jan 2025 18:49:38 +0100 Subject: [PATCH 214/281] [Notifier] Add Matrix bridge --- DependencyInjection/FrameworkExtension.php | 1 + Resources/config/notifier_transports.php | 1 + 2 files changed, 2 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 38d37c7fc..24f84d327 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2926,6 +2926,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\Lox24\Lox24TransportFactory::class => 'notifier.transport_factory.lox24', NotifierBridge\Mailjet\MailjetTransportFactory::class => 'notifier.transport_factory.mailjet', NotifierBridge\Mastodon\MastodonTransportFactory::class => 'notifier.transport_factory.mastodon', + NotifierBridge\Matrix\MatrixTransportFactory::class => 'notifier.transport_factory.matrix', NotifierBridge\Mattermost\MattermostTransportFactory::class => 'notifier.transport_factory.mattermost', NotifierBridge\Mercure\MercureTransportFactory::class => 'notifier.transport_factory.mercure', NotifierBridge\MessageBird\MessageBirdTransportFactory::class => 'notifier.transport_factory.message-bird', diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index f28007dec..d1adcfc37 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -36,6 +36,7 @@ 'line-notify' => Bridge\LineNotify\LineNotifyTransportFactory::class, 'linked-in' => Bridge\LinkedIn\LinkedInTransportFactory::class, 'mastodon' => Bridge\Mastodon\MastodonTransportFactory::class, + 'matrix' => Bridge\Matrix\MatrixTransportFactory::class, 'mattermost' => Bridge\Mattermost\MattermostTransportFactory::class, 'mercure' => Bridge\Mercure\MercureTransportFactory::class, 'microsoft-teams' => Bridge\MicrosoftTeams\MicrosoftTeamsTransportFactory::class, From bb860bf829832283e1e382f42dd60809e38ec508 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 27 Aug 2024 19:49:01 +0200 Subject: [PATCH 215/281] [Security] Add ability for voters to explain their vote --- Controller/AbstractController.php | 38 ++++++++++++++++++--- Tests/Controller/AbstractControllerTest.php | 28 +++++++++++++-- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/Controller/AbstractController.php b/Controller/AbstractController.php index af453619b..de7395d5a 100644 --- a/Controller/AbstractController.php +++ b/Controller/AbstractController.php @@ -35,6 +35,7 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authorization\AccessDecision; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\User\UserInterface; @@ -202,6 +203,21 @@ protected function isGranted(mixed $attribute, mixed $subject = null): bool return $this->container->get('security.authorization_checker')->isGranted($attribute, $subject); } + /** + * Checks if the attribute is granted against the current authentication token and optionally supplied subject. + */ + protected function getAccessDecision(mixed $attribute, mixed $subject = null): AccessDecision + { + if (!$this->container->has('security.authorization_checker')) { + throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".'); + } + + $accessDecision = new AccessDecision(); + $accessDecision->isGranted = $this->container->get('security.authorization_checker')->isGranted($attribute, $subject, $accessDecision); + + return $accessDecision; + } + /** * Throws an exception unless the attribute is granted against the current authentication token and optionally * supplied subject. @@ -210,12 +226,24 @@ protected function isGranted(mixed $attribute, mixed $subject = null): bool */ protected function denyAccessUnlessGranted(mixed $attribute, mixed $subject = null, string $message = 'Access Denied.'): void { - if (!$this->isGranted($attribute, $subject)) { - $exception = $this->createAccessDeniedException($message); - $exception->setAttributes([$attribute]); - $exception->setSubject($subject); + if (class_exists(AccessDecision::class)) { + $accessDecision = $this->getAccessDecision($attribute, $subject); + $isGranted = $accessDecision->isGranted; + } else { + $accessDecision = null; + $isGranted = $this->isGranted($attribute, $subject); + } + + if (!$isGranted) { + $e = $this->createAccessDeniedException(3 > \func_num_args() && $accessDecision ? $accessDecision->getMessage() : $message); + $e->setAttributes([$attribute]); + $e->setSubject($subject); + + if ($accessDecision) { + $e->setAccessDecision($accessDecision); + } - throw $exception; + throw $e; } } diff --git a/Tests/Controller/AbstractControllerTest.php b/Tests/Controller/AbstractControllerTest.php index 55a363984..5f5fc5ca5 100644 --- a/Tests/Controller/AbstractControllerTest.php +++ b/Tests/Controller/AbstractControllerTest.php @@ -40,7 +40,10 @@ use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\Authorization\AccessDecision; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Vote; +use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; @@ -352,7 +355,19 @@ public function testIsGranted() public function testdenyAccessUnlessGranted() { $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); - $authorizationChecker->expects($this->once())->method('isGranted')->willReturn(false); + $authorizationChecker + ->expects($this->once()) + ->method('isGranted') + ->willReturnCallback(function ($attribute, $subject, ?AccessDecision $accessDecision = null) { + if (class_exists(AccessDecision::class)) { + $this->assertInstanceOf(AccessDecision::class, $accessDecision); + $accessDecision->votes[] = $vote = new Vote(); + $vote->result = VoterInterface::ACCESS_DENIED; + $vote->reasons[] = 'Why should I.'; + } + + return false; + }); $container = new Container(); $container->set('security.authorization_checker', $authorizationChecker); @@ -361,8 +376,17 @@ public function testdenyAccessUnlessGranted() $controller->setContainer($container); $this->expectException(AccessDeniedException::class); + $this->expectExceptionMessage('Access Denied.'.(class_exists(AccessDecision::class) ? ' Why should I.' : '')); - $controller->denyAccessUnlessGranted('foo'); + try { + $controller->denyAccessUnlessGranted('foo'); + } catch (AccessDeniedException $e) { + if (class_exists(AccessDecision::class)) { + $this->assertFalse($e->getAccessDecision()->isGranted); + } + + throw $e; + } } /** From 8951bc81562bbe7cbe112e6c24aa1f9cf3818cae Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Mon, 23 Dec 2024 15:26:31 +0100 Subject: [PATCH 216/281] [JsonEncoder] Replace normalizers by value transformers --- .../Compiler/UnusedTagsPass.php | 3 +- DependencyInjection/FrameworkExtension.php | 9 ++---- Resources/config/json_encoder.php | 31 +++++++----------- .../Functional/app/JsonEncoder/Dto/Dummy.php | 12 +++---- .../RangeToStringValueTransformer.php | 32 +++++++++++++++++++ ....php => StringToRangeValueTransformer.php} | 16 +++------- Tests/Functional/app/JsonEncoder/config.yml | 3 +- 7 files changed, 60 insertions(+), 46 deletions(-) create mode 100644 Tests/Functional/app/JsonEncoder/RangeToStringValueTransformer.php rename Tests/Functional/app/JsonEncoder/{RangeNormalizer.php => StringToRangeValueTransformer.php} (51%) diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index 5714f8fc2..c5191a659 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -53,8 +53,7 @@ class UnusedTagsPass implements CompilerPassInterface 'form.type_guesser', 'html_sanitizer', 'http_client.client', - 'json_encoder.denormalizer', - 'json_encoder.normalizer', + 'json_encoder.value_transformer', 'kernel.cache_clearer', 'kernel.cache_warmer', 'kernel.event_listener', diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 88fe69ec3..ee302a20c 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -101,11 +101,10 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator; use Symfony\Component\JsonEncoder\Attribute\JsonEncodable; -use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface as JsonEncoderDenormalizerInterface; use Symfony\Component\JsonEncoder\DecoderInterface as JsonEncoderDecoderInterface; -use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface as JsonEncoderNormalizerInterface; use Symfony\Component\JsonEncoder\EncoderInterface as JsonEncoderEncoderInterface; use Symfony\Component\JsonEncoder\JsonEncoder; +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\LockInterface; use Symfony\Component\Lock\PersistingStoreInterface; @@ -2027,10 +2026,8 @@ private function registerJsonEncoderConfiguration(array $config, ContainerBuilde throw new LogicException('JsonEncoder support cannot be enabled as the JsonEncoder component is not installed. Try running "composer require symfony/json-encoder".'); } - $container->registerForAutoconfiguration(JsonEncoderNormalizerInterface::class) - ->addTag('json_encoder.normalizer'); - $container->registerForAutoconfiguration(JsonEncoderDenormalizerInterface::class) - ->addTag('json_encoder.denormalizer'); + $container->registerForAutoconfiguration(ValueTransformerInterface::class) + ->addTag('json_encoder.value_transformer'); $loader->load('json_encoder.php'); diff --git a/Resources/config/json_encoder.php b/Resources/config/json_encoder.php index 67cb25d0a..f6fbd05e6 100644 --- a/Resources/config/json_encoder.php +++ b/Resources/config/json_encoder.php @@ -13,8 +13,6 @@ use Symfony\Component\JsonEncoder\CacheWarmer\EncoderDecoderCacheWarmer; use Symfony\Component\JsonEncoder\CacheWarmer\LazyGhostCacheWarmer; -use Symfony\Component\JsonEncoder\Decode\Denormalizer\DateTimeDenormalizer; -use Symfony\Component\JsonEncoder\Encode\Normalizer\DateTimeNormalizer; use Symfony\Component\JsonEncoder\JsonDecoder; use Symfony\Component\JsonEncoder\JsonEncoder; use Symfony\Component\JsonEncoder\Mapping\Decode\AttributePropertyMetadataLoader as DecodeAttributePropertyMetadataLoader; @@ -23,19 +21,21 @@ use Symfony\Component\JsonEncoder\Mapping\Encode\DateTimeTypePropertyMetadataLoader as EncodeDateTimeTypePropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\GenericTypePropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader; +use Symfony\Component\JsonEncoder\ValueTransformer\DateTimeToStringValueTransformer; +use Symfony\Component\JsonEncoder\ValueTransformer\StringToDateTimeValueTransformer; return static function (ContainerConfigurator $container) { $container->services() // encoder/decoder ->set('json_encoder.encoder', JsonEncoder::class) ->args([ - tagged_locator('json_encoder.normalizer'), + tagged_locator('json_encoder.value_transformer'), service('json_encoder.encode.property_metadata_loader'), param('.json_encoder.encoders_dir'), ]) ->set('json_encoder.decoder', JsonDecoder::class) ->args([ - tagged_locator('json_encoder.denormalizer'), + tagged_locator('json_encoder.value_transformer'), service('json_encoder.decode.property_metadata_loader'), param('.json_encoder.decoders_dir'), param('.json_encoder.lazy_ghosts_dir'), @@ -63,7 +63,7 @@ ->decorate('json_encoder.encode.property_metadata_loader') ->args([ service('.inner'), - tagged_locator('json_encoder.normalizer'), + tagged_locator('json_encoder.value_transformer'), service('type_info.resolver'), ]) @@ -86,23 +86,16 @@ ->decorate('json_encoder.decode.property_metadata_loader') ->args([ service('.inner'), - tagged_locator('json_encoder.normalizer'), + tagged_locator('json_encoder.value_transformer'), service('type_info.resolver'), ]) - // normalizers/denormalizers - ->set('json_encoder.normalizer.date_time', DateTimeNormalizer::class) - ->tag('json_encoder.normalizer') - ->set('json_encoder.denormalizer.date_time', DateTimeDenormalizer::class) - ->args([ - false, - ]) - ->tag('json_encoder.denormalizer') - ->set('json_encoder.denormalizer.date_time_immutable', DateTimeDenormalizer::class) - ->args([ - true, - ]) - ->tag('json_encoder.denormalizer') + // value transformers + ->set('json_encoder.value_transformer.date_time_to_string', DateTimeToStringValueTransformer::class) + ->tag('json_encoder.value_transformer') + + ->set('json_encoder.value_transformer.string_to_date_time', StringToDateTimeValueTransformer::class) + ->tag('json_encoder.value_transformer') // cache ->set('.json_encoder.cache_warmer.encoder_decoder', EncoderDecoderCacheWarmer::class) diff --git a/Tests/Functional/app/JsonEncoder/Dto/Dummy.php b/Tests/Functional/app/JsonEncoder/Dto/Dummy.php index 8610de049..a6b4d8e91 100644 --- a/Tests/Functional/app/JsonEncoder/Dto/Dummy.php +++ b/Tests/Functional/app/JsonEncoder/Dto/Dummy.php @@ -11,11 +11,11 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto; -use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer; -use Symfony\Component\JsonEncoder\Attribute\Denormalizer; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeToStringValueTransformer; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\StringToRangeValueTransformer; use Symfony\Component\JsonEncoder\Attribute\EncodedName; use Symfony\Component\JsonEncoder\Attribute\JsonEncodable; -use Symfony\Component\JsonEncoder\Attribute\Normalizer; +use Symfony\Component\JsonEncoder\Attribute\ValueTransformer; /** * @author Mathias Arlaud @@ -24,11 +24,9 @@ class Dummy { #[EncodedName('@name')] - #[Normalizer('strtoupper')] - #[Denormalizer('strtolower')] + #[ValueTransformer(toJsonValue: 'strtoupper', toNativeValue: 'strtolower')] public string $name = 'dummy'; - #[Normalizer(RangeNormalizer::class)] - #[Denormalizer(RangeNormalizer::class)] + #[ValueTransformer(toJsonValue: RangeToStringValueTransformer::class, toNativeValue: StringToRangeValueTransformer::class)] public array $range = [10, 20]; } diff --git a/Tests/Functional/app/JsonEncoder/RangeToStringValueTransformer.php b/Tests/Functional/app/JsonEncoder/RangeToStringValueTransformer.php new file mode 100644 index 000000000..93be1fad9 --- /dev/null +++ b/Tests/Functional/app/JsonEncoder/RangeToStringValueTransformer.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder; + +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; +use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\Type\BuiltinType; + +/** + * @author Mathias Arlaud + */ +class RangeToStringValueTransformer implements ValueTransformerInterface +{ + public function transform(mixed $value, array $options = []): string + { + return $value[0].'..'.$value[1]; + } + + public static function getJsonValueType(): BuiltinType + { + return Type::string(); + } +} diff --git a/Tests/Functional/app/JsonEncoder/RangeNormalizer.php b/Tests/Functional/app/JsonEncoder/StringToRangeValueTransformer.php similarity index 51% rename from Tests/Functional/app/JsonEncoder/RangeNormalizer.php rename to Tests/Functional/app/JsonEncoder/StringToRangeValueTransformer.php index beb9e8188..46e3dd305 100644 --- a/Tests/Functional/app/JsonEncoder/RangeNormalizer.php +++ b/Tests/Functional/app/JsonEncoder/StringToRangeValueTransformer.php @@ -11,27 +11,21 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder; -use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface; -use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface; +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\Type\BuiltinType; /** * @author Mathias Arlaud */ -class RangeNormalizer implements NormalizerInterface, DenormalizerInterface +class StringToRangeValueTransformer implements ValueTransformerInterface { - public function normalize(mixed $denormalized, array $options = []): string + public function transform(mixed $value, array $options = []): array { - return $denormalized[0].'..'.$denormalized[1]; + return array_map(static fn (string $v): int => (int) $v, explode('..', $value)); } - public function denormalize(mixed $normalized, array $options = []): array - { - return array_map(static fn (string $v): int => (int) $v, explode('..', $normalized)); - } - - public static function getNormalizedType(): BuiltinType + public static function getJsonValueType(): BuiltinType { return Type::string(); } diff --git a/Tests/Functional/app/JsonEncoder/config.yml b/Tests/Functional/app/JsonEncoder/config.yml index 13b68adef..5eb5d4996 100644 --- a/Tests/Functional/app/JsonEncoder/config.yml +++ b/Tests/Functional/app/JsonEncoder/config.yml @@ -23,4 +23,5 @@ services: public: true Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\Dummy: ~ - Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer: ~ + Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\StringToRangeValueTransformer: ~ + Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeToStringValueTransformer: ~ From 81758d2301826192269b9b46ecc9236ffcbf2075 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 20 Feb 2025 09:13:24 +0100 Subject: [PATCH 217/281] [Framework] Deprecate the `framework.validation.cache` config option --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55eeaf1fd..3b9873591 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ CHANGELOG * Add `RateLimiterFactoryInterface` as an alias of the `limiter` service * Add `framework.validation.disable_translation` option * Add support for signal plain name in the `messenger.stop_worker_on_signals` configuration + * Deprecate the `framework.validation.cache` option 7.2 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index f5279c419..ca20f15a4 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1034,7 +1034,9 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e ->info('Validation configuration') ->{$enableIfStandalone('symfony/validator', Validation::class)}() ->children() - ->scalarNode('cache')->end() + ->scalarNode('cache') + ->setDeprecated('symfony/framework-bundle', '7.3', 'Setting the "%path%.%node%" configuration option is deprecated. It will be removed in version 8.0.') + ->end() ->booleanNode('enable_attributes')->{class_exists(FullStack::class) ? 'defaultFalse' : 'defaultTrue'}()->end() ->arrayNode('static_method') ->defaultValue(['loadValidatorMetadata']) From 53cad5c6cfee42bb2c026b907313592ae9eefb8d Mon Sep 17 00:00:00 2001 From: Quentin Schuler Date: Fri, 21 Feb 2025 14:41:03 +0100 Subject: [PATCH 218/281] [FrameworkBundle] Disable the keys normalization of the CSRF form field attributes The form.csrf_protection.field_attr configuration node value should remain as-is when defined. The default behavior of the configuration component is to normalize keys, but in that specific cases, keys becomes HTML attributes and therefore should not be changed. This commit fix that behaviour for the specific node. --- DependencyInjection/Configuration.php | 1 + Tests/DependencyInjection/ConfigurationTest.php | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 678698f4d..4d494eed0 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -250,6 +250,7 @@ private function addFormSection(ArrayNodeDefinition $rootNode, callable $enableI ->scalarNode('field_name')->defaultValue('_token')->end() ->arrayNode('field_attr') ->performNoDeepMerging() + ->normalizeKeys(false) ->scalarPrototype()->end() ->defaultValue(['data-controller' => 'csrf-protection']) ->end() diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 53706d2e0..6f3363f39 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -699,6 +699,22 @@ public function testSerializerJsonDetailedErrorMessagesNotSetByDefaultWithDebugD $this->assertSame([], $config['serializer']['default_context'] ?? []); } + public function testFormCsrfProtectionFieldAttrDoNotNormalizeKeys() + { + $processor = new Processor(); + $config = $processor->processConfiguration(new Configuration(false), [ + [ + 'form' => [ + 'csrf_protection' => [ + 'field_attr' => ['data-example-attr' => 'value'], + ], + ], + ], + ]); + + $this->assertSame(['data-example-attr' => 'value'], $config['form']['csrf_protection']['field_attr'] ?? []); + } + protected static function getBundleDefaultConfig() { return [ From 73f2dab16365ad5ad224c4f39d2386b7c0d47fe6 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Fri, 21 Feb 2025 17:40:20 +0100 Subject: [PATCH 219/281] Refactor S/MIME encrypter to use certificate repository Replaces direct certificate path usage with a repository interface for managing S/MIME certificates. This improves flexibility by allowing custom certificate retrieval logic through `SmimeCertificateRepositoryInterface`. Adjusted related tests, configuration, and event listener implementation accordingly. --- DependencyInjection/Configuration.php | 4 ++-- DependencyInjection/FrameworkExtension.php | 6 ++---- Resources/config/mailer.php | 10 ++-------- Resources/config/schema/symfony-1.0.xsd | 2 +- Tests/DependencyInjection/ConfigurationTest.php | 2 +- 5 files changed, 8 insertions(+), 16 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index ca20f15a4..7507ae392 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -2314,8 +2314,8 @@ private function addMailerSection(ArrayNodeDefinition $rootNode, callable $enabl ->canBeEnabled() ->info('S/MIME encrypter configuration') ->children() - ->scalarNode('certificate') - ->info('Path to certificate (in PEM format without the `file://` prefix)') + ->scalarNode('repository') + ->info('Path to the S/MIME certificate repository. Shall implement the `Symfony\Component\Mailer\EventListener\SmimeCertificateRepositoryInterface`.') ->defaultValue('') ->cannotBeEmpty() ->end() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 4c36bf4dc..331005d63 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2893,11 +2893,9 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co if (!class_exists(SmimeEncryptedMessageListener::class)) { throw new LogicException('S/MIME encrypted messages support cannot be enabled as this version of the Mailer component does not support it.'); } - $smimeDecrypter = $container->getDefinition('mailer.smime_encrypter'); - $smimeDecrypter->setArgument(0, $config['smime_encrypter']['certificate']); - $smimeDecrypter->setArgument(1, $config['smime_encrypter']['cipher']); + $container->setAlias('mailer.smime_encrypter.repository', $config['smime_encrypter']['repository']); + $container->setParameter('mailer.smime_encrypter.cipher', $config['smime_encrypter']['cipher']); } else { - $container->removeDefinition('mailer.smime_encrypter'); $container->removeDefinition('mailer.smime_encrypter.listener'); } diff --git a/Resources/config/mailer.php b/Resources/config/mailer.php index 25b3fefdb..71a43b9c8 100644 --- a/Resources/config/mailer.php +++ b/Resources/config/mailer.php @@ -26,7 +26,6 @@ use Symfony\Component\Mailer\Transport\TransportInterface; use Symfony\Component\Mailer\Transport\Transports; use Symfony\Component\Mime\Crypto\DkimSigner; -use Symfony\Component\Mime\Crypto\SMimeEncrypter; use Symfony\Component\Mime\Crypto\SMimeSigner; return static function (ContainerConfigurator $container) { @@ -102,12 +101,6 @@ abstract_arg('signOptions'), ]) - ->set('mailer.smime_encrypter', SMimeEncrypter::class) - ->args([ - abstract_arg('certificate'), - abstract_arg('cipher'), - ]) - ->set('mailer.dkim_signer.listener', DkimSignedMessageListener::class) ->args([ service(DkimSigner::class), @@ -122,7 +115,8 @@ ->set('mailer.smime_encrypter.listener', SmimeEncryptedMessageListener::class) ->args([ - service('mailer.smime_encrypter'), + service('mailer.smime_encrypter.repository'), + param('mailer.smime_encrypter.cipher'), ]) ->tag('kernel.event_subscriber') diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index b47de331a..d961ca97c 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -836,7 +836,7 @@ - + diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 607049274..c4b273dc8 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -941,7 +941,7 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor ], 'smime_encrypter' => [ 'enabled' => false, - 'certificate' => '', + 'repository' => '', 'cipher' => null, ], ], From 322f4966e013f27368fa375ba95e6f70a8b3abf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?El=C3=ADas=20Fern=C3=A1ndez?= Date: Tue, 25 Feb 2025 17:20:33 +0100 Subject: [PATCH 220/281] bug #59854 Fix mailer signer configuration issues --- DependencyInjection/FrameworkExtension.php | 4 ++-- Resources/config/mailer.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 4c36bf4dc..4d43f2089 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2879,8 +2879,8 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co throw new LogicException('SMIME signed messages support cannot be enabled as this version of the Mailer component does not support it.'); } $smimeSigner = $container->getDefinition('mailer.smime_signer'); - $smimeSigner->setArgument(0, $config['smime_signer']['key']); - $smimeSigner->setArgument(1, $config['smime_signer']['certificate']); + $smimeSigner->setArgument(0, $config['smime_signer']['certificate']); + $smimeSigner->setArgument(1, $config['smime_signer']['key']); $smimeSigner->setArgument(2, $config['smime_signer']['passphrase']); $smimeSigner->setArgument(3, $config['smime_signer']['extra_certificates']); $smimeSigner->setArgument(4, $config['smime_signer']['sign_options']); diff --git a/Resources/config/mailer.php b/Resources/config/mailer.php index 25b3fefdb..e7d2f98fa 100644 --- a/Resources/config/mailer.php +++ b/Resources/config/mailer.php @@ -95,8 +95,8 @@ ->set('mailer.smime_signer', SMimeSigner::class) ->args([ - abstract_arg('key'), abstract_arg('certificate'), + abstract_arg('key'), abstract_arg('passphrase'), abstract_arg('extraCertificates'), abstract_arg('signOptions'), @@ -110,7 +110,7 @@ ->set('mailer.dkim_signer.listener', DkimSignedMessageListener::class) ->args([ - service(DkimSigner::class), + service('mailer.dkim_signer'), ]) ->tag('kernel.event_subscriber') From 70bc1b3ed44ebb96271befbd2ab13e212e637367 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Tue, 25 Feb 2025 14:53:52 +0100 Subject: [PATCH 221/281] [JsonStreamer] Rename the component --- CHANGELOG.md | 2 +- .../Compiler/UnusedTagsPass.php | 2 +- DependencyInjection/Configuration.php | 12 +- DependencyInjection/FrameworkExtension.php | 42 +++---- FrameworkBundle.php | 4 +- Resources/config/json_encoder.php | 119 ------------------ Resources/config/json_streamer.php | 119 ++++++++++++++++++ Resources/config/schema/symfony-1.0.xsd | 4 +- .../DependencyInjection/ConfigurationTest.php | 6 +- .../{json_encoder.php => json_streamer.php} | 2 +- .../DependencyInjection/Fixtures/xml/full.xml | 2 +- .../{json_encoder.xml => json_streamer.xml} | 2 +- .../DependencyInjection/Fixtures/yml/full.yml | 2 +- .../{json_encoder.yml => json_streamer.yml} | 2 +- .../FrameworkExtensionTestCase.php | 6 +- Tests/Functional/JsonEncoderTest.php | 67 ---------- Tests/Functional/JsonStreamerTest.php | 67 ++++++++++ .../Functional/app/JsonEncoder/Dto/Dummy.php | 32 ----- Tests/Functional/app/JsonEncoder/config.yml | 27 ---- .../Functional/app/JsonStreamer/Dto/Dummy.php | 38 ++++++ .../RangeToStringValueTransformer.php | 6 +- .../StringToRangeValueTransformer.php | 6 +- .../{JsonEncoder => JsonStreamer}/bundles.php | 0 Tests/Functional/app/JsonStreamer/config.yml | 27 ++++ composer.json | 4 +- 25 files changed, 303 insertions(+), 297 deletions(-) delete mode 100644 Resources/config/json_encoder.php create mode 100644 Resources/config/json_streamer.php rename Tests/DependencyInjection/Fixtures/php/{json_encoder.php => json_streamer.php} (91%) rename Tests/DependencyInjection/Fixtures/xml/{json_encoder.xml => json_streamer.xml} (93%) rename Tests/DependencyInjection/Fixtures/yml/{json_encoder.yml => json_streamer.yml} (90%) delete mode 100644 Tests/Functional/JsonEncoderTest.php create mode 100644 Tests/Functional/JsonStreamerTest.php delete mode 100644 Tests/Functional/app/JsonEncoder/Dto/Dummy.php delete mode 100644 Tests/Functional/app/JsonEncoder/config.yml create mode 100644 Tests/Functional/app/JsonStreamer/Dto/Dummy.php rename Tests/Functional/app/{JsonEncoder => JsonStreamer}/RangeToStringValueTransformer.php (82%) rename Tests/Functional/app/{JsonEncoder => JsonStreamer}/StringToRangeValueTransformer.php (83%) rename Tests/Functional/app/{JsonEncoder => JsonStreamer}/bundles.php (100%) create mode 100644 Tests/Functional/app/JsonStreamer/config.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b9873591..17201aeae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ CHANGELOG * Add support for assets pre-compression * Rename `TranslationUpdateCommand` to `TranslationExtractCommand` - * Add JsonEncoder services and configuration + * Add JsonStreamer services and configuration * Add new `framework.property_info.with_constructor_extractor` option to allow enabling or disabling the constructor extractor integration * Deprecate the `--show-arguments` option of the `container:debug` command, as arguments are now always shown * Add `RateLimiterFactoryInterface` as an alias of the `limiter` service diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index c5191a659..c8271d463 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -53,7 +53,7 @@ class UnusedTagsPass implements CompilerPassInterface 'form.type_guesser', 'html_sanitizer', 'http_client.client', - 'json_encoder.value_transformer', + 'json_streamer.value_transformer', 'kernel.cache_clearer', 'kernel.cache_warmer', 'kernel.event_listener', diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index d4bf35257..ce77b50f8 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -31,7 +31,7 @@ use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\IpUtils; -use Symfony\Component\JsonEncoder\EncoderInterface; +use Symfony\Component\JsonStreamer\StreamWriterInterface; use Symfony\Component\Lock\Lock; use Symfony\Component\Lock\Store\SemaphoreStore; use Symfony\Component\Mailer\Mailer; @@ -182,7 +182,7 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addHtmlSanitizerSection($rootNode, $enableIfStandalone); $this->addWebhookSection($rootNode, $enableIfStandalone); $this->addRemoteEventSection($rootNode, $enableIfStandalone); - $this->addJsonEncoderSection($rootNode, $enableIfStandalone); + $this->addJsonStreamerSection($rootNode, $enableIfStandalone); return $treeBuilder; } @@ -2692,13 +2692,13 @@ private function addHtmlSanitizerSection(ArrayNodeDefinition $rootNode, callable ; } - private function addJsonEncoderSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone): void + private function addJsonStreamerSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone): void { $rootNode ->children() - ->arrayNode('json_encoder') - ->info('JSON encoder configuration') - ->{$enableIfStandalone('symfony/json-encoder', EncoderInterface::class)}() + ->arrayNode('json_streamer') + ->info('JSON streamer configuration') + ->{$enableIfStandalone('symfony/json-streamer', StreamWriterInterface::class)}() ->end() ->end() ; diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 73024eaf8..6be0e2be7 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -100,11 +100,11 @@ use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator; -use Symfony\Component\JsonEncoder\Attribute\JsonEncodable; -use Symfony\Component\JsonEncoder\DecoderInterface as JsonEncoderDecoderInterface; -use Symfony\Component\JsonEncoder\EncoderInterface as JsonEncoderEncoderInterface; -use Symfony\Component\JsonEncoder\JsonEncoder; -use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; +use Symfony\Component\JsonStreamer\Attribute\JsonStreamable; +use Symfony\Component\JsonStreamer\JsonStreamWriter; +use Symfony\Component\JsonStreamer\StreamReaderInterface; +use Symfony\Component\JsonStreamer\StreamWriterInterface; +use Symfony\Component\JsonStreamer\ValueTransformer\ValueTransformerInterface; use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\LockInterface; use Symfony\Component\Lock\PersistingStoreInterface; @@ -436,12 +436,12 @@ public function load(array $configs, ContainerBuilder $container): void $this->registerPropertyInfoConfiguration($config['property_info'], $container, $loader); } - if ($this->readConfigEnabled('json_encoder', $container, $config['json_encoder'])) { + if ($this->readConfigEnabled('json_streamer', $container, $config['json_streamer'])) { if (!$typeInfoEnabled) { - throw new LogicException('JsonEncoder support cannot be enabled as the TypeInfo component is not '.(interface_exists(TypeResolverInterface::class) ? 'enabled.' : 'installed. Try running "composer require symfony/type-info".')); + throw new LogicException('JsonStreamer support cannot be enabled as the TypeInfo component is not '.(interface_exists(TypeResolverInterface::class) ? 'enabled.' : 'installed. Try running "composer require symfony/type-info".')); } - $this->registerJsonEncoderConfiguration($config['json_encoder'], $container, $loader); + $this->registerJsonStreamerConfiguration($config['json_streamer'], $container, $loader); } if ($this->readConfigEnabled('lock', $container, $config['lock'])) { @@ -755,8 +755,8 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu } ); } - $container->registerAttributeForAutoconfiguration(JsonEncodable::class, static function (ChildDefinition $definition, JsonEncodable $attribute): void { - $definition->addTag('json_encoder.encodable', [ + $container->registerAttributeForAutoconfiguration(JsonStreamable::class, static function (ChildDefinition $definition, JsonStreamable $attribute): void { + $definition->addTag('json_streamer.streamable', [ 'object' => $attribute->asObject, 'list' => $attribute->asList, ]); @@ -2033,26 +2033,26 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->setParameter('.serializer.named_serializers', $config['named_serializers'] ?? []); } - private function registerJsonEncoderConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void + private function registerJsonStreamerConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void { - if (!class_exists(JsonEncoder::class)) { - throw new LogicException('JsonEncoder support cannot be enabled as the JsonEncoder component is not installed. Try running "composer require symfony/json-encoder".'); + if (!class_exists(JsonStreamWriter::class)) { + throw new LogicException('JsonStreamer support cannot be enabled as the JsonStreamer component is not installed. Try running "composer require symfony/json-streamer".'); } $container->registerForAutoconfiguration(ValueTransformerInterface::class) - ->addTag('json_encoder.value_transformer'); + ->addTag('json_streamer.value_transformer'); - $loader->load('json_encoder.php'); + $loader->load('json_streamer.php'); - $container->registerAliasForArgument('json_encoder.encoder', JsonEncoderEncoderInterface::class, 'json.encoder'); - $container->registerAliasForArgument('json_encoder.decoder', JsonEncoderDecoderInterface::class, 'json.decoder'); + $container->registerAliasForArgument('json_streamer.stream_writer', StreamWriterInterface::class, 'json.stream_writer'); + $container->registerAliasForArgument('json_streamer.stream_reader', StreamReaderInterface::class, 'json.stream_reader'); - $container->setParameter('.json_encoder.encoders_dir', '%kernel.cache_dir%/json_encoder/encoder'); - $container->setParameter('.json_encoder.decoders_dir', '%kernel.cache_dir%/json_encoder/decoder'); - $container->setParameter('.json_encoder.lazy_ghosts_dir', '%kernel.cache_dir%/json_encoder/lazy_ghost'); + $container->setParameter('.json_streamer.stream_writers_dir', '%kernel.cache_dir%/json_streamer/stream_writer'); + $container->setParameter('.json_streamer.stream_readers_dir', '%kernel.cache_dir%/json_streamer/stream_reader'); + $container->setParameter('.json_streamer.lazy_ghosts_dir', '%kernel.cache_dir%/json_streamer/lazy_ghost'); if (\PHP_VERSION_ID >= 80400) { - $container->removeDefinition('.json_encoder.cache_warmer.lazy_ghost'); + $container->removeDefinition('.json_streamer.cache_warmer.lazy_ghost'); } } diff --git a/FrameworkBundle.php b/FrameworkBundle.php index 89d9744f5..faf2841f4 100644 --- a/FrameworkBundle.php +++ b/FrameworkBundle.php @@ -54,7 +54,7 @@ use Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass; use Symfony\Component\HttpKernel\DependencyInjection\ResettableServicePass; use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\JsonEncoder\DependencyInjection\EncodablePass; +use Symfony\Component\JsonStreamer\DependencyInjection\StreamablePass; use Symfony\Component\Messenger\DependencyInjection\MessengerPass; use Symfony\Component\Mime\DependencyInjection\AddMimeTypeGuesserPass; use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoConstructorPass; @@ -189,7 +189,7 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new ErrorLoggerCompilerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); $container->addCompilerPass(new VirtualRequestStackPass()); $container->addCompilerPass(new TranslationUpdateCommandPass(), PassConfig::TYPE_BEFORE_REMOVING); - $this->addCompilerPassIfExists($container, EncodablePass::class); + $this->addCompilerPassIfExists($container, StreamablePass::class); if ($container->getParameter('kernel.debug')) { $container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 2); diff --git a/Resources/config/json_encoder.php b/Resources/config/json_encoder.php deleted file mode 100644 index f6fbd05e6..000000000 --- a/Resources/config/json_encoder.php +++ /dev/null @@ -1,119 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Loader\Configurator; - -use Symfony\Component\JsonEncoder\CacheWarmer\EncoderDecoderCacheWarmer; -use Symfony\Component\JsonEncoder\CacheWarmer\LazyGhostCacheWarmer; -use Symfony\Component\JsonEncoder\JsonDecoder; -use Symfony\Component\JsonEncoder\JsonEncoder; -use Symfony\Component\JsonEncoder\Mapping\Decode\AttributePropertyMetadataLoader as DecodeAttributePropertyMetadataLoader; -use Symfony\Component\JsonEncoder\Mapping\Decode\DateTimeTypePropertyMetadataLoader as DecodeDateTimeTypePropertyMetadataLoader; -use Symfony\Component\JsonEncoder\Mapping\Encode\AttributePropertyMetadataLoader as EncodeAttributePropertyMetadataLoader; -use Symfony\Component\JsonEncoder\Mapping\Encode\DateTimeTypePropertyMetadataLoader as EncodeDateTimeTypePropertyMetadataLoader; -use Symfony\Component\JsonEncoder\Mapping\GenericTypePropertyMetadataLoader; -use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader; -use Symfony\Component\JsonEncoder\ValueTransformer\DateTimeToStringValueTransformer; -use Symfony\Component\JsonEncoder\ValueTransformer\StringToDateTimeValueTransformer; - -return static function (ContainerConfigurator $container) { - $container->services() - // encoder/decoder - ->set('json_encoder.encoder', JsonEncoder::class) - ->args([ - tagged_locator('json_encoder.value_transformer'), - service('json_encoder.encode.property_metadata_loader'), - param('.json_encoder.encoders_dir'), - ]) - ->set('json_encoder.decoder', JsonDecoder::class) - ->args([ - tagged_locator('json_encoder.value_transformer'), - service('json_encoder.decode.property_metadata_loader'), - param('.json_encoder.decoders_dir'), - param('.json_encoder.lazy_ghosts_dir'), - ]) - ->alias(JsonEncoder::class, 'json_encoder.encoder') - ->alias(JsonDecoder::class, 'json_encoder.decoder') - - // metadata - ->set('json_encoder.encode.property_metadata_loader', PropertyMetadataLoader::class) - ->args([ - service('type_info.resolver'), - ]) - ->set('.json_encoder.encode.property_metadata_loader.generic', GenericTypePropertyMetadataLoader::class) - ->decorate('json_encoder.encode.property_metadata_loader') - ->args([ - service('.inner'), - service('type_info.type_context_factory'), - ]) - ->set('.json_encoder.encode.property_metadata_loader.date_time', EncodeDateTimeTypePropertyMetadataLoader::class) - ->decorate('json_encoder.encode.property_metadata_loader') - ->args([ - service('.inner'), - ]) - ->set('.json_encoder.encode.property_metadata_loader.attribute', EncodeAttributePropertyMetadataLoader::class) - ->decorate('json_encoder.encode.property_metadata_loader') - ->args([ - service('.inner'), - tagged_locator('json_encoder.value_transformer'), - service('type_info.resolver'), - ]) - - ->set('json_encoder.decode.property_metadata_loader', PropertyMetadataLoader::class) - ->args([ - service('type_info.resolver'), - ]) - ->set('.json_encoder.decode.property_metadata_loader.generic', GenericTypePropertyMetadataLoader::class) - ->decorate('json_encoder.decode.property_metadata_loader') - ->args([ - service('.inner'), - service('type_info.type_context_factory'), - ]) - ->set('.json_encoder.decode.property_metadata_loader.date_time', DecodeDateTimeTypePropertyMetadataLoader::class) - ->decorate('json_encoder.decode.property_metadata_loader') - ->args([ - service('.inner'), - ]) - ->set('.json_encoder.decode.property_metadata_loader.attribute', DecodeAttributePropertyMetadataLoader::class) - ->decorate('json_encoder.decode.property_metadata_loader') - ->args([ - service('.inner'), - tagged_locator('json_encoder.value_transformer'), - service('type_info.resolver'), - ]) - - // value transformers - ->set('json_encoder.value_transformer.date_time_to_string', DateTimeToStringValueTransformer::class) - ->tag('json_encoder.value_transformer') - - ->set('json_encoder.value_transformer.string_to_date_time', StringToDateTimeValueTransformer::class) - ->tag('json_encoder.value_transformer') - - // cache - ->set('.json_encoder.cache_warmer.encoder_decoder', EncoderDecoderCacheWarmer::class) - ->args([ - abstract_arg('encodable class names'), - service('json_encoder.encode.property_metadata_loader'), - service('json_encoder.decode.property_metadata_loader'), - param('.json_encoder.encoders_dir'), - param('.json_encoder.decoders_dir'), - service('logger')->ignoreOnInvalid(), - ]) - ->tag('kernel.cache_warmer') - - ->set('.json_encoder.cache_warmer.lazy_ghost', LazyGhostCacheWarmer::class) - ->args([ - abstract_arg('encodable class names'), - param('.json_encoder.lazy_ghosts_dir'), - ]) - ->tag('kernel.cache_warmer') - ; -}; diff --git a/Resources/config/json_streamer.php b/Resources/config/json_streamer.php new file mode 100644 index 000000000..79fb25833 --- /dev/null +++ b/Resources/config/json_streamer.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\JsonStreamer\CacheWarmer\LazyGhostCacheWarmer; +use Symfony\Component\JsonStreamer\CacheWarmer\StreamerCacheWarmer; +use Symfony\Component\JsonStreamer\JsonStreamReader; +use Symfony\Component\JsonStreamer\JsonStreamWriter; +use Symfony\Component\JsonStreamer\Mapping\GenericTypePropertyMetadataLoader; +use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoader; +use Symfony\Component\JsonStreamer\Mapping\Read\AttributePropertyMetadataLoader as ReadAttributePropertyMetadataLoader; +use Symfony\Component\JsonStreamer\Mapping\Read\DateTimeTypePropertyMetadataLoader as ReadDateTimeTypePropertyMetadataLoader; +use Symfony\Component\JsonStreamer\Mapping\Write\AttributePropertyMetadataLoader as WriteAttributePropertyMetadataLoader; +use Symfony\Component\JsonStreamer\Mapping\Write\DateTimeTypePropertyMetadataLoader as WriteDateTimeTypePropertyMetadataLoader; +use Symfony\Component\JsonStreamer\ValueTransformer\DateTimeToStringValueTransformer; +use Symfony\Component\JsonStreamer\ValueTransformer\StringToDateTimeValueTransformer; + +return static function (ContainerConfigurator $container) { + $container->services() + // stream reader/writer + ->set('json_streamer.stream_writer', JsonStreamWriter::class) + ->args([ + tagged_locator('json_streamer.value_transformer'), + service('json_streamer.write.property_metadata_loader'), + param('.json_streamer.stream_writers_dir'), + ]) + ->set('json_streamer.stream_reader', JsonStreamReader::class) + ->args([ + tagged_locator('json_streamer.value_transformer'), + service('json_streamer.read.property_metadata_loader'), + param('.json_streamer.stream_readers_dir'), + param('.json_streamer.lazy_ghosts_dir'), + ]) + ->alias(JsonStreamWriter::class, 'json_streamer.stream_writer') + ->alias(JsonStreamReader::class, 'json_streamer.stream_reader') + + // metadata + ->set('json_streamer.write.property_metadata_loader', PropertyMetadataLoader::class) + ->args([ + service('type_info.resolver'), + ]) + ->set('.json_streamer.write.property_metadata_loader.generic', GenericTypePropertyMetadataLoader::class) + ->decorate('json_streamer.write.property_metadata_loader') + ->args([ + service('.inner'), + service('type_info.type_context_factory'), + ]) + ->set('.json_streamer.write.property_metadata_loader.date_time', WriteDateTimeTypePropertyMetadataLoader::class) + ->decorate('json_streamer.write.property_metadata_loader') + ->args([ + service('.inner'), + ]) + ->set('.json_streamer.write.property_metadata_loader.attribute', WriteAttributePropertyMetadataLoader::class) + ->decorate('json_streamer.write.property_metadata_loader') + ->args([ + service('.inner'), + tagged_locator('json_streamer.value_transformer'), + service('type_info.resolver'), + ]) + + ->set('json_streamer.read.property_metadata_loader', PropertyMetadataLoader::class) + ->args([ + service('type_info.resolver'), + ]) + ->set('.json_streamer.read.property_metadata_loader.generic', GenericTypePropertyMetadataLoader::class) + ->decorate('json_streamer.read.property_metadata_loader') + ->args([ + service('.inner'), + service('type_info.type_context_factory'), + ]) + ->set('.json_streamer.read.property_metadata_loader.date_time', ReadDateTimeTypePropertyMetadataLoader::class) + ->decorate('json_streamer.read.property_metadata_loader') + ->args([ + service('.inner'), + ]) + ->set('.json_streamer.read.property_metadata_loader.attribute', ReadAttributePropertyMetadataLoader::class) + ->decorate('json_streamer.read.property_metadata_loader') + ->args([ + service('.inner'), + tagged_locator('json_streamer.value_transformer'), + service('type_info.resolver'), + ]) + + // value transformers + ->set('json_streamer.value_transformer.date_time_to_string', DateTimeToStringValueTransformer::class) + ->tag('json_streamer.value_transformer') + + ->set('json_streamer.value_transformer.string_to_date_time', StringToDateTimeValueTransformer::class) + ->tag('json_streamer.value_transformer') + + // cache + ->set('.json_streamer.cache_warmer.streamer', StreamerCacheWarmer::class) + ->args([ + abstract_arg('streamable'), + service('json_streamer.write.property_metadata_loader'), + service('json_streamer.read.property_metadata_loader'), + param('.json_streamer.stream_writers_dir'), + param('.json_streamer.stream_readers_dir'), + service('logger')->ignoreOnInvalid(), + ]) + ->tag('kernel.cache_warmer') + + ->set('.json_streamer.cache_warmer.lazy_ghost', LazyGhostCacheWarmer::class) + ->args([ + abstract_arg('streamable class names'), + param('.json_streamer.lazy_ghosts_dir'), + ]) + ->tag('kernel.cache_warmer') + ; +}; diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index b47de331a..ae69a5aa9 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -46,7 +46,7 @@ - + @@ -1041,7 +1041,7 @@ - + diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 6dbb0f0e3..6842cf9e9 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -22,7 +22,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HtmlSanitizer\HtmlSanitizer; use Symfony\Component\HttpClient\HttpClient; -use Symfony\Component\JsonEncoder\JsonEncoder; +use Symfony\Component\JsonStreamer\JsonStreamWriter; use Symfony\Component\Lock\Store\SemaphoreStore; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Messenger\MessageBusInterface; @@ -1009,8 +1009,8 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor 'remote-event' => [ 'enabled' => !class_exists(FullStack::class) && class_exists(RemoteEvent::class), ], - 'json_encoder' => [ - 'enabled' => !class_exists(FullStack::class) && class_exists(JsonEncoder::class), + 'json_streamer' => [ + 'enabled' => !class_exists(FullStack::class) && class_exists(JsonStreamWriter::class), ], ]; } diff --git a/Tests/DependencyInjection/Fixtures/php/json_encoder.php b/Tests/DependencyInjection/Fixtures/php/json_streamer.php similarity index 91% rename from Tests/DependencyInjection/Fixtures/php/json_encoder.php rename to Tests/DependencyInjection/Fixtures/php/json_streamer.php index 42204b2cb..844b72004 100644 --- a/Tests/DependencyInjection/Fixtures/php/json_encoder.php +++ b/Tests/DependencyInjection/Fixtures/php/json_streamer.php @@ -8,7 +8,7 @@ 'type_info' => [ 'enabled' => true, ], - 'json_encoder' => [ + 'json_streamer' => [ 'enabled' => true, ], ]); diff --git a/Tests/DependencyInjection/Fixtures/xml/full.xml b/Tests/DependencyInjection/Fixtures/xml/full.xml index 23d325e61..07faf22ab 100644 --- a/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -46,6 +46,6 @@ - + diff --git a/Tests/DependencyInjection/Fixtures/xml/json_encoder.xml b/Tests/DependencyInjection/Fixtures/xml/json_streamer.xml similarity index 93% rename from Tests/DependencyInjection/Fixtures/xml/json_encoder.xml rename to Tests/DependencyInjection/Fixtures/xml/json_streamer.xml index a20f98567..5c79cb840 100644 --- a/Tests/DependencyInjection/Fixtures/xml/json_encoder.xml +++ b/Tests/DependencyInjection/Fixtures/xml/json_streamer.xml @@ -9,6 +9,6 @@ - + diff --git a/Tests/DependencyInjection/Fixtures/yml/full.yml b/Tests/DependencyInjection/Fixtures/yml/full.yml index 28c4336d9..8a1a3834b 100644 --- a/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -71,4 +71,4 @@ framework: formats: csv: ['text/csv', 'text/plain'] pdf: 'application/pdf' - json_encoder: ~ + json_streamer: ~ diff --git a/Tests/DependencyInjection/Fixtures/yml/json_encoder.yml b/Tests/DependencyInjection/Fixtures/yml/json_streamer.yml similarity index 90% rename from Tests/DependencyInjection/Fixtures/yml/json_encoder.yml rename to Tests/DependencyInjection/Fixtures/yml/json_streamer.yml index e09f7c7d3..8873fea97 100644 --- a/Tests/DependencyInjection/Fixtures/yml/json_encoder.yml +++ b/Tests/DependencyInjection/Fixtures/yml/json_streamer.yml @@ -6,5 +6,5 @@ framework: log: true type_info: enabled: true - json_encoder: + json_streamer: enabled: true diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index d48dd6ad6..8581e4c8b 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -2551,10 +2551,10 @@ public function testSemaphoreWithService() self::assertEquals(new Reference('my_service'), $storeDef->getArgument(0)); } - public function testJsonEncoderEnabled() + public function testJsonStreamerEnabled() { - $container = $this->createContainerFromFile('json_encoder'); - $this->assertTrue($container->has('json_encoder.encoder')); + $container = $this->createContainerFromFile('json_streamer'); + $this->assertTrue($container->has('json_streamer.stream_writer')); } protected function createContainer(array $data = []) diff --git a/Tests/Functional/JsonEncoderTest.php b/Tests/Functional/JsonEncoderTest.php deleted file mode 100644 index b5410e1e1..000000000 --- a/Tests/Functional/JsonEncoderTest.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; - -use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\Dummy; -use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\JsonEncoder\DecoderInterface; -use Symfony\Component\JsonEncoder\EncoderInterface; -use Symfony\Component\TypeInfo\Type; - -/** - * @author Mathias Arlaud - */ -class JsonEncoderTest extends AbstractWebTestCase -{ - protected function setUp(): void - { - static::bootKernel(['test_case' => 'JsonEncoder']); - } - - public function testEncode() - { - /** @var EncoderInterface $encoder */ - $encoder = static::getContainer()->get('json_encoder.encoder.alias'); - - $this->assertSame('{"@name":"DUMMY","range":"10..20"}', (string) $encoder->encode(new Dummy(), Type::object(Dummy::class))); - } - - public function testDecode() - { - /** @var DecoderInterface $decoder */ - $decoder = static::getContainer()->get('json_encoder.decoder.alias'); - - $expected = new Dummy(); - $expected->name = 'dummy'; - $expected->range = [0, 1]; - - $this->assertEquals($expected, $decoder->decode('{"@name": "DUMMY", "range": "0..1"}', Type::object(Dummy::class))); - } - - public function testWarmupEncodableClasses() - { - /** @var Filesystem $fs */ - $fs = static::getContainer()->get('filesystem'); - - $encodersDir = \sprintf('%s/json_encoder/encoder/', static::getContainer()->getParameter('kernel.cache_dir')); - - // clear already created encoders - if ($fs->exists($encodersDir)) { - $fs->remove($encodersDir); - } - - static::getContainer()->get('json_encoder.cache_warmer.encoder_decoder.alias')->warmUp(static::getContainer()->getParameter('kernel.cache_dir')); - - $this->assertFileExists($encodersDir); - $this->assertCount(2, glob($encodersDir.'/*')); - } -} diff --git a/Tests/Functional/JsonStreamerTest.php b/Tests/Functional/JsonStreamerTest.php new file mode 100644 index 000000000..9816015b4 --- /dev/null +++ b/Tests/Functional/JsonStreamerTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonStreamer\Dto\Dummy; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\JsonStreamer\StreamReaderInterface; +use Symfony\Component\JsonStreamer\StreamWriterInterface; +use Symfony\Component\TypeInfo\Type; + +/** + * @author Mathias Arlaud + */ +class JsonStreamerTest extends AbstractWebTestCase +{ + protected function setUp(): void + { + static::bootKernel(['test_case' => 'JsonStreamer']); + } + + public function testWrite() + { + /** @var StreamWriterInterface $writer */ + $writer = static::getContainer()->get('json_streamer.stream_writer.alias'); + + $this->assertSame('{"@name":"DUMMY","range":"10..20"}', (string) $writer->write(new Dummy(), Type::object(Dummy::class))); + } + + public function testRead() + { + /** @var StreamReaderInterface $reader */ + $reader = static::getContainer()->get('json_streamer.stream_reader.alias'); + + $expected = new Dummy(); + $expected->name = 'dummy'; + $expected->range = [0, 1]; + + $this->assertEquals($expected, $reader->read('{"@name": "DUMMY", "range": "0..1"}', Type::object(Dummy::class))); + } + + public function testWarmupStreamableClasses() + { + /** @var Filesystem $fs */ + $fs = static::getContainer()->get('filesystem'); + + $streamWritersDir = \sprintf('%s/json_streamer/stream_writer/', static::getContainer()->getParameter('kernel.cache_dir')); + + // clear already created stream writers + if ($fs->exists($streamWritersDir)) { + $fs->remove($streamWritersDir); + } + + static::getContainer()->get('json_streamer.cache_warmer.streamer.alias')->warmUp(static::getContainer()->getParameter('kernel.cache_dir')); + + $this->assertFileExists($streamWritersDir); + $this->assertCount(2, glob($streamWritersDir.'/*')); + } +} diff --git a/Tests/Functional/app/JsonEncoder/Dto/Dummy.php b/Tests/Functional/app/JsonEncoder/Dto/Dummy.php deleted file mode 100644 index a6b4d8e91..000000000 --- a/Tests/Functional/app/JsonEncoder/Dto/Dummy.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto; - -use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeToStringValueTransformer; -use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\StringToRangeValueTransformer; -use Symfony\Component\JsonEncoder\Attribute\EncodedName; -use Symfony\Component\JsonEncoder\Attribute\JsonEncodable; -use Symfony\Component\JsonEncoder\Attribute\ValueTransformer; - -/** - * @author Mathias Arlaud - */ -#[JsonEncodable] -class Dummy -{ - #[EncodedName('@name')] - #[ValueTransformer(toJsonValue: 'strtoupper', toNativeValue: 'strtolower')] - public string $name = 'dummy'; - - #[ValueTransformer(toJsonValue: RangeToStringValueTransformer::class, toNativeValue: StringToRangeValueTransformer::class)] - public array $range = [10, 20]; -} diff --git a/Tests/Functional/app/JsonEncoder/config.yml b/Tests/Functional/app/JsonEncoder/config.yml deleted file mode 100644 index 5eb5d4996..000000000 --- a/Tests/Functional/app/JsonEncoder/config.yml +++ /dev/null @@ -1,27 +0,0 @@ -imports: - - { resource: ../config/default.yml } - -framework: - http_method_override: false - type_info: ~ - json_encoder: ~ - -services: - _defaults: - autoconfigure: true - - json_encoder.encoder.alias: - alias: json_encoder.encoder - public: true - - json_encoder.decoder.alias: - alias: json_encoder.decoder - public: true - - json_encoder.cache_warmer.encoder_decoder.alias: - alias: .json_encoder.cache_warmer.encoder_decoder - public: true - - Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\Dummy: ~ - Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\StringToRangeValueTransformer: ~ - Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeToStringValueTransformer: ~ diff --git a/Tests/Functional/app/JsonStreamer/Dto/Dummy.php b/Tests/Functional/app/JsonStreamer/Dto/Dummy.php new file mode 100644 index 000000000..d1f1ca67a --- /dev/null +++ b/Tests/Functional/app/JsonStreamer/Dto/Dummy.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonStreamer\Dto; + +use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonStreamer\RangeToStringValueTransformer; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonStreamer\StringToRangeValueTransformer; +use Symfony\Component\JsonStreamer\Attribute\JsonStreamable; +use Symfony\Component\JsonStreamer\Attribute\StreamedName; +use Symfony\Component\JsonStreamer\Attribute\ValueTransformer; + +/** + * @author Mathias Arlaud + */ +#[JsonStreamable] +class Dummy +{ + #[StreamedName('@name')] + #[ValueTransformer( + nativeToStream: 'strtoupper', + streamToNative: 'strtolower', + )] + public string $name = 'dummy'; + + #[ValueTransformer( + nativeToStream: RangeToStringValueTransformer::class, + streamToNative: StringToRangeValueTransformer::class, + )] + public array $range = [10, 20]; +} diff --git a/Tests/Functional/app/JsonEncoder/RangeToStringValueTransformer.php b/Tests/Functional/app/JsonStreamer/RangeToStringValueTransformer.php similarity index 82% rename from Tests/Functional/app/JsonEncoder/RangeToStringValueTransformer.php rename to Tests/Functional/app/JsonStreamer/RangeToStringValueTransformer.php index 93be1fad9..6d21f2d2f 100644 --- a/Tests/Functional/app/JsonEncoder/RangeToStringValueTransformer.php +++ b/Tests/Functional/app/JsonStreamer/RangeToStringValueTransformer.php @@ -9,9 +9,9 @@ * file that was distributed with this source code. */ -namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder; +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonStreamer; -use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; +use Symfony\Component\JsonStreamer\ValueTransformer\ValueTransformerInterface; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\Type\BuiltinType; @@ -25,7 +25,7 @@ public function transform(mixed $value, array $options = []): string return $value[0].'..'.$value[1]; } - public static function getJsonValueType(): BuiltinType + public static function getStreamValueType(): BuiltinType { return Type::string(); } diff --git a/Tests/Functional/app/JsonEncoder/StringToRangeValueTransformer.php b/Tests/Functional/app/JsonStreamer/StringToRangeValueTransformer.php similarity index 83% rename from Tests/Functional/app/JsonEncoder/StringToRangeValueTransformer.php rename to Tests/Functional/app/JsonStreamer/StringToRangeValueTransformer.php index 46e3dd305..398beb2ff 100644 --- a/Tests/Functional/app/JsonEncoder/StringToRangeValueTransformer.php +++ b/Tests/Functional/app/JsonStreamer/StringToRangeValueTransformer.php @@ -9,9 +9,9 @@ * file that was distributed with this source code. */ -namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder; +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonStreamer; -use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; +use Symfony\Component\JsonStreamer\ValueTransformer\ValueTransformerInterface; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\Type\BuiltinType; @@ -25,7 +25,7 @@ public function transform(mixed $value, array $options = []): array return array_map(static fn (string $v): int => (int) $v, explode('..', $value)); } - public static function getJsonValueType(): BuiltinType + public static function getStreamValueType(): BuiltinType { return Type::string(); } diff --git a/Tests/Functional/app/JsonEncoder/bundles.php b/Tests/Functional/app/JsonStreamer/bundles.php similarity index 100% rename from Tests/Functional/app/JsonEncoder/bundles.php rename to Tests/Functional/app/JsonStreamer/bundles.php diff --git a/Tests/Functional/app/JsonStreamer/config.yml b/Tests/Functional/app/JsonStreamer/config.yml new file mode 100644 index 000000000..188869b82 --- /dev/null +++ b/Tests/Functional/app/JsonStreamer/config.yml @@ -0,0 +1,27 @@ +imports: + - { resource: ../config/default.yml } + +framework: + http_method_override: false + type_info: ~ + json_streamer: ~ + +services: + _defaults: + autoconfigure: true + + json_streamer.stream_writer.alias: + alias: json_streamer.stream_writer + public: true + + json_streamer.stream_reader.alias: + alias: json_streamer.stream_reader + public: true + + json_streamer.cache_warmer.streamer.alias: + alias: .json_streamer.cache_warmer.streamer + public: true + + Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonStreamer\Dto\Dummy: ~ + Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonStreamer\StringToRangeValueTransformer: ~ + Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonStreamer\RangeToStringValueTransformer: ~ diff --git a/composer.json b/composer.json index 03707eea3..a39899d26 100644 --- a/composer.json +++ b/composer.json @@ -69,7 +69,7 @@ "symfony/workflow": "^6.4|^7.0", "symfony/yaml": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", - "symfony/json-encoder": "7.3.*", + "symfony/json-streamer": "7.3.*", "symfony/uid": "^6.4|^7.0", "symfony/web-link": "^6.4|^7.0", "symfony/webhook": "^7.2", @@ -88,7 +88,7 @@ "symfony/dom-crawler": "<6.4", "symfony/http-client": "<6.4", "symfony/form": "<6.4", - "symfony/json-encoder": ">=7.4", + "symfony/json-streamer": ">=7.4", "symfony/lock": "<6.4", "symfony/mailer": "<6.4", "symfony/messenger": "<6.4", From 018191a292a0ce7006b7bb26e02ee6cb07d2d313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFck=20Piera?= Date: Tue, 5 Nov 2024 15:14:54 +0100 Subject: [PATCH 222/281] [ErrorHandler] Add a command to dump static error pages --- Resources/config/console.php | 10 ++++++++++ composer.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Resources/config/console.php b/Resources/config/console.php index 7168caa4d..7ef10bb52 100644 --- a/Resources/config/console.php +++ b/Resources/config/console.php @@ -44,6 +44,7 @@ use Symfony\Component\Console\EventListener\ErrorListener; use Symfony\Component\Console\Messenger\RunCommandMessageHandler; use Symfony\Component\Dotenv\Command\DebugCommand as DotenvDebugCommand; +use Symfony\Component\ErrorHandler\Command\ErrorDumpCommand; use Symfony\Component\Messenger\Command\ConsumeMessagesCommand; use Symfony\Component\Messenger\Command\DebugCommand as MessengerDebugCommand; use Symfony\Component\Messenger\Command\FailedMessagesRemoveCommand; @@ -59,6 +60,7 @@ use Symfony\Component\Translation\Command\TranslationPushCommand; use Symfony\Component\Translation\Command\XliffLintCommand; use Symfony\Component\Validator\Command\DebugCommand as ValidatorDebugCommand; +use Symfony\WebpackEncoreBundle\Asset\EntrypointLookupInterface; return static function (ContainerConfigurator $container) { $container->services() @@ -385,6 +387,14 @@ ]) ->tag('console.command') + ->set('console.command.error_dumper', ErrorDumpCommand::class) + ->args([ + service('filesystem'), + service('error_renderer.html'), + service(EntrypointLookupInterface::class)->nullOnInvalid(), + ]) + ->tag('console.command') + ->set('console.messenger.application', Application::class) ->share(false) ->call('setAutoExit', [false]) diff --git a/composer.json b/composer.json index 03707eea3..ec7977291 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "symfony/config": "^7.3", "symfony/dependency-injection": "^7.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.4|^7.0", + "symfony/error-handler": "^7.3", "symfony/event-dispatcher": "^6.4|^7.0", "symfony/http-foundation": "^7.3", "symfony/http-kernel": "^7.2", From a4b66fbb797c2d55525f933d9f54ddb2a6578c7e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 26 Feb 2025 18:04:06 +0100 Subject: [PATCH 223/281] Add support for `valkey:` / `valkeys:` schemes --- DependencyInjection/Configuration.php | 1 + DependencyInjection/FrameworkExtension.php | 7 ++++--- Resources/config/cache.php | 2 ++ Tests/DependencyInjection/ConfigurationTest.php | 1 + 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index ce77b50f8..fd77de26e 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1285,6 +1285,7 @@ private function addCacheSection(ArrayNodeDefinition $rootNode, callable $willBe ->scalarNode('directory')->defaultValue('%kernel.cache_dir%/pools/app')->end() ->scalarNode('default_psr6_provider')->end() ->scalarNode('default_redis_provider')->defaultValue('redis://localhost')->end() + ->scalarNode('default_valkey_provider')->defaultValue('valkey://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) && class_exists(DoctrineAdapter::class) ? 'database_connection' : null)->end() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 45f4366c1..418ee739c 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2501,7 +2501,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 (['psr6', 'redis', 'memcached', 'doctrine_dbal', 'pdo'] as $name) { + foreach (['psr6', 'redis', 'valkey', '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)); } @@ -2513,12 +2513,13 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con 'tags' => false, ]; } + $redisTagAwareAdapters = [['cache.adapter.redis_tag_aware'], ['cache.adapter.valkey_tag_aware']]; foreach ($config['pools'] as $name => $pool) { $pool['adapters'] = $pool['adapters'] ?: ['cache.app']; - $isRedisTagAware = ['cache.adapter.redis_tag_aware'] === $pool['adapters']; + $isRedisTagAware = \in_array($pool['adapters'], $redisTagAwareAdapters, true); foreach ($pool['adapters'] as $provider => $adapter) { - if (($config['pools'][$adapter]['adapters'] ?? null) === ['cache.adapter.redis_tag_aware']) { + if (\in_array($config['pools'][$adapter]['adapters'] ?? null, $redisTagAwareAdapters, true)) { $isRedisTagAware = true; } elseif ($config['pools'][$adapter]['tags'] ?? false) { $pool['adapters'][$provider] = $adapter = '.'.$adapter.'.inner'; diff --git a/Resources/config/cache.php b/Resources/config/cache.php index ad4dca42d..3d96ba059 100644 --- a/Resources/config/cache.php +++ b/Resources/config/cache.php @@ -140,6 +140,7 @@ 'reset' => 'reset', ]) ->tag('monolog.logger', ['channel' => 'cache']) + ->alias('cache.adapter.valkey', 'cache.adapter.redis') ->set('cache.adapter.redis_tag_aware', RedisTagAwareAdapter::class) ->abstract() @@ -156,6 +157,7 @@ 'reset' => 'reset', ]) ->tag('monolog.logger', ['channel' => 'cache']) + ->alias('cache.adapter.valkey_tag_aware', 'cache.adapter.redis_tag_aware') ->set('cache.adapter.memcached', MemcachedAdapter::class) ->abstract() diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 6842cf9e9..e4ec77451 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -883,6 +883,7 @@ protected static function getBundleDefaultConfig() 'system' => 'cache.adapter.system', 'directory' => '%kernel.cache_dir%/pools/app', 'default_redis_provider' => 'redis://localhost', + 'default_valkey_provider' => 'valkey://localhost', 'default_memcached_provider' => 'memcached://localhost', 'default_doctrine_dbal_provider' => 'database_connection', 'default_pdo_provider' => ContainerBuilder::willBeAvailable('doctrine/dbal', Connection::class, ['symfony/framework-bundle']) && class_exists(DoctrineAdapter::class) ? 'database_connection' : null, From 5121dfd2f5504ef2a639c01e0dc73975c2d46091 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 2 Mar 2025 16:03:52 +0100 Subject: [PATCH 224/281] replace assertEmpty() with stricter assertions --- Tests/DependencyInjection/FrameworkExtensionTestCase.php | 4 ++-- Tests/Functional/ApiAttributesTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 8581e4c8b..3a719b863 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -796,7 +796,7 @@ public function testMessengerServicesRemovedWhenDisabled() \ARRAY_FILTER_USE_KEY ); - $this->assertEmpty($messengerDefinitions); + $this->assertSame([], $messengerDefinitions); $this->assertFalse($container->hasDefinition('console.command.messenger_consume_messages')); $this->assertFalse($container->hasDefinition('console.command.messenger_debug')); $this->assertFalse($container->hasDefinition('console.command.messenger_stop_workers')); @@ -1941,7 +1941,7 @@ public function testRemovesResourceCheckerConfigCacheFactoryArgumentOnlyIfNoDebu $container = $this->createContainer(['kernel.debug' => false]); (new FrameworkExtension())->load([['annotations' => false, 'http_method_override' => false, 'handle_all_throwables' => true, 'php_errors' => ['log' => true]]], $container); - $this->assertEmpty($container->getDefinition('config_cache_factory')->getArguments()); + $this->assertSame([], $container->getDefinition('config_cache_factory')->getArguments()); } public function testLoggerAwareRegistration() diff --git a/Tests/Functional/ApiAttributesTest.php b/Tests/Functional/ApiAttributesTest.php index cf5c384ba..0dcfeaeba 100644 --- a/Tests/Functional/ApiAttributesTest.php +++ b/Tests/Functional/ApiAttributesTest.php @@ -34,7 +34,7 @@ public function testMapQueryString(string $uri, array $query, string $expectedRe if ($expectedResponse) { self::assertJsonStringEqualsJsonString($expectedResponse, $response->getContent()); } else { - self::assertEmpty($response->getContent()); + self::assertSame('', $response->getContent()); } self::assertSame($expectedStatusCode, $response->getStatusCode()); } From 52b51ab8ac5a9f41893b678d94bd83ab11c0e65e Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Thu, 6 Mar 2025 11:54:56 -0500 Subject: [PATCH 225/281] [FrameworkBundle] fix autowiring `RateLimiterFactoryInterface` --- CHANGELOG.md | 2 +- DependencyInjection/FrameworkExtension.php | 5 +++++ Resources/config/rate_limiter.php | 6 ------ 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17201aeae..3e10d305f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ CHANGELOG * Add JsonStreamer services and configuration * Add new `framework.property_info.with_constructor_extractor` option to allow enabling or disabling the constructor extractor integration * Deprecate the `--show-arguments` option of the `container:debug` command, as arguments are now always shown - * Add `RateLimiterFactoryInterface` as an alias of the `limiter` service + * Add autowiring alias for `RateLimiterFactoryInterface` * Add `framework.validation.disable_translation` option * Add support for signal plain name in the `messenger.stop_worker_on_signals` configuration * Deprecate the `framework.validation.cache` option diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 418ee739c..49078e2ee 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -151,6 +151,7 @@ use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\RateLimiter\LimiterInterface; use Symfony\Component\RateLimiter\RateLimiterFactory; +use Symfony\Component\RateLimiter\RateLimiterFactoryInterface; use Symfony\Component\RateLimiter\Storage\CacheStorage; use Symfony\Component\RemoteEvent\Attribute\AsRemoteEventConsumer; use Symfony\Component\RemoteEvent\RemoteEvent; @@ -3201,6 +3202,10 @@ private function registerRateLimiterConfiguration(array $config, ContainerBuilde $limiter->replaceArgument(0, $limiterConfig); $container->registerAliasForArgument($limiterId, RateLimiterFactory::class, $name.'.limiter'); + + if (interface_exists(RateLimiterFactoryInterface::class)) { + $container->registerAliasForArgument($limiterId, RateLimiterFactoryInterface::class, $name.'.limiter'); + } } } diff --git a/Resources/config/rate_limiter.php b/Resources/config/rate_limiter.php index 90af4d758..727a1f636 100644 --- a/Resources/config/rate_limiter.php +++ b/Resources/config/rate_limiter.php @@ -12,7 +12,6 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Component\RateLimiter\RateLimiterFactory; -use Symfony\Component\RateLimiter\RateLimiterFactoryInterface; return static function (ContainerConfigurator $container) { $container->services() @@ -28,9 +27,4 @@ null, ]) ; - - if (interface_exists(RateLimiterFactoryInterface::class)) { - $container->services() - ->alias(RateLimiterFactoryInterface::class, 'limiter'); - } }; From 373bdf534646e196b60851bc362023a89fd78159 Mon Sep 17 00:00:00 2001 From: Florian Cellier Date: Mon, 18 Nov 2024 12:13:09 +0100 Subject: [PATCH 226/281] [AssetMapper] Add `--dry-run` option on `importmap:require` command --- Resources/config/asset_mapper.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/config/asset_mapper.php b/Resources/config/asset_mapper.php index c18755864..eeb1ceb4f 100644 --- a/Resources/config/asset_mapper.php +++ b/Resources/config/asset_mapper.php @@ -232,6 +232,7 @@ ->args([ service('asset_mapper.importmap.manager'), service('asset_mapper.importmap.version_checker'), + param('kernel.project_dir'), ]) ->tag('console.command') From ce42a91b77510a7fd2eebafba989299783b7ee05 Mon Sep 17 00:00:00 2001 From: "hubert.lenoir" Date: Fri, 29 Dec 2023 10:09:04 +0100 Subject: [PATCH 227/281] [Translation] Allow default parameters --- DependencyInjection/Configuration.php | 28 +++++++++++++++++ DependencyInjection/FrameworkExtension.php | 5 +++ Resources/config/schema/symfony-1.0.xsd | 19 ++++++++++++ .../DependencyInjection/ConfigurationTest.php | 1 + .../Fixtures/php/translator_globals.php | 15 +++++++++ .../php/translator_without_globals.php | 9 ++++++ .../Fixtures/xml/translator_globals.xml | 20 ++++++++++++ .../xml/translator_without_globals.xml | 14 +++++++++ .../Fixtures/yml/translator_globals.yml | 11 +++++++ .../yml/translator_without_globals.yml | 8 +++++ .../FrameworkExtensionTestCase.php | 31 +++++++++++++++++++ composer.json | 4 +-- 12 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 Tests/DependencyInjection/Fixtures/php/translator_globals.php create mode 100644 Tests/DependencyInjection/Fixtures/php/translator_without_globals.php create mode 100644 Tests/DependencyInjection/Fixtures/xml/translator_globals.xml create mode 100644 Tests/DependencyInjection/Fixtures/xml/translator_without_globals.xml create mode 100644 Tests/DependencyInjection/Fixtures/yml/translator_globals.yml create mode 100644 Tests/DependencyInjection/Fixtures/yml/translator_without_globals.yml diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index fd77de26e..17b4a438b 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -967,6 +967,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e ->fixXmlConfig('fallback') ->fixXmlConfig('path') ->fixXmlConfig('provider') + ->fixXmlConfig('global') ->children() ->arrayNode('fallbacks') ->info('Defaults to the value of "default_locale".') @@ -1021,6 +1022,33 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e ->end() ->defaultValue([]) ->end() + ->arrayNode('globals') + ->info('Global parameters.') + ->example(['app_version' => 3.14]) + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->arrayPrototype() + ->fixXmlConfig('parameter') + ->children() + ->variableNode('value')->end() + ->stringNode('message')->end() + ->arrayNode('parameters') + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->scalarPrototype()->end() + ->end() + ->stringNode('domain')->end() + ->end() + ->beforeNormalization() + ->ifTrue(static fn ($v) => !\is_array($v)) + ->then(static fn ($v) => ['value' => $v]) + ->end() + ->validate() + ->ifTrue(static fn ($v) => !(isset($v['value']) xor isset($v['message']))) + ->thenInvalid('The "globals" parameter should be either a string or an array with a "value" or a "message" key') + ->end() + ->end() + ->end() ->end() ->end() ->end() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 418ee739c..fdcae2a34 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -185,6 +185,7 @@ use Symfony\Component\Translation\Extractor\PhpAstExtractor; use Symfony\Component\Translation\LocaleSwitcher; use Symfony\Component\Translation\PseudoLocalizationTranslator; +use Symfony\Component\Translation\TranslatableMessage; use Symfony\Component\Translation\Translator; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\TypeResolver\PhpDocAwareReflectionTypeResolver; @@ -1614,6 +1615,10 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder $translator->replaceArgument(4, $options); } + foreach ($config['globals'] as $name => $global) { + $translator->addMethodCall('addGlobalParameter', [$name, $global['value'] ?? new Definition(TranslatableMessage::class, [$global['message'], $global['parameters'] ?? [], $global['domain'] ?? null])]); + } + if ($config['pseudo_localization']['enabled']) { $options = $config['pseudo_localization']; unset($options['enabled']); diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index ae69a5aa9..8661384e2 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -256,6 +256,7 @@ + @@ -285,6 +286,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index e4ec77451..babfb2eab 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -785,6 +785,7 @@ protected static function getBundleDefaultConfig() 'localizable_html_attributes' => [], ], 'providers' => [], + 'globals' => [], ], 'validation' => [ 'enabled' => !class_exists(FullStack::class), diff --git a/Tests/DependencyInjection/Fixtures/php/translator_globals.php b/Tests/DependencyInjection/Fixtures/php/translator_globals.php new file mode 100644 index 000000000..8ee438ff9 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/translator_globals.php @@ -0,0 +1,15 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'translator' => [ + 'globals' => [ + '%%app_name%%' => 'My application', + '{app_version}' => '1.2.3', + '{url}' => ['message' => 'url', 'parameters' => ['scheme' => 'https://'], 'domain' => 'global'], + ], + ], +]); diff --git a/Tests/DependencyInjection/Fixtures/php/translator_without_globals.php b/Tests/DependencyInjection/Fixtures/php/translator_without_globals.php new file mode 100644 index 000000000..fcc65c968 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/translator_without_globals.php @@ -0,0 +1,9 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'translator' => ['globals' => []], +]); diff --git a/Tests/DependencyInjection/Fixtures/xml/translator_globals.xml b/Tests/DependencyInjection/Fixtures/xml/translator_globals.xml new file mode 100644 index 000000000..017fd9393 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/translator_globals.xml @@ -0,0 +1,20 @@ + + + + + + + + + My application + + + https:// + + + + diff --git a/Tests/DependencyInjection/Fixtures/xml/translator_without_globals.xml b/Tests/DependencyInjection/Fixtures/xml/translator_without_globals.xml new file mode 100644 index 000000000..6c686bd30 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/translator_without_globals.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/Tests/DependencyInjection/Fixtures/yml/translator_globals.yml b/Tests/DependencyInjection/Fixtures/yml/translator_globals.yml new file mode 100644 index 000000000..ed42b676c --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/translator_globals.yml @@ -0,0 +1,11 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + translator: + globals: + '%%app_name%%': 'My application' + '{app_version}': '1.2.3' + '{url}': { message: 'url', parameters: { scheme: 'https://' }, domain: 'global' } diff --git a/Tests/DependencyInjection/Fixtures/yml/translator_without_globals.yml b/Tests/DependencyInjection/Fixtures/yml/translator_without_globals.yml new file mode 100644 index 000000000..dc7323868 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/translator_without_globals.yml @@ -0,0 +1,8 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + translator: + globals: [] diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 3a719b863..fdc586cc9 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -83,6 +83,7 @@ use Symfony\Component\Serializer\Serializer; use Symfony\Component\Translation\DependencyInjection\TranslatorPass; use Symfony\Component\Translation\LocaleSwitcher; +use Symfony\Component\Translation\TranslatableMessage; use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass; use Symfony\Component\Validator\Validation; use Symfony\Component\Validator\Validator\ValidatorInterface; @@ -1241,6 +1242,36 @@ public function testTranslatorCacheDirDisabled() $this->assertNull($options['cache_dir']); } + public function testTranslatorGlobals() + { + $container = $this->createContainerFromFile('translator_globals'); + + $calls = $container->getDefinition('translator.default')->getMethodCalls(); + + $this->assertCount(5, $calls); + $this->assertSame( + ['addGlobalParameter', ['%%app_name%%', 'My application']], + $calls[2], + ); + $this->assertSame( + ['addGlobalParameter', ['{app_version}', '1.2.3']], + $calls[3], + ); + $this->assertEquals( + ['addGlobalParameter', ['{url}', new Definition(TranslatableMessage::class, ['url', ['scheme' => 'https://'], 'global'])]], + $calls[4], + ); + } + + public function testTranslatorWithoutGlobals() + { + $container = $this->createContainerFromFile('translator_without_globals'); + + $calls = $container->getDefinition('translator.default')->getMethodCalls(); + + $this->assertCount(2, $calls); + } + public function testValidation() { $container = $this->createContainerFromFile('full'); diff --git a/composer.json b/composer.json index 999dd7fed..72d33ed88 100644 --- a/composer.json +++ b/composer.json @@ -62,7 +62,7 @@ "symfony/serializer": "^7.1", "symfony/stopwatch": "^6.4|^7.0", "symfony/string": "^6.4|^7.0", - "symfony/translation": "^6.4.3|^7.0", + "symfony/translation": "^7.3", "symfony/twig-bundle": "^6.4|^7.0", "symfony/type-info": "^7.1", "symfony/validator": "^6.4|^7.0", @@ -101,7 +101,7 @@ "symfony/security-core": "<6.4", "symfony/serializer": "<7.1", "symfony/stopwatch": "<6.4", - "symfony/translation": "<6.4.3", + "symfony/translation": "<7.3", "symfony/twig-bridge": "<6.4", "symfony/twig-bundle": "<6.4", "symfony/validator": "<6.4", From cfe5c8beb6052fb0292023c05ade0dc866cc3c59 Mon Sep 17 00:00:00 2001 From: Santiago San Martin Date: Tue, 4 Mar 2025 15:18:16 -0300 Subject: [PATCH 228/281] Add 'method' option to debug:router command to filter routes by HTTP method --- CHANGELOG.md | 1 + Command/RouterDebugCommand.php | 10 +++-- Console/Descriptor/Descriptor.php | 18 ++++++++- .../Descriptor/AbstractDescriptorTestCase.php | 16 ++++++++ Tests/Console/Descriptor/ObjectsProvider.php | 32 ++++++++++++++++ .../Descriptor/route_collection_2.json | 38 +++++++++++++++++++ .../Fixtures/Descriptor/route_collection_2.md | 37 ++++++++++++++++++ .../Descriptor/route_collection_2.txt | 7 ++++ .../Descriptor/route_collection_2.xml | 33 ++++++++++++++++ .../Descriptor/route_collection_3.json | 19 ++++++++++ .../Fixtures/Descriptor/route_collection_3.md | 18 +++++++++ .../Descriptor/route_collection_3.txt | 6 +++ .../Descriptor/route_collection_3.xml | 17 +++++++++ 13 files changed, 248 insertions(+), 4 deletions(-) create mode 100644 Tests/Fixtures/Descriptor/route_collection_2.json create mode 100644 Tests/Fixtures/Descriptor/route_collection_2.md create mode 100644 Tests/Fixtures/Descriptor/route_collection_2.txt create mode 100644 Tests/Fixtures/Descriptor/route_collection_2.xml create mode 100644 Tests/Fixtures/Descriptor/route_collection_3.json create mode 100644 Tests/Fixtures/Descriptor/route_collection_3.md create mode 100644 Tests/Fixtures/Descriptor/route_collection_3.txt create mode 100644 Tests/Fixtures/Descriptor/route_collection_3.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 17201aeae..8340a4970 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ CHANGELOG * Add `framework.validation.disable_translation` option * Add support for signal plain name in the `messenger.stop_worker_on_signals` configuration * Deprecate the `framework.validation.cache` option + * Add `--method` option to the `debug:router` command 7.2 --- diff --git a/Command/RouterDebugCommand.php b/Command/RouterDebugCommand.php index 13a6f75d0..e54377115 100644 --- a/Command/RouterDebugCommand.php +++ b/Command/RouterDebugCommand.php @@ -55,6 +55,7 @@ protected function configure(): void new InputOption('show-aliases', null, InputOption::VALUE_NONE, 'Show aliases in overview'), new InputOption('format', null, InputOption::VALUE_REQUIRED, \sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'txt'), new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw route(s)'), + new InputOption('method', null, InputOption::VALUE_REQUIRED, 'Filter by HTTP method', '', ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']), ]) ->setHelp(<<<'EOF' The %command.name% displays the configured routes: @@ -76,6 +77,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $name = $input->getArgument('name'); + $method = strtoupper($input->getOption('method')); $helper = new DescriptorHelper($this->fileLinkFormatter); $routes = $this->router->getRouteCollection(); $container = null; @@ -85,7 +87,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($name) { $route = $routes->get($name); - $matchingRoutes = $this->findRouteNameContaining($name, $routes); + $matchingRoutes = $this->findRouteNameContaining($name, $routes, $method); if (!$input->isInteractive() && !$route && \count($matchingRoutes) > 1) { $helper->describe($io, $this->findRouteContaining($name, $routes), [ @@ -94,6 +96,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'show_controllers' => $input->getOption('show-controllers'), 'show_aliases' => $input->getOption('show-aliases'), 'output' => $io, + 'method' => $method, ]); return 0; @@ -124,17 +127,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'show_aliases' => $input->getOption('show-aliases'), 'output' => $io, 'container' => $container, + 'method' => $method, ]); } return 0; } - private function findRouteNameContaining(string $name, RouteCollection $routes): array + private function findRouteNameContaining(string $name, RouteCollection $routes, string $method): array { $foundRoutesNames = []; foreach ($routes as $routeName => $route) { - if (false !== stripos($routeName, $name)) { + if (false !== stripos($routeName, $name) && (!$method || !$route->getMethods() || \in_array($method, $route->getMethods(), true))) { $foundRoutesNames[] = $routeName; } } diff --git a/Console/Descriptor/Descriptor.php b/Console/Descriptor/Descriptor.php index af5c3b10a..e76b74247 100644 --- a/Console/Descriptor/Descriptor.php +++ b/Console/Descriptor/Descriptor.php @@ -49,7 +49,7 @@ public function describe(OutputInterface $output, mixed $object, array $options } match (true) { - $object instanceof RouteCollection => $this->describeRouteCollection($object, $options), + $object instanceof RouteCollection => $this->describeRouteCollection($this->filterRoutesByHttpMethod($object, $options['method'] ?? ''), $options), $object instanceof Route => $this->describeRoute($object, $options), $object instanceof ParameterBag => $this->describeContainerParameters($object, $options), $object instanceof ContainerBuilder && !empty($options['env-vars']) => $this->describeContainerEnvVars($this->getContainerEnvVars($object), $options), @@ -360,4 +360,20 @@ protected function getServiceEdges(ContainerBuilder $container, string $serviceI return []; } } + + private function filterRoutesByHttpMethod(RouteCollection $routes, string $method): RouteCollection + { + if (!$method) { + return $routes; + } + $filteredRoutes = clone $routes; + + foreach ($filteredRoutes as $routeName => $route) { + if ($route->getMethods() && !\in_array($method, $route->getMethods(), true)) { + $filteredRoutes->remove($routeName); + } + } + + return $filteredRoutes; + } } diff --git a/Tests/Console/Descriptor/AbstractDescriptorTestCase.php b/Tests/Console/Descriptor/AbstractDescriptorTestCase.php index 477bd1014..eb18fbcc7 100644 --- a/Tests/Console/Descriptor/AbstractDescriptorTestCase.php +++ b/Tests/Console/Descriptor/AbstractDescriptorTestCase.php @@ -50,6 +50,21 @@ public static function getDescribeRouteCollectionTestData(): array return static::getDescriptionTestData(ObjectsProvider::getRouteCollections()); } + /** @dataProvider getDescribeRouteCollectionWithHttpMethodFilterTestData */ + public function testDescribeRouteCollectionWithHttpMethodFilter(string $httpMethod, RouteCollection $routes, $expectedDescription) + { + $this->assertDescription($expectedDescription, $routes, ['method' => $httpMethod]); + } + + public static function getDescribeRouteCollectionWithHttpMethodFilterTestData(): iterable + { + foreach (ObjectsProvider::getRouteCollectionsByHttpMethod() as $httpMethod => $routeCollection) { + foreach (static::getDescriptionTestData($routeCollection) as $testData) { + yield [$httpMethod, ...$testData]; + } + } + } + /** @dataProvider getDescribeRouteTestData */ public function testDescribeRoute(Route $route, $expectedDescription) { @@ -273,6 +288,7 @@ private function assertDescription($expectedDescription, $describedObject, array $options['is_debug'] = false; $options['raw_output'] = true; $options['raw_text'] = true; + $options['method'] ??= null; $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); if ('txt' === $this->getFormat()) { diff --git a/Tests/Console/Descriptor/ObjectsProvider.php b/Tests/Console/Descriptor/ObjectsProvider.php index 84adc4ac9..8eb1c4386 100644 --- a/Tests/Console/Descriptor/ObjectsProvider.php +++ b/Tests/Console/Descriptor/ObjectsProvider.php @@ -37,6 +37,38 @@ public static function getRouteCollections() return ['route_collection_1' => $collection1]; } + public static function getRouteCollectionsByHttpMethod(): array + { + $collection = new RouteCollection(); + foreach (self::getRoutes() as $name => $route) { + $collection->add($name, $route); + } + + // Clone the original collection and add a route without any specific method restrictions + $collectionWithRouteWithoutMethodRestriction = clone $collection; + $collectionWithRouteWithoutMethodRestriction->add( + 'route_3', + new RouteStub( + '/other/route', + [], + [], + ['opt1' => 'val1', 'opt2' => 'val2'], + 'localhost', + ['http', 'https'], + [], + ) + ); + + return [ + 'GET' => [ + 'route_collection_2' => $collectionWithRouteWithoutMethodRestriction, + ], + 'PUT' => [ + 'route_collection_3' => $collection, + ], + ]; + } + public static function getRoutes() { return [ diff --git a/Tests/Fixtures/Descriptor/route_collection_2.json b/Tests/Fixtures/Descriptor/route_collection_2.json new file mode 100644 index 000000000..8f5d2c743 --- /dev/null +++ b/Tests/Fixtures/Descriptor/route_collection_2.json @@ -0,0 +1,38 @@ +{ + "route_1": { + "path": "\/hello\/{name}", + "pathRegex": "#PATH_REGEX#", + "host": "localhost", + "hostRegex": "#HOST_REGEX#", + "scheme": "http|https", + "method": "GET|HEAD", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\RouteStub", + "defaults": { + "name": "Joseph" + }, + "requirements": { + "name": "[a-z]+" + }, + "options": { + "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", + "opt1": "val1", + "opt2": "val2" + } + }, + "route_3": { + "path": "\/other\/route", + "pathRegex": "#PATH_REGEX#", + "host": "localhost", + "hostRegex": "#HOST_REGEX#", + "scheme": "http|https", + "method": "ANY", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\RouteStub", + "defaults": [], + "requirements": "NO CUSTOM", + "options": { + "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", + "opt1": "val1", + "opt2": "val2" + } + } +} diff --git a/Tests/Fixtures/Descriptor/route_collection_2.md b/Tests/Fixtures/Descriptor/route_collection_2.md new file mode 100644 index 000000000..e1b11e4a4 --- /dev/null +++ b/Tests/Fixtures/Descriptor/route_collection_2.md @@ -0,0 +1,37 @@ +route_1 +------- + +- Path: /hello/{name} +- Path Regex: #PATH_REGEX# +- Host: localhost +- Host Regex: #HOST_REGEX# +- Scheme: http|https +- Method: GET|HEAD +- Class: Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub +- Defaults: + - `name`: Joseph +- Requirements: + - `name`: [a-z]+ +- Options: + - `compiler_class`: Symfony\Component\Routing\RouteCompiler + - `opt1`: val1 + - `opt2`: val2 + + +route_3 +------- + +- Path: /other/route +- Path Regex: #PATH_REGEX# +- Host: localhost +- Host Regex: #HOST_REGEX# +- Scheme: http|https +- Method: ANY +- Class: Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub +- Defaults: NONE +- Requirements: NO CUSTOM +- Options: + - `compiler_class`: Symfony\Component\Routing\RouteCompiler + - `opt1`: val1 + - `opt2`: val2 + diff --git a/Tests/Fixtures/Descriptor/route_collection_2.txt b/Tests/Fixtures/Descriptor/route_collection_2.txt new file mode 100644 index 000000000..a9f9ee21b --- /dev/null +++ b/Tests/Fixtures/Descriptor/route_collection_2.txt @@ -0,0 +1,7 @@ + --------- ---------- ------------ ----------- --------------- +  Name   Method   Scheme   Host   Path  + --------- ---------- ------------ ----------- --------------- + route_1 GET|HEAD http|https localhost /hello/{name} + route_3 ANY http|https localhost /other/route + --------- ---------- ------------ ----------- --------------- + diff --git a/Tests/Fixtures/Descriptor/route_collection_2.xml b/Tests/Fixtures/Descriptor/route_collection_2.xml new file mode 100644 index 000000000..18c41deb7 --- /dev/null +++ b/Tests/Fixtures/Descriptor/route_collection_2.xml @@ -0,0 +1,33 @@ + + + + /hello/{name} + localhost + http + https + GET + HEAD + + Joseph + + + [a-z]+ + + + + + + + + + /other/route + localhost + http + https + + + + + + + diff --git a/Tests/Fixtures/Descriptor/route_collection_3.json b/Tests/Fixtures/Descriptor/route_collection_3.json new file mode 100644 index 000000000..cabc8e0a7 --- /dev/null +++ b/Tests/Fixtures/Descriptor/route_collection_3.json @@ -0,0 +1,19 @@ +{ + "route_2": { + "path": "\/name\/add", + "pathRegex": "#PATH_REGEX#", + "host": "localhost", + "hostRegex": "#HOST_REGEX#", + "scheme": "http|https", + "method": "PUT|POST", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\RouteStub", + "defaults": [], + "requirements": "NO CUSTOM", + "options": { + "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", + "opt1": "val1", + "opt2": "val2" + }, + "condition": "context.getMethod() in ['GET', 'HEAD', 'POST']" + } +} diff --git a/Tests/Fixtures/Descriptor/route_collection_3.md b/Tests/Fixtures/Descriptor/route_collection_3.md new file mode 100644 index 000000000..20fdabb95 --- /dev/null +++ b/Tests/Fixtures/Descriptor/route_collection_3.md @@ -0,0 +1,18 @@ +route_2 +------- + +- Path: /name/add +- Path Regex: #PATH_REGEX# +- Host: localhost +- Host Regex: #HOST_REGEX# +- Scheme: http|https +- Method: PUT|POST +- Class: Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub +- Defaults: NONE +- Requirements: NO CUSTOM +- Options: + - `compiler_class`: Symfony\Component\Routing\RouteCompiler + - `opt1`: val1 + - `opt2`: val2 +- Condition: context.getMethod() in ['GET', 'HEAD', 'POST'] + diff --git a/Tests/Fixtures/Descriptor/route_collection_3.txt b/Tests/Fixtures/Descriptor/route_collection_3.txt new file mode 100644 index 000000000..8822b3c40 --- /dev/null +++ b/Tests/Fixtures/Descriptor/route_collection_3.txt @@ -0,0 +1,6 @@ + --------- ---------- ------------ ----------- ----------- +  Name   Method   Scheme   Host   Path  + --------- ---------- ------------ ----------- ----------- + route_2 PUT|POST http|https localhost /name/add + --------- ---------- ------------ ----------- ----------- + diff --git a/Tests/Fixtures/Descriptor/route_collection_3.xml b/Tests/Fixtures/Descriptor/route_collection_3.xml new file mode 100644 index 000000000..57a05d4c1 --- /dev/null +++ b/Tests/Fixtures/Descriptor/route_collection_3.xml @@ -0,0 +1,17 @@ + + + + /name/add + localhost + http + https + PUT + POST + + + + + + context.getMethod() in ['GET', 'HEAD', 'POST'] + + From 7ee3ca4ef02f16e33000ec8e3b1b5a2a68af8924 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 19 Feb 2025 15:04:36 +0100 Subject: [PATCH 229/281] [Cache] Enable namespace-based invalidation by prefixing keys with backend-native namespace separators --- DependencyInjection/FrameworkExtension.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index e2d0888db..353fd1942 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -208,6 +208,7 @@ use Symfony\Component\Yaml\Yaml; use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\CallbackInterface; +use Symfony\Contracts\Cache\NamespacedPoolInterface; use Symfony\Contracts\Cache\TagAwareCacheInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -2576,6 +2577,10 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con $container->registerAliasForArgument($tagAwareId, TagAwareCacheInterface::class, $pool['name'] ?? $name); $container->registerAliasForArgument($name, CacheInterface::class, $pool['name'] ?? $name); $container->registerAliasForArgument($name, CacheItemPoolInterface::class, $pool['name'] ?? $name); + + if (interface_exists(NamespacedPoolInterface::class)) { + $container->registerAliasForArgument($name, NamespacedPoolInterface::class, $pool['name'] ?? $name); + } } $definition->setPublic($pool['public']); From 2da58e6d5bb3559bcacf9bf81aac5ef6f97d3b43 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Fri, 14 Mar 2025 19:20:21 -0400 Subject: [PATCH 230/281] Fixed support for Kernel as command --- Tests/Kernel/KernelCommand.php | 26 ++++++++++++++++++++++++++ Tests/Kernel/MicroKernelTraitTest.php | 21 +++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 Tests/Kernel/KernelCommand.php diff --git a/Tests/Kernel/KernelCommand.php b/Tests/Kernel/KernelCommand.php new file mode 100644 index 000000000..4c9a5d85a --- /dev/null +++ b/Tests/Kernel/KernelCommand.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel; + +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Output\OutputInterface; + +#[AsCommand(name: 'kernel:hello')] +final class KernelCommand extends MinimalKernel +{ + public function __invoke(OutputInterface $output): int + { + $output->write('Hello Kernel!'); + + return 0; + } +} diff --git a/Tests/Kernel/MicroKernelTraitTest.php b/Tests/Kernel/MicroKernelTraitTest.php index a9d2ae720..5c7161124 100644 --- a/Tests/Kernel/MicroKernelTraitTest.php +++ b/Tests/Kernel/MicroKernelTraitTest.php @@ -13,7 +13,11 @@ use PHPUnit\Framework\TestCase; use Psr\Log\NullLogger; +use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; @@ -152,6 +156,23 @@ public function testSimpleKernel() $this->assertSame('Hello World!', $response->getContent()); } + public function testKernelCommand() + { + if (!property_exists(AsCommand::class, 'help')) { + $this->markTestSkipped('Invokable command no available.'); + } + + $kernel = $this->kernel = new KernelCommand('kernel_command'); + $application = new Application($kernel); + + $input = new ArrayInput(['command' => 'kernel:hello']); + $output = new BufferedOutput(); + + $this->assertTrue($application->has('kernel:hello')); + $this->assertSame(0, $application->doRun($input, $output)); + $this->assertSame('Hello Kernel!', $output->fetch()); + } + public function testDefaultKernel() { $kernel = $this->kernel = new DefaultKernel('test', false); From b17181f38963cbc2e160a71a5b8c2c9971375574 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 16 Mar 2025 15:56:09 +0100 Subject: [PATCH 231/281] [FrameworkBundle] Auto-exclude DI extensions, test cases, entities and messenger messages --- CHANGELOG.md | 1 + DependencyInjection/FrameworkExtension.php | 29 +++++++++++++++++++--- Kernel/MicroKernelTrait.php | 3 +-- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2337f5ae0..9d3b3eae4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ CHANGELOG * Add support for signal plain name in the `messenger.stop_worker_on_signals` configuration * Deprecate the `framework.validation.cache` option * Add `--method` option to the `debug:router` command + * Auto-exclude DI extensions, test cases, entities and messenger messages 7.2 --- diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index e2d0888db..9ee0f0f43 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -12,12 +12,16 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; use Composer\InstalledVersions; +use Doctrine\ORM\Mapping\Embeddable; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\MappedSuperclass; use Http\Client\HttpAsyncClient; use Http\Client\HttpClient; use phpDocumentor\Reflection\DocBlockFactoryInterface; use phpDocumentor\Reflection\Types\ContextFactory; use PhpParser\Parser; use PHPStan\PhpDocParser\Parser\PhpDocParser; +use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemPoolInterface; use Psr\Clock\ClockInterface as PsrClockInterface; use Psr\Container\ContainerInterface as PsrContainerInterface; @@ -57,6 +61,7 @@ use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -117,6 +122,7 @@ use Symfony\Component\Mailer\EventListener\SmimeSignedMessageListener; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mercure\HubRegistry; +use Symfony\Component\Messenger\Attribute\AsMessage; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Messenger\Bridge as MessengerBridge; use Symfony\Component\Messenger\Handler\BatchHandlerInterface; @@ -757,12 +763,29 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu } ); } - $container->registerAttributeForAutoconfiguration(JsonStreamable::class, static function (ChildDefinition $definition, JsonStreamable $attribute): void { - $definition->addTag('json_streamer.streamable', [ + + $container->registerForAutoconfiguration(CompilerPassInterface::class) + ->addExcludeTag('container.excluded.compiler_pass'); + $container->registerForAutoconfiguration(TestCase::class) + ->addExcludeTag('container.excluded.test_case'); + $container->registerAttributeForAutoconfiguration(AsMessage::class, static function (ChildDefinition $definition) { + $definition->addExcludeTag('container.excluded.messenger.message'); + }); + $container->registerAttributeForAutoconfiguration(Entity::class, static function (ChildDefinition $definition) { + $definition->addExcludeTag('container.excluded.doctrine.entity'); + }); + $container->registerAttributeForAutoconfiguration(Embeddable::class, static function (ChildDefinition $definition) { + $definition->addExcludeTag('container.excluded.doctrine.embeddable'); + }); + $container->registerAttributeForAutoconfiguration(MappedSuperclass::class, static function (ChildDefinition $definition) { + $definition->addExcludeTag('container.excluded.doctrine.mapped_superclass'); + }); + + $container->registerAttributeForAutoconfiguration(JsonStreamable::class, static function (ChildDefinition $definition, JsonStreamable $attribute) { + $definition->addExcludeTag('json_streamer.streamable', [ 'object' => $attribute->asObject, 'list' => $attribute->asList, ]); - $definition->addTag('container.excluded'); }); if (!$container->getParameter('kernel.debug')) { diff --git a/Kernel/MicroKernelTrait.php b/Kernel/MicroKernelTrait.php index f40373a30..28d616c13 100644 --- a/Kernel/MicroKernelTrait.php +++ b/Kernel/MicroKernelTrait.php @@ -165,6 +165,7 @@ public function registerContainerConfiguration(LoaderInterface $loader): void ->setPublic(true) ; } + $container->setAlias($kernelClass, 'kernel')->setPublic(true); $kernelDefinition = $container->getDefinition('kernel'); $kernelDefinition->addTag('routing.route_loader'); @@ -197,8 +198,6 @@ public function registerContainerConfiguration(LoaderInterface $loader): void $kernelLoader->registerAliasesForSinglyImplementedInterfaces(); AbstractConfigurator::$valuePreProcessor = $valuePreProcessor; } - - $container->setAlias($kernelClass, 'kernel')->setPublic(true); }); } From 719e035947bd6657312400889c7352cda76eecd3 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Tue, 18 Mar 2025 20:37:55 -0400 Subject: [PATCH 232/281] [DI] Rename "exclude tag" to "resource tag" --- DependencyInjection/FrameworkExtension.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 9ee0f0f43..2860457a8 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -765,24 +765,24 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu } $container->registerForAutoconfiguration(CompilerPassInterface::class) - ->addExcludeTag('container.excluded.compiler_pass'); + ->addResourceTag('container.excluded.compiler_pass'); $container->registerForAutoconfiguration(TestCase::class) - ->addExcludeTag('container.excluded.test_case'); + ->addResourceTag('container.excluded.test_case'); $container->registerAttributeForAutoconfiguration(AsMessage::class, static function (ChildDefinition $definition) { - $definition->addExcludeTag('container.excluded.messenger.message'); + $definition->addResourceTag('container.excluded.messenger.message'); }); $container->registerAttributeForAutoconfiguration(Entity::class, static function (ChildDefinition $definition) { - $definition->addExcludeTag('container.excluded.doctrine.entity'); + $definition->addResourceTag('container.excluded.doctrine.entity'); }); $container->registerAttributeForAutoconfiguration(Embeddable::class, static function (ChildDefinition $definition) { - $definition->addExcludeTag('container.excluded.doctrine.embeddable'); + $definition->addResourceTag('container.excluded.doctrine.embeddable'); }); $container->registerAttributeForAutoconfiguration(MappedSuperclass::class, static function (ChildDefinition $definition) { - $definition->addExcludeTag('container.excluded.doctrine.mapped_superclass'); + $definition->addResourceTag('container.excluded.doctrine.mapped_superclass'); }); $container->registerAttributeForAutoconfiguration(JsonStreamable::class, static function (ChildDefinition $definition, JsonStreamable $attribute) { - $definition->addExcludeTag('json_streamer.streamable', [ + $definition->addResourceTag('json_streamer.streamable', [ 'object' => $attribute->asObject, 'list' => $attribute->asList, ]); From 914cb2cf66f6220cf29dd9244c4d7b81620a3b1d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 18 Mar 2025 10:01:33 +0100 Subject: [PATCH 233/281] fix compatibility with DependencyInjection < 7.3 --- DependencyInjection/FrameworkExtension.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 2860457a8..2f2df8535 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -765,27 +765,29 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu } $container->registerForAutoconfiguration(CompilerPassInterface::class) - ->addResourceTag('container.excluded.compiler_pass'); + ->addTag('container.excluded.compiler_pass')->addTag('container.excluded')->setAbstract(true); $container->registerForAutoconfiguration(TestCase::class) - ->addResourceTag('container.excluded.test_case'); + ->addTag('container.excluded.test_case')->addTag('container.excluded')->setAbstract(true); $container->registerAttributeForAutoconfiguration(AsMessage::class, static function (ChildDefinition $definition) { - $definition->addResourceTag('container.excluded.messenger.message'); + $definition->addTag('container.excluded.messenger.message')->addTag('container.excluded')->setAbstract(true); }); $container->registerAttributeForAutoconfiguration(Entity::class, static function (ChildDefinition $definition) { - $definition->addResourceTag('container.excluded.doctrine.entity'); + $definition->addTag('container.excluded.doctrine.entity')->addTag('container.excluded')->setAbstract(true); }); $container->registerAttributeForAutoconfiguration(Embeddable::class, static function (ChildDefinition $definition) { - $definition->addResourceTag('container.excluded.doctrine.embeddable'); + $definition->addTag('container.excluded.doctrine.embeddable')->addTag('container.excluded')->setAbstract(true); }); $container->registerAttributeForAutoconfiguration(MappedSuperclass::class, static function (ChildDefinition $definition) { - $definition->addResourceTag('container.excluded.doctrine.mapped_superclass'); + $definition->addTag('container.excluded.doctrine.mapped_superclass')->addTag('container.excluded')->setAbstract(true); }); $container->registerAttributeForAutoconfiguration(JsonStreamable::class, static function (ChildDefinition $definition, JsonStreamable $attribute) { - $definition->addResourceTag('json_streamer.streamable', [ + $definition->addTag('json_streamer.streamable', [ 'object' => $attribute->asObject, 'list' => $attribute->asList, ]); + $definition->addTag('container.excluded'); + $definition->setAbstract(true); }); if (!$container->getParameter('kernel.debug')) { From 11b2364b6cadbbd9f576c76653b447ec5321718d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 21 Mar 2025 13:14:10 +0100 Subject: [PATCH 234/281] [FrameworkBundle] Add alias `ServicesResetter` for `services_resetter` service --- CHANGELOG.md | 1 + Resources/config/services.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d3b3eae4..5ffc9f85f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ CHANGELOG * Deprecate the `framework.validation.cache` option * Add `--method` option to the `debug:router` command * Auto-exclude DI extensions, test cases, entities and messenger messages + * Add DI alias from `ServicesResetterInterface` to `services_resetter` 7.2 --- diff --git a/Resources/config/services.php b/Resources/config/services.php index e5a86d8f4..558c2b6d5 100644 --- a/Resources/config/services.php +++ b/Resources/config/services.php @@ -42,6 +42,7 @@ use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate; use Symfony\Component\HttpKernel\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter; +use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetterInterface; use Symfony\Component\HttpKernel\EventListener\LocaleAwareListener; use Symfony\Component\HttpKernel\HttpCache\Store; use Symfony\Component\HttpKernel\HttpCache\StoreInterface; @@ -177,6 +178,7 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : [] ->set('services_resetter', ServicesResetter::class) ->public() + ->alias(ServicesResetterInterface::class, 'services_resetter') ->set('reverse_container', ReverseContainer::class) ->args([ From 68875215bf74a481a64d345d81510214845adf94 Mon Sep 17 00:00:00 2001 From: Oviglo Date: Thu, 20 Mar 2025 09:12:34 +0100 Subject: [PATCH 235/281] [Security] Add methods param in IsCsrfTokenValid attribute --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ffc9f85f..9dd35a826 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ CHANGELOG * Add `--method` option to the `debug:router` command * Auto-exclude DI extensions, test cases, entities and messenger messages * Add DI alias from `ServicesResetterInterface` to `services_resetter` + * Add `methods` argument in `#[IsCsrfTokenValid]` attribute 7.2 --- From 98c231361b54e3939a6a2fb4ac5a01f46c18f7c0 Mon Sep 17 00:00:00 2001 From: Arkalo2 <24898676+Arkalo2@users.noreply.github.com> Date: Sat, 22 Mar 2025 19:57:41 +0100 Subject: [PATCH 236/281] [FrameworkBundle][HttpKernel] Allow configuring the logging channel per type of exceptions --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 4 ++++ DependencyInjection/FrameworkExtension.php | 15 ++++++++++++++- Resources/config/web.php | 1 + .../FrameworkExtensionTestCase.php | 4 ++++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dd35a826..63eca480b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ CHANGELOG * Auto-exclude DI extensions, test cases, entities and messenger messages * Add DI alias from `ServicesResetterInterface` to `services_resetter` * Add `methods` argument in `#[IsCsrfTokenValid]` attribute + * Allow configuring the logging channel per type of exceptions 7.2 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 17b4a438b..bb6a3347b 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1458,6 +1458,10 @@ private function addExceptionsSection(ArrayNodeDefinition $rootNode): void ->end() ->defaultNull() ->end() + ->scalarNode('log_channel') + ->info('The channel of log message. Null to let Symfony decide.') + ->defaultNull() + ->end() ->end() ->end() ->end() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index b8b723d1d..fed06a6c7 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -417,7 +417,20 @@ public function load(array $configs, ContainerBuilder $container): void $this->registerPropertyAccessConfiguration($config['property_access'], $container, $loader); $this->registerSecretsConfiguration($config['secrets'], $container, $loader, $config['secret'] ?? null); - $container->getDefinition('exception_listener')->replaceArgument(3, $config['exceptions']); + $exceptionListener = $container->getDefinition('exception_listener'); + + $loggers = []; + foreach ($config['exceptions'] as $exception) { + if (!isset($exception['log_channel'])) { + continue; + } + $loggers[$exception['log_channel']] = new Reference('monolog.logger.'.$exception['log_channel'], ContainerInterface::NULL_ON_INVALID_REFERENCE); + } + + $exceptionListener + ->replaceArgument(3, $config['exceptions']) + ->setArgument(4, $loggers) + ; if ($this->readConfigEnabled('serializer', $container, $config['serializer'])) { if (!class_exists(Serializer::class)) { diff --git a/Resources/config/web.php b/Resources/config/web.php index 6f8358fb0..a4e975dac 100644 --- a/Resources/config/web.php +++ b/Resources/config/web.php @@ -138,6 +138,7 @@ service('logger')->nullOnInvalid(), param('kernel.debug'), abstract_arg('an exceptions to log & status code mapping'), + abstract_arg('list of loggers by log_channel'), ]) ->tag('kernel.event_subscriber') ->tag('monolog.logger', ['channel' => 'request']) diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index fdc586cc9..9edbf111e 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -615,21 +615,25 @@ public function testExceptionsConfig() ], array_keys($configuration)); $this->assertEqualsCanonicalizing([ + 'log_channel' => null, 'log_level' => 'info', 'status_code' => 422, ], $configuration[\Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class]); $this->assertEqualsCanonicalizing([ + 'log_channel' => null, 'log_level' => 'info', 'status_code' => null, ], $configuration[\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class]); $this->assertEqualsCanonicalizing([ + 'log_channel' => null, 'log_level' => 'info', 'status_code' => null, ], $configuration[\Symfony\Component\HttpKernel\Exception\ConflictHttpException::class]); $this->assertEqualsCanonicalizing([ + 'log_channel' => null, 'log_level' => null, 'status_code' => 500, ], $configuration[\Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class]); From c1c6ee8946491b698b067df2258e07918c25da02 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Mon, 24 Mar 2025 05:35:59 +0100 Subject: [PATCH 237/281] [Serializer] Fix ObjectNormalizer default context with named serializers --- DependencyInjection/FrameworkExtension.php | 16 +++------------- Resources/config/serializer.php | 2 +- .../FrameworkExtensionTestCase.php | 16 ++++++++++++---- composer.json | 4 ++-- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index bd27c27a6..8d64adeca 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -1946,24 +1946,14 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->setParameter('serializer.default_context', $defaultContext); } - if (!$container->hasDefinition('serializer.normalizer.object')) { - return; - } - - $arguments = $container->getDefinition('serializer.normalizer.object')->getArguments(); - $context = $arguments[6] ?? $defaultContext; - - if (isset($config['circular_reference_handler']) && $config['circular_reference_handler']) { - $context += ['circular_reference_handler' => new Reference($config['circular_reference_handler'])]; - $container->getDefinition('serializer.normalizer.object')->setArgument(5, null); + if ($config['circular_reference_handler'] ?? false) { + $container->setParameter('.serializer.circular_reference_handler', $config['circular_reference_handler']); } if ($config['max_depth_handler'] ?? false) { - $context += ['max_depth_handler' => new Reference($config['max_depth_handler'])]; + $container->setParameter('.serializer.max_depth_handler', $config['max_depth_handler']); } - $container->getDefinition('serializer.normalizer.object')->setArgument(6, $context); - $container->getDefinition('serializer.normalizer.property')->setArgument(5, $defaultContext); $container->setParameter('.serializer.named_serializers', $config['named_serializers'] ?? []); diff --git a/Resources/config/serializer.php b/Resources/config/serializer.php index 4686a88f6..b291f51ac 100644 --- a/Resources/config/serializer.php +++ b/Resources/config/serializer.php @@ -129,7 +129,7 @@ service('property_info')->ignoreOnInvalid(), service('serializer.mapping.class_discriminator_resolver')->ignoreOnInvalid(), null, - null, + abstract_arg('default context, set in the SerializerPass'), service('property_info')->ignoreOnInvalid(), ]) ->tag('serializer.normalizer', ['built_in' => true, 'priority' => -1000]) diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 5f5f41801..7bf66512d 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -33,6 +33,7 @@ use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass; @@ -67,6 +68,7 @@ use Symfony\Component\Notifier\TexterInterface; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\Security\Core\AuthenticationEvents; +use Symfony\Component\Serializer\DependencyInjection\SerializerPass; use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader; use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; @@ -1447,9 +1449,6 @@ public function testSerializerEnabled() $this->assertEquals(AttributeLoader::class, $argument[0]->getClass()); $this->assertEquals(new Reference('serializer.name_converter.camel_case_to_snake_case'), $container->getDefinition('serializer.name_converter.metadata_aware')->getArgument(1)); $this->assertEquals(new Reference('property_info', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE), $container->getDefinition('serializer.normalizer.object')->getArgument(3)); - $this->assertArrayHasKey('circular_reference_handler', $container->getDefinition('serializer.normalizer.object')->getArgument(6)); - $this->assertArrayHasKey('max_depth_handler', $container->getDefinition('serializer.normalizer.object')->getArgument(6)); - $this->assertEquals($container->getDefinition('serializer.normalizer.object')->getArgument(6)['max_depth_handler'], new Reference('my.max.depth.handler')); } public function testSerializerWithoutTranslator() @@ -1547,13 +1546,22 @@ public function testJsonSerializableNormalizerRegistered() public function testObjectNormalizerRegistered() { - $container = $this->createContainerFromFile('full'); + $container = $this->createContainerFromFile('full', compile: false); + $container->addCompilerPass(new SerializerPass()); + $container->addCompilerPass(new ResolveBindingsPass()); + $container->compile(); $definition = $container->getDefinition('serializer.normalizer.object'); $tag = $definition->getTag('serializer.normalizer'); $this->assertEquals(ObjectNormalizer::class, $definition->getClass()); $this->assertEquals(-1000, $tag[0]['priority']); + + $this->assertEquals([ + 'enable_max_depth' => true, + 'circular_reference_handler' => new Reference('my.circular.reference.handler'), + 'max_depth_handler' => new Reference('my.max.depth.handler'), + ], $definition->getArgument(6)); } public function testConstraintViolationListNormalizerRegistered() diff --git a/composer.json b/composer.json index 9b3e7c86e..6689b61b0 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,7 @@ "symfony/scheduler": "^6.4.4|^7.0.4", "symfony/security-bundle": "^6.4|^7.0", "symfony/semaphore": "^6.4|^7.0", - "symfony/serializer": "^7.1", + "symfony/serializer": "^7.2.5", "symfony/stopwatch": "^6.4|^7.0", "symfony/string": "^6.4|^7.0", "symfony/translation": "^6.4|^7.0", @@ -97,7 +97,7 @@ "symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4", "symfony/security-csrf": "<7.2", "symfony/security-core": "<6.4", - "symfony/serializer": "<7.1", + "symfony/serializer": "<7.2.5", "symfony/stopwatch": "<6.4", "symfony/translation": "<6.4", "symfony/twig-bridge": "<6.4", From f1822e2c542c36574b95bd8cb4c37967764ea857 Mon Sep 17 00:00:00 2001 From: soyuka Date: Thu, 24 Oct 2024 19:04:50 +0200 Subject: [PATCH 238/281] [FrameworkBundle] Object Mapper component bindings --- .../Compiler/UnusedTagsPass.php | 2 ++ DependencyInjection/FrameworkExtension.php | 11 +++++++ Resources/config/object_mapper.php | 33 +++++++++++++++++++ Tests/Fixtures/ObjectMapper/ObjectMapped.php | 17 ++++++++++ .../ObjectMapper/ObjectToBeMapped.php | 21 ++++++++++++ .../ObjectMapper/TransformCallable.php | 25 ++++++++++++++ Tests/Functional/ObjectMapperTest.php | 31 +++++++++++++++++ Tests/Functional/app/ObjectMapper/bundles.php | 16 +++++++++ Tests/Functional/app/ObjectMapper/config.yml | 9 +++++ composer.json | 1 + 10 files changed, 166 insertions(+) create mode 100644 Resources/config/object_mapper.php create mode 100644 Tests/Fixtures/ObjectMapper/ObjectMapped.php create mode 100644 Tests/Fixtures/ObjectMapper/ObjectToBeMapped.php create mode 100644 Tests/Fixtures/ObjectMapper/TransformCallable.php create mode 100644 Tests/Functional/ObjectMapperTest.php create mode 100644 Tests/Functional/app/ObjectMapper/bundles.php create mode 100644 Tests/Functional/app/ObjectMapper/config.yml diff --git a/DependencyInjection/Compiler/UnusedTagsPass.php b/DependencyInjection/Compiler/UnusedTagsPass.php index c8271d463..53361e312 100644 --- a/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/DependencyInjection/Compiler/UnusedTagsPass.php @@ -106,6 +106,8 @@ class UnusedTagsPass implements CompilerPassInterface 'validator.group_provider', 'validator.initializer', 'workflow', + 'object_mapper.transform_callable', + 'object_mapper.condition_callable', ]; public function process(ContainerBuilder $container): void diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index fed06a6c7..5044e3cc2 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -144,6 +144,9 @@ use Symfony\Component\Notifier\Recipient\Recipient; use Symfony\Component\Notifier\TexterInterface; use Symfony\Component\Notifier\Transport\TransportFactoryInterface as NotifierTransportFactoryInterface; +use Symfony\Component\ObjectMapper\ConditionCallableInterface; +use Symfony\Component\ObjectMapper\ObjectMapperInterface; +use Symfony\Component\ObjectMapper\TransformCallableInterface; use Symfony\Component\Process\Messenger\RunProcessMessageHandler; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyInfo\Extractor\ConstructorArgumentTypeExtractorInterface; @@ -863,6 +866,14 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont if (!ContainerBuilder::willBeAvailable('symfony/translation', Translator::class, ['symfony/framework-bundle', 'symfony/form'])) { $container->removeDefinition('form.type_extension.upload.validator'); } + + if (ContainerBuilder::willBeAvailable('symfony/object-mapper', ObjectMapperInterface::class, ['symfony/framework-bundle'])) { + $loader->load('object_mapper.php'); + $container->registerForAutoconfiguration(TransformCallableInterface::class) + ->addTag('object_mapper.transform_callable'); + $container->registerForAutoconfiguration(ConditionCallableInterface::class) + ->addTag('object_mapper.condition_callable'); + } } private function registerHttpCacheConfiguration(array $config, ContainerBuilder $container, bool $httpMethodOverride): void diff --git a/Resources/config/object_mapper.php b/Resources/config/object_mapper.php new file mode 100644 index 000000000..8addad4da --- /dev/null +++ b/Resources/config/object_mapper.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\ObjectMapper\Metadata\ObjectMapperMetadataFactoryInterface; +use Symfony\Component\ObjectMapper\Metadata\ReflectionObjectMapperMetadataFactory; +use Symfony\Component\ObjectMapper\ObjectMapper; +use Symfony\Component\ObjectMapper\ObjectMapperInterface; + +return static function (ContainerConfigurator $container) { + $container->services() + ->set('object_mapper.metadata_factory', ReflectionObjectMapperMetadataFactory::class) + ->alias(ObjectMapperMetadataFactoryInterface::class, 'object_mapper.metadata_factory') + + ->set('object_mapper', ObjectMapper::class) + ->args([ + service('object_mapper.metadata_factory'), + service('property_accessor')->ignoreOnInvalid(), + tagged_locator('object_mapper.transform_callable'), + tagged_locator('object_mapper.condition_callable'), + ]) + ->alias(ObjectMapperInterface::class, 'object_mapper') + ; +}; diff --git a/Tests/Fixtures/ObjectMapper/ObjectMapped.php b/Tests/Fixtures/ObjectMapper/ObjectMapped.php new file mode 100644 index 000000000..17edc9dce --- /dev/null +++ b/Tests/Fixtures/ObjectMapper/ObjectMapped.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ObjectMapper; + +final class ObjectMapped +{ + public string $a; +} diff --git a/Tests/Fixtures/ObjectMapper/ObjectToBeMapped.php b/Tests/Fixtures/ObjectMapper/ObjectToBeMapped.php new file mode 100644 index 000000000..fc5b7080a --- /dev/null +++ b/Tests/Fixtures/ObjectMapper/ObjectToBeMapped.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ObjectMapper; + +use Symfony\Component\ObjectMapper\Attribute\Map; + +#[Map(target: ObjectMapped::class)] +final class ObjectToBeMapped +{ + #[Map(transform: TransformCallable::class)] + public string $a = 'nottransformed'; +} diff --git a/Tests/Fixtures/ObjectMapper/TransformCallable.php b/Tests/Fixtures/ObjectMapper/TransformCallable.php new file mode 100644 index 000000000..da4f26a2d --- /dev/null +++ b/Tests/Fixtures/ObjectMapper/TransformCallable.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ObjectMapper; + +use Symfony\Component\ObjectMapper\TransformCallableInterface; + +/** + * @implements TransformCallableInterface + */ +final class TransformCallable implements TransformCallableInterface +{ + public function __invoke(mixed $value, object $object): mixed + { + return 'transformed'; + } +} diff --git a/Tests/Functional/ObjectMapperTest.php b/Tests/Functional/ObjectMapperTest.php new file mode 100644 index 000000000..e314ee1b0 --- /dev/null +++ b/Tests/Functional/ObjectMapperTest.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ObjectMapper\ObjectMapped; +use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ObjectMapper\ObjectToBeMapped; + +/** + * @author Kévin Dunglas + */ +class ObjectMapperTest extends AbstractWebTestCase +{ + public function testObjectMapper() + { + static::bootKernel(['test_case' => 'ObjectMapper']); + + /** @var Symfony\Component\ObjectMapper\ObjectMapperInterface */ + $objectMapper = static::getContainer()->get('object_mapper.alias'); + $mapped = $objectMapper->map(new ObjectToBeMapped()); + $this->assertSame($mapped->a, 'transformed'); + } +} diff --git a/Tests/Functional/app/ObjectMapper/bundles.php b/Tests/Functional/app/ObjectMapper/bundles.php new file mode 100644 index 000000000..13ab9fdde --- /dev/null +++ b/Tests/Functional/app/ObjectMapper/bundles.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; + +return [ + new FrameworkBundle(), +]; diff --git a/Tests/Functional/app/ObjectMapper/config.yml b/Tests/Functional/app/ObjectMapper/config.yml new file mode 100644 index 000000000..3e3bd8702 --- /dev/null +++ b/Tests/Functional/app/ObjectMapper/config.yml @@ -0,0 +1,9 @@ +imports: + - { resource: ../config/default.yml } + +services: + object_mapper.alias: + alias: object_mapper + public: true + Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ObjectMapper\TransformCallable: + autoconfigure: true diff --git a/composer.json b/composer.json index 72d33ed88..6ac3491c7 100644 --- a/composer.json +++ b/composer.json @@ -54,6 +54,7 @@ "symfony/messenger": "^6.4|^7.0", "symfony/mime": "^6.4|^7.0", "symfony/notifier": "^6.4|^7.0", + "symfony/object-mapper": "^7.3", "symfony/process": "^6.4|^7.0", "symfony/rate-limiter": "^6.4|^7.0", "symfony/scheduler": "^6.4.4|^7.0.4", From 3fca2f034730296f4f4ced6da97c7b903d20ef9b Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 26 Mar 2025 09:40:47 +0100 Subject: [PATCH 239/281] =?UTF-8?q?[FrameworkBundle]=C2=A0Add=20missing=20?= =?UTF-8?q?line=20in=20CHANGELOG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63eca480b..fa7d0a778 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.3 --- + * Add support for the ObjectMapper component * Add support for assets pre-compression * Rename `TranslationUpdateCommand` to `TranslationExtractCommand` * Add JsonStreamer services and configuration From c81e1a7882cfc1f37e34aaaf3bfec2eb1c19b1c8 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Fri, 28 Mar 2025 09:22:25 -0400 Subject: [PATCH 240/281] Deprecate returning a non-integer value from a `\Closure` function set via `Command::setCode()` --- Tests/Console/ApplicationTest.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Tests/Console/ApplicationTest.php b/Tests/Console/ApplicationTest.php index 9afb5a2fd..0b92a813c 100644 --- a/Tests/Console/ApplicationTest.php +++ b/Tests/Console/ApplicationTest.php @@ -135,7 +135,11 @@ public function testRunOnlyWarnsOnUnregistrableCommand() $kernel ->method('getBundles') ->willReturn([$this->createBundleMock( - [(new Command('fine'))->setCode(function (InputInterface $input, OutputInterface $output) { $output->write('fine'); })] + [(new Command('fine'))->setCode(function (InputInterface $input, OutputInterface $output): int { + $output->write('fine'); + + return 0; + })] )]); $kernel ->method('getContainer') @@ -163,7 +167,11 @@ public function testRegistrationErrorsAreDisplayedOnCommandNotFound() $kernel ->method('getBundles') ->willReturn([$this->createBundleMock( - [(new Command(null))->setCode(function (InputInterface $input, OutputInterface $output) { $output->write('fine'); })] + [(new Command(null))->setCode(function (InputInterface $input, OutputInterface $output): int { + $output->write('fine'); + + return 0; + })] )]); $kernel ->method('getContainer') @@ -193,7 +201,11 @@ public function testRunOnlyWarnsOnUnregistrableCommandAtTheEnd() $kernel ->method('getBundles') ->willReturn([$this->createBundleMock( - [(new Command('fine'))->setCode(function (InputInterface $input, OutputInterface $output) { $output->write('fine'); })] + [(new Command('fine'))->setCode(function (InputInterface $input, OutputInterface $output): int { + $output->write('fine'); + + return 0; + })] )]); $kernel ->method('getContainer') From 8d83a9020ad88075f2abd655e1819359bd56a636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Fri, 28 Mar 2025 23:25:48 +0100 Subject: [PATCH 241/281] Enable controller service with #[Route] attribute instead of #[AsController] --- CHANGELOG.md | 2 ++ DependencyInjection/FrameworkExtension.php | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa7d0a778..997564262 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ CHANGELOG * Add DI alias from `ServicesResetterInterface` to `services_resetter` * Add `methods` argument in `#[IsCsrfTokenValid]` attribute * Allow configuring the logging channel per type of exceptions + * Enable service argument resolution on classes that use the `#[Route]` attribute, + the `#[AsController]` attribute is no longer required 7.2 --- diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 0015aee41..7e500af88 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -164,6 +164,7 @@ use Symfony\Component\RateLimiter\Storage\CacheStorage; use Symfony\Component\RemoteEvent\Attribute\AsRemoteEventConsumer; use Symfony\Component\RemoteEvent\RemoteEvent; +use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Scheduler\Attribute\AsCronTask; use Symfony\Component\Scheduler\Attribute\AsPeriodicTask; use Symfony\Component\Scheduler\Attribute\AsSchedule; @@ -429,7 +430,7 @@ public function load(array $configs, ContainerBuilder $container): void } $loggers[$exception['log_channel']] = new Reference('monolog.logger.'.$exception['log_channel'], ContainerInterface::NULL_ON_INVALID_REFERENCE); } - + $exceptionListener ->replaceArgument(3, $config['exceptions']) ->setArgument(4, $loggers) @@ -739,6 +740,9 @@ public function load(array $configs, ContainerBuilder $container): void $container->registerAttributeForAutoconfiguration(AsController::class, static function (ChildDefinition $definition, AsController $attribute): void { $definition->addTag('controller.service_arguments'); }); + $container->registerAttributeForAutoconfiguration(Route::class, static function (ChildDefinition $definition, Route $attribute, \ReflectionClass|\ReflectionMethod $reflection): void { + $definition->addTag('controller.service_arguments'); + }); $container->registerAttributeForAutoconfiguration(AsRemoteEventConsumer::class, static function (ChildDefinition $definition, AsRemoteEventConsumer $attribute): void { $definition->addTag('remote_event.consumer', ['consumer' => $attribute->name]); }); From 33a91b7bc9957ca6b988fe5db0c7554d4358d3f1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 31 Mar 2025 14:55:09 +0200 Subject: [PATCH 242/281] [FrameworkBundle] Exclude validator constrains, attributes, enums from the container --- DependencyInjection/FrameworkExtension.php | 24 ++++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 7e500af88..d76aa8cd6 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -204,6 +204,7 @@ use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface; use Symfony\Component\Uid\Factory\UuidFactory; use Symfony\Component\Uid\UuidV4; +use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\ExpressionLanguageProvider; use Symfony\Component\Validator\ConstraintValidatorInterface; use Symfony\Component\Validator\GroupProviderInterface; @@ -786,29 +787,34 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu } $container->registerForAutoconfiguration(CompilerPassInterface::class) - ->addTag('container.excluded.compiler_pass')->addTag('container.excluded')->setAbstract(true); + ->addTag('container.excluded', ['source' => 'because it\'s a compiler pass'])->setAbstract(true); + $container->registerForAutoconfiguration(Constraint::class) + ->addTag('container.excluded', ['source' => 'because it\'s a validation constraint'])->setAbstract(true); $container->registerForAutoconfiguration(TestCase::class) - ->addTag('container.excluded.test_case')->addTag('container.excluded')->setAbstract(true); + ->addTag('container.excluded', ['source' => 'because it\'s a test case'])->setAbstract(true); + $container->registerForAutoconfiguration(\UnitEnum::class) + ->addTag('container.excluded', ['source' => 'because it\'s an enum'])->setAbstract(true); $container->registerAttributeForAutoconfiguration(AsMessage::class, static function (ChildDefinition $definition) { - $definition->addTag('container.excluded.messenger.message')->addTag('container.excluded')->setAbstract(true); + $definition->addTag('container.excluded', ['source' => 'because it\'s a messenger message'])->setAbstract(true); + }); + $container->registerAttributeForAutoconfiguration(\Attribute::class, static function (ChildDefinition $definition) { + $definition->addTag('container.excluded', ['source' => 'because it\'s an attribute'])->setAbstract(true); }); $container->registerAttributeForAutoconfiguration(Entity::class, static function (ChildDefinition $definition) { - $definition->addTag('container.excluded.doctrine.entity')->addTag('container.excluded')->setAbstract(true); + $definition->addTag('container.excluded', ['source' => 'because it\'s a doctrine entity'])->setAbstract(true); }); $container->registerAttributeForAutoconfiguration(Embeddable::class, static function (ChildDefinition $definition) { - $definition->addTag('container.excluded.doctrine.embeddable')->addTag('container.excluded')->setAbstract(true); + $definition->addTag('container.excluded', ['source' => 'because it\'s a doctrine embeddable'])->setAbstract(true); }); $container->registerAttributeForAutoconfiguration(MappedSuperclass::class, static function (ChildDefinition $definition) { - $definition->addTag('container.excluded.doctrine.mapped_superclass')->addTag('container.excluded')->setAbstract(true); + $definition->addTag('container.excluded', ['source' => 'because it\'s a doctrine mapped superclass'])->setAbstract(true); }); $container->registerAttributeForAutoconfiguration(JsonStreamable::class, static function (ChildDefinition $definition, JsonStreamable $attribute) { $definition->addTag('json_streamer.streamable', [ 'object' => $attribute->asObject, 'list' => $attribute->asList, - ]); - $definition->addTag('container.excluded'); - $definition->setAbstract(true); + ])->addTag('container.excluded', ['source' => 'because it\'s a streamable JSON'])->setAbstract(true); }); if (!$container->getParameter('kernel.debug')) { From ea981cce0ee667131b4a50c03448aa23cca74ff2 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Fri, 28 Mar 2025 09:42:19 +0100 Subject: [PATCH 243/281] [FrameworkBundle] Deprecate setting the `collect_serializer_data` to `false` --- CHANGELOG.md | 1 + DependencyInjection/FrameworkExtension.php | 4 ++++ .../DependencyInjection/Fixtures/php/profiler.php | 1 + .../php/profiler_collect_serializer_data.php | 15 --------------- .../DependencyInjection/Fixtures/xml/profiler.xml | 2 +- .../xml/profiler_collect_serializer_data.xml | 15 --------------- .../DependencyInjection/Fixtures/yml/profiler.yml | 1 + .../yml/profiler_collect_serializer_data.yml | 11 ----------- .../FrameworkExtensionTestCase.php | 11 +---------- Tests/Functional/app/config/framework.yml | 2 ++ 10 files changed, 11 insertions(+), 52 deletions(-) delete mode 100644 Tests/DependencyInjection/Fixtures/php/profiler_collect_serializer_data.php delete mode 100644 Tests/DependencyInjection/Fixtures/xml/profiler_collect_serializer_data.xml delete mode 100644 Tests/DependencyInjection/Fixtures/yml/profiler_collect_serializer_data.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 997564262..6c4daeb6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ CHANGELOG * Allow configuring the logging channel per type of exceptions * Enable service argument resolution on classes that use the `#[Route]` attribute, the `#[AsController]` attribute is no longer required + * Deprecate setting the `framework.profiler.collect_serializer_data` config option to `false` 7.2 --- diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 7e500af88..f6440e3de 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -988,6 +988,10 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ $loader->load('notifier_debug.php'); } + if (false === $config['collect_serializer_data']) { + trigger_deprecation('symfony/framework-bundle', '7.3', 'Setting the "framework.profiler.collect_serializer_data" config option to "false" is deprecated.'); + } + if ($this->isInitializedConfigEnabled('serializer') && $config['collect_serializer_data']) { $loader->load('serializer_debug.php'); } diff --git a/Tests/DependencyInjection/Fixtures/php/profiler.php b/Tests/DependencyInjection/Fixtures/php/profiler.php index faf76bbc7..99e2a52cf 100644 --- a/Tests/DependencyInjection/Fixtures/php/profiler.php +++ b/Tests/DependencyInjection/Fixtures/php/profiler.php @@ -7,6 +7,7 @@ 'php_errors' => ['log' => true], 'profiler' => [ 'enabled' => true, + 'collect_serializer_data' => true, ], 'serializer' => [ 'enabled' => true, diff --git a/Tests/DependencyInjection/Fixtures/php/profiler_collect_serializer_data.php b/Tests/DependencyInjection/Fixtures/php/profiler_collect_serializer_data.php deleted file mode 100644 index 99e2a52cf..000000000 --- a/Tests/DependencyInjection/Fixtures/php/profiler_collect_serializer_data.php +++ /dev/null @@ -1,15 +0,0 @@ -loadFromExtension('framework', [ - 'annotations' => false, - 'http_method_override' => false, - 'handle_all_throwables' => true, - 'php_errors' => ['log' => true], - 'profiler' => [ - 'enabled' => true, - 'collect_serializer_data' => true, - ], - 'serializer' => [ - 'enabled' => true, - ], -]); diff --git a/Tests/DependencyInjection/Fixtures/xml/profiler.xml b/Tests/DependencyInjection/Fixtures/xml/profiler.xml index ffbff7f21..34d44d91c 100644 --- a/Tests/DependencyInjection/Fixtures/xml/profiler.xml +++ b/Tests/DependencyInjection/Fixtures/xml/profiler.xml @@ -9,7 +9,7 @@ - + diff --git a/Tests/DependencyInjection/Fixtures/xml/profiler_collect_serializer_data.xml b/Tests/DependencyInjection/Fixtures/xml/profiler_collect_serializer_data.xml deleted file mode 100644 index 34d44d91c..000000000 --- a/Tests/DependencyInjection/Fixtures/xml/profiler_collect_serializer_data.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/Tests/DependencyInjection/Fixtures/yml/profiler.yml b/Tests/DependencyInjection/Fixtures/yml/profiler.yml index 5c867fc89..2ccec1685 100644 --- a/Tests/DependencyInjection/Fixtures/yml/profiler.yml +++ b/Tests/DependencyInjection/Fixtures/yml/profiler.yml @@ -6,5 +6,6 @@ framework: log: true profiler: enabled: true + collect_serializer_data: true serializer: enabled: true diff --git a/Tests/DependencyInjection/Fixtures/yml/profiler_collect_serializer_data.yml b/Tests/DependencyInjection/Fixtures/yml/profiler_collect_serializer_data.yml deleted file mode 100644 index 5fe74b290..000000000 --- a/Tests/DependencyInjection/Fixtures/yml/profiler_collect_serializer_data.yml +++ /dev/null @@ -1,11 +0,0 @@ -framework: - annotations: false - http_method_override: false - handle_all_throwables: true - php_errors: - log: true - serializer: - enabled: true - profiler: - enabled: true - collect_serializer_data: true diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 8bddf53be..d942c122c 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -278,22 +278,13 @@ public function testDisabledProfiler() public function testProfilerCollectSerializerDataEnabled() { - $container = $this->createContainerFromFile('profiler_collect_serializer_data'); + $container = $this->createContainerFromFile('profiler'); $this->assertTrue($container->hasDefinition('profiler')); $this->assertTrue($container->hasDefinition('serializer.data_collector')); $this->assertTrue($container->hasDefinition('debug.serializer')); } - public function testProfilerCollectSerializerDataDefaultDisabled() - { - $container = $this->createContainerFromFile('profiler'); - - $this->assertTrue($container->hasDefinition('profiler')); - $this->assertFalse($container->hasDefinition('serializer.data_collector')); - $this->assertFalse($container->hasDefinition('debug.serializer')); - } - public function testWorkflows() { $container = $this->createContainerFromFile('workflows'); diff --git a/Tests/Functional/app/config/framework.yml b/Tests/Functional/app/config/framework.yml index 1eaee513c..ac051614b 100644 --- a/Tests/Functional/app/config/framework.yml +++ b/Tests/Functional/app/config/framework.yml @@ -18,6 +18,8 @@ framework: cookie_samesite: lax php_errors: log: true + profiler: + collect_serializer_data: true services: logger: { class: Psr\Log\NullLogger } From d4d23025226aff76e03259f6979dfa3db0db1d07 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Mon, 31 Mar 2025 11:48:18 -0400 Subject: [PATCH 244/281] [FrameworkBundle][RateLimiter] default `lock_factory` to `auto` --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 2 +- DependencyInjection/FrameworkExtension.php | 4 +++ .../PhpFrameworkExtensionTest.php | 31 +++++++++++++++++-- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c4daeb6d..b7efe5a18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ CHANGELOG * Enable service argument resolution on classes that use the `#[Route]` attribute, the `#[AsController]` attribute is no longer required * Deprecate setting the `framework.profiler.collect_serializer_data` config option to `false` + * Set `framework.rate_limiter.limiters.*.lock_factory` to `auto` by default 7.2 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index aa61cb12c..7f37b5216 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -2504,7 +2504,7 @@ private function addRateLimiterSection(ArrayNodeDefinition $rootNode, callable $ ->children() ->scalarNode('lock_factory') ->info('The service ID of the lock factory used by this limiter (or null to disable locking).') - ->defaultValue('lock.factory') + ->defaultValue('auto') ->end() ->scalarNode('cache_pool') ->info('The cache pool to use for storing the current limiter state.') diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 1a1bcdd16..98e2e8904 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -3239,6 +3239,10 @@ private function registerRateLimiterConfiguration(array $config, ContainerBuilde $limiter = $container->setDefinition($limiterId = 'limiter.'.$name, new ChildDefinition('limiter')) ->addTag('rate_limiter', ['name' => $name]); + if ('auto' === $limiterConfig['lock_factory']) { + $limiterConfig['lock_factory'] = $this->isInitializedConfigEnabled('lock') ? 'lock.factory' : null; + } + if (null !== $limiterConfig['lock_factory']) { if (!interface_exists(LockInterface::class)) { throw new LogicException(\sprintf('Rate limiter "%s" requires the Lock component to be installed. Try running "composer require symfony/lock".', $name)); diff --git a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index deac159b6..ea8d481e0 100644 --- a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -188,7 +188,7 @@ public function testWorkflowDefaultMarkingStoreDefinition() $this->assertNull($argumentsB['index_1'], 'workflow_b marking_store argument is null'); } - public function testRateLimiterWithLockFactory() + public function testRateLimiterLockFactoryWithLockDisabled() { try { $this->createContainerFromClosure(function (ContainerBuilder $container) { @@ -199,7 +199,7 @@ public function testRateLimiterWithLockFactory() 'php_errors' => ['log' => true], 'lock' => false, 'rate_limiter' => [ - 'with_lock' => ['policy' => 'fixed_window', 'limit' => 10, 'interval' => '1 hour'], + 'with_lock' => ['policy' => 'fixed_window', 'limit' => 10, 'interval' => '1 hour', 'lock_factory' => 'lock.factory'], ], ]); }); @@ -208,7 +208,10 @@ public function testRateLimiterWithLockFactory() } catch (LogicException $e) { $this->assertEquals('Rate limiter "with_lock" requires the Lock component to be configured.', $e->getMessage()); } + } + public function testRateLimiterAutoLockFactoryWithLockEnabled() + { $container = $this->createContainerFromClosure(function (ContainerBuilder $container) { $container->loadFromExtension('framework', [ 'annotations' => false, @@ -226,13 +229,35 @@ public function testRateLimiterWithLockFactory() $this->assertEquals('lock.factory', (string) $withLock->getArgument(2)); } - public function testRateLimiterLockFactory() + public function testRateLimiterAutoLockFactoryWithLockDisabled() { $container = $this->createContainerFromClosure(function (ContainerBuilder $container) { $container->loadFromExtension('framework', [ 'annotations' => false, 'http_method_override' => false, 'handle_all_throwables' => true, + 'lock' => false, + 'php_errors' => ['log' => true], + 'rate_limiter' => [ + 'without_lock' => ['policy' => 'fixed_window', 'limit' => 10, 'interval' => '1 hour'], + ], + ]); + }); + + $this->expectException(OutOfBoundsException::class); + $this->expectExceptionMessageMatches('/^The argument "2" doesn\'t exist.*\.$/'); + + $container->getDefinition('limiter.without_lock')->getArgument(2); + } + + public function testRateLimiterDisableLockFactory() + { + $container = $this->createContainerFromClosure(function (ContainerBuilder $container) { + $container->loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'lock' => true, 'php_errors' => ['log' => true], 'rate_limiter' => [ 'without_lock' => ['policy' => 'fixed_window', 'limit' => 10, 'interval' => '1 hour', 'lock_factory' => null], From eab16fe6e61f0df51c30ca2589d8cf3f585633b8 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 12 Feb 2025 19:13:00 +0100 Subject: [PATCH 245/281] [Config] Add `NodeDefinition::docUrl()` --- Command/ConfigDebugCommand.php | 15 +++++++++++++++ Command/ConfigDumpReferenceCommand.php | 19 +++++++++++++++++++ DependencyInjection/Configuration.php | 1 + 3 files changed, 35 insertions(+) diff --git a/Command/ConfigDebugCommand.php b/Command/ConfigDebugCommand.php index 55c101e9c..8d5f85cee 100644 --- a/Command/ConfigDebugCommand.php +++ b/Command/ConfigDebugCommand.php @@ -104,6 +104,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->title( \sprintf('Current configuration for %s', $name === $extensionAlias ? \sprintf('extension with alias "%s"', $extensionAlias) : \sprintf('"%s"', $name)) ); + + if ($docUrl = $this->getDocUrl($extension, $container)) { + $io->comment(\sprintf('Documentation at %s', $docUrl)); + } } $io->writeln($this->convertToFormat([$extensionAlias => $config], $format)); @@ -269,4 +273,15 @@ private function getAvailableFormatOptions(): array { return ['txt', 'yaml', 'json']; } + + private function getDocUrl(ExtensionInterface $extension, ContainerBuilder $container): ?string + { + $configuration = $extension instanceof ConfigurationInterface ? $extension : $extension->getConfiguration($container->getExtensionConfig($extension->getAlias()), $container); + + return $configuration + ->getConfigTreeBuilder() + ->getRootNode() + ->getNode(true) + ->getAttribute('docUrl'); + } } diff --git a/Command/ConfigDumpReferenceCommand.php b/Command/ConfigDumpReferenceCommand.php index 7e5cd765f..3cb744d74 100644 --- a/Command/ConfigDumpReferenceCommand.php +++ b/Command/ConfigDumpReferenceCommand.php @@ -23,6 +23,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface; use Symfony\Component\Yaml\Yaml; /** @@ -123,6 +124,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int $message .= \sprintf(' at path "%s"', $path); } + if ($docUrl = $this->getExtensionDocUrl($extension)) { + $message .= \sprintf(' (see %s)', $docUrl); + } + switch ($format) { case 'yaml': $io->writeln(\sprintf('# %s', $message)); @@ -182,4 +187,18 @@ private function getAvailableFormatOptions(): array { return ['yaml', 'xml']; } + + private function getExtensionDocUrl(ConfigurationInterface|ConfigurationExtensionInterface $extension): ?string + { + $kernel = $this->getApplication()->getKernel(); + $container = $this->getContainerBuilder($kernel); + + $configuration = $extension instanceof ConfigurationInterface ? $extension : $extension->getConfiguration($container->getExtensionConfig($extension->getAlias()), $container); + + return $configuration + ->getConfigTreeBuilder() + ->getRootNode() + ->getNode(true) + ->getAttribute('docUrl'); + } } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index aa61cb12c..0f882d356 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -75,6 +75,7 @@ public function getConfigTreeBuilder(): TreeBuilder $rootNode = $treeBuilder->getRootNode(); $rootNode + ->docUrl('https://symfony.com/doc/{version:major}.{version:minor}/reference/configuration/framework.html', 'symfony/framework-bundle') ->beforeNormalization() ->ifTrue(fn ($v) => !isset($v['assets']) && isset($v['templating']) && class_exists(Package::class)) ->then(function ($v) { From 3a074477a48991c11d8441e490187e74ce85720c Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Mon, 7 Apr 2025 16:05:31 -0400 Subject: [PATCH 246/281] [FrameworkBundle][RateLimiter] deprecate `RateLimiterFactory` alias --- CHANGELOG.md | 1 + DependencyInjection/FrameworkExtension.php | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7efe5a18..2f145d165 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ CHANGELOG the `#[AsController]` attribute is no longer required * Deprecate setting the `framework.profiler.collect_serializer_data` config option to `false` * Set `framework.rate_limiter.limiters.*.lock_factory` to `auto` by default + * Deprecate `RateLimiterFactory` autowiring aliases, use `RateLimiterFactoryInterface` instead 7.2 --- diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 98e2e8904..814397d9c 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -3266,10 +3266,11 @@ private function registerRateLimiterConfiguration(array $config, ContainerBuilde $limiterConfig['id'] = $name; $limiter->replaceArgument(0, $limiterConfig); - $container->registerAliasForArgument($limiterId, RateLimiterFactory::class, $name.'.limiter'); + $factoryAlias = $container->registerAliasForArgument($limiterId, RateLimiterFactory::class, $name.'.limiter'); 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.'); } } } From 80403e0cceec80bdfd33e958e3519615218eaab0 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Sat, 5 Apr 2025 10:05:38 -0400 Subject: [PATCH 247/281] [FrameworkBundle][RateLimiter] compound rate limiter config --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 11 ++- DependencyInjection/FrameworkExtension.php | 37 +++++++++ .../PhpFrameworkExtensionTest.php | 75 +++++++++++++++++++ 4 files changed, 121 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f145d165..8e70fb98e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ CHANGELOG * Deprecate setting the `framework.profiler.collect_serializer_data` config option to `false` * Set `framework.rate_limiter.limiters.*.lock_factory` to `auto` by default * Deprecate `RateLimiterFactory` autowiring aliases, use `RateLimiterFactoryInterface` instead + * Allow configuring compound rate limiters 7.2 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 6dc1b7d6e..6b168a2d4 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -2518,7 +2518,12 @@ private function addRateLimiterSection(ArrayNodeDefinition $rootNode, callable $ ->enumNode('policy') ->info('The algorithm to be used by this limiter.') ->isRequired() - ->values(['fixed_window', 'token_bucket', 'sliding_window', 'no_limit']) + ->values(['fixed_window', 'token_bucket', 'sliding_window', 'compound', 'no_limit']) + ->end() + ->arrayNode('limiters') + ->info('The limiter names to use when using the "compound" policy.') + ->beforeNormalization()->castToArray()->end() + ->scalarPrototype()->end() ->end() ->integerNode('limit') ->info('The maximum allowed hits in a fixed interval or burst.') @@ -2537,8 +2542,8 @@ private function addRateLimiterSection(ArrayNodeDefinition $rootNode, callable $ ->end() ->end() ->validate() - ->ifTrue(fn ($v) => 'no_limit' !== $v['policy'] && !isset($v['limit'])) - ->thenInvalid('A limit must be provided when using a policy different than "no_limit".') + ->ifTrue(static fn ($v) => !\in_array($v['policy'], ['no_limit', 'compound']) && !isset($v['limit'])) + ->thenInvalid('A limit must be provided when using a policy different than "compound" or "no_limit".') ->end() ->end() ->end() diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 814397d9c..716c11b63 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -59,6 +59,7 @@ use Symfony\Component\Console\Debug\CliRequest; use Symfony\Component\Console\Messenger\RunCommandMessageHandler; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -158,6 +159,7 @@ use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface; use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; +use Symfony\Component\RateLimiter\CompoundRateLimiterFactory; use Symfony\Component\RateLimiter\LimiterInterface; use Symfony\Component\RateLimiter\RateLimiterFactory; use Symfony\Component\RateLimiter\RateLimiterFactoryInterface; @@ -3232,7 +3234,18 @@ private function registerRateLimiterConfiguration(array $config, ContainerBuilde { $loader->load('rate_limiter.php'); + $limiters = []; + $compoundLimiters = []; + foreach ($config['limiters'] as $name => $limiterConfig) { + if ('compound' === $limiterConfig['policy']) { + $compoundLimiters[$name] = $limiterConfig; + + continue; + } + + $limiters[] = $name; + // default configuration (when used by other DI extensions) $limiterConfig += ['lock_factory' => 'lock.factory', 'cache_pool' => 'cache.rate_limiter']; @@ -3273,6 +3286,30 @@ private function registerRateLimiterConfiguration(array $config, ContainerBuilde $factoryAlias->setDeprecated('symfony/dependency-injection', '7.3', 'The "%alias_id%" autowiring alias is deprecated and will be removed in 8.0, use "RateLimiterFactoryInterface" instead.'); } } + + if ($compoundLimiters && !class_exists(CompoundRateLimiterFactory::class)) { + throw new LogicException('Configuring compound rate limiters is only available in symfony/rate-limiter 7.3+.'); + } + + foreach ($compoundLimiters as $name => $limiterConfig) { + if (!$limiterConfig['limiters']) { + throw new LogicException(\sprintf('Compound rate limiter "%s" requires at least one sub-limiter.', $name)); + } + + if (\array_diff($limiterConfig['limiters'], $limiters)) { + throw new LogicException(\sprintf('Compound rate limiter "%s" requires at least one sub-limiter to be configured.', $name)); + } + + $container->register($limiterId = 'limiter.'.$name, CompoundRateLimiterFactory::class) + ->addTag('rate_limiter', ['name' => $name]) + ->addArgument(new IteratorArgument(\array_map( + static fn (string $name) => new Reference('limiter.'.$name), + $limiterConfig['limiters'] + ))) + ; + + $container->registerAliasForArgument($limiterId, RateLimiterFactoryInterface::class, $name.'.limiter'); + } } private function registerUidConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void diff --git a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index ea8d481e0..a7606b683 100644 --- a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -17,6 +17,8 @@ use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\RateLimiter\CompoundRateLimiterFactory; +use Symfony\Component\RateLimiter\RateLimiterFactoryInterface; use Symfony\Component\Workflow\Exception\InvalidDefinitionException; class PhpFrameworkExtensionTest extends FrameworkExtensionTestCase @@ -290,4 +292,77 @@ public function testRateLimiterIsTagged() $this->assertSame('first', $container->getDefinition('limiter.first')->getTag('rate_limiter')[0]['name']); $this->assertSame('second', $container->getDefinition('limiter.second')->getTag('rate_limiter')[0]['name']); } + + public function testRateLimiterCompoundPolicy() + { + if (!class_exists(CompoundRateLimiterFactory::class)) { + $this->markTestSkipped('CompoundRateLimiterFactory is not available.'); + } + + $container = $this->createContainerFromClosure(function (ContainerBuilder $container) { + $container->loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'lock' => true, + 'rate_limiter' => [ + 'first' => ['policy' => 'fixed_window', 'limit' => 10, 'interval' => '1 hour'], + 'second' => ['policy' => 'sliding_window', 'limit' => 10, 'interval' => '1 hour'], + 'compound' => ['policy' => 'compound', 'limiters' => ['first', 'second']], + ], + ]); + }); + + $definition = $container->getDefinition('limiter.compound'); + $this->assertSame(CompoundRateLimiterFactory::class, $definition->getClass()); + $this->assertEquals( + [ + 'limiter.first', + 'limiter.second', + ], + $definition->getArgument(0)->getValues() + ); + $this->assertSame('limiter.compound', (string) $container->getAlias(RateLimiterFactoryInterface::class.' $compoundLimiter')); + } + + public function testRateLimiterCompoundPolicyNoLimiters() + { + if (!class_exists(CompoundRateLimiterFactory::class)) { + $this->markTestSkipped('CompoundRateLimiterFactory is not available.'); + } + + $this->expectException(\LogicException::class); + $this->createContainerFromClosure(function ($container) { + $container->loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'rate_limiter' => [ + 'compound' => ['policy' => 'compound'], + ], + ]); + }); + } + + public function testRateLimiterCompoundPolicyInvalidLimiters() + { + if (!class_exists(CompoundRateLimiterFactory::class)) { + $this->markTestSkipped('CompoundRateLimiterFactory is not available.'); + } + + $this->expectException(\LogicException::class); + $this->createContainerFromClosure(function ($container) { + $container->loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'rate_limiter' => [ + 'compound' => ['policy' => 'compound', 'limiters' => ['invalid1', 'invalid2']], + ], + ]); + }); + } } From acdab36a61f1d2cf2dad249843b1704405400546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 19 Mar 2025 10:57:26 +0100 Subject: [PATCH 248/281] [Messenger] Reset peak memory usage for each message --- Resources/config/messenger.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Resources/config/messenger.php b/Resources/config/messenger.php index 8798d5f2e..e02cd1ca3 100644 --- a/Resources/config/messenger.php +++ b/Resources/config/messenger.php @@ -18,6 +18,7 @@ use Symfony\Component\Messenger\Bridge\Redis\Transport\RedisTransportFactory; use Symfony\Component\Messenger\EventListener\AddErrorDetailsStampListener; use Symfony\Component\Messenger\EventListener\DispatchPcntlSignalListener; +use Symfony\Component\Messenger\EventListener\ResetMemoryUsageListener; use Symfony\Component\Messenger\EventListener\ResetServicesListener; use Symfony\Component\Messenger\EventListener\SendFailedMessageForRetryListener; use Symfony\Component\Messenger\EventListener\SendFailedMessageToFailureTransportListener; @@ -218,6 +219,9 @@ service('services_resetter'), ]) + ->set('messenger.listener.reset_memory_usage', ResetMemoryUsageListener::class) + ->tag('kernel.event_subscriber') + ->set('messenger.routable_message_bus', RoutableMessageBus::class) ->args([ abstract_arg('message bus locator'), From 0839cc6be4615e815c41ad94e15a35720e0de791 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 9 Apr 2025 10:47:56 +0200 Subject: [PATCH 249/281] remove service if its class does not exist --- DependencyInjection/FrameworkExtension.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 716c11b63..5595e14b3 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -126,6 +126,7 @@ use Symfony\Component\Messenger\Attribute\AsMessage; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Messenger\Bridge as MessengerBridge; +use Symfony\Component\Messenger\EventListener\ResetMemoryUsageListener; use Symfony\Component\Messenger\Handler\BatchHandlerInterface; use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\MessageBusInterface; @@ -2304,6 +2305,10 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder $container->removeDefinition('serializer.normalizer.flatten_exception'); } + if (!class_exists(ResetMemoryUsageListener::class)) { + $container->removeDefinition('messenger.listener.reset_memory_usage'); + } + if (ContainerBuilder::willBeAvailable('symfony/amqp-messenger', MessengerBridge\Amqp\Transport\AmqpTransportFactory::class, ['symfony/framework-bundle', 'symfony/messenger'])) { $container->getDefinition('messenger.transport.amqp.factory')->addTag('messenger.transport_factory'); } From 962751f8d4769f2203dd38bf7f8077c87c719085 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Tue, 15 Apr 2025 11:04:08 -0400 Subject: [PATCH 250/281] [HttpFoundation][FrameworkBundle] clock support for `UriSigner` --- Resources/config/services.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Resources/config/services.php b/Resources/config/services.php index 558c2b6d5..936867d54 100644 --- a/Resources/config/services.php +++ b/Resources/config/services.php @@ -158,6 +158,9 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : [] ->set('uri_signer', UriSigner::class) ->args([ new Parameter('kernel.secret'), + '_hash', + '_expiration', + service('clock')->nullOnInvalid(), ]) ->lazy() ->alias(UriSigner::class, 'uri_signer') From 4bd6301f4de3baa50f6e5f2149fcd7433d9fc69a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 18 Apr 2025 14:51:48 +0200 Subject: [PATCH 251/281] Don't enable tracing unless the profiler is enabled --- DependencyInjection/FrameworkExtension.php | 6 ++++++ Resources/config/debug.php | 1 + Resources/config/profiling.php | 11 +++++++++++ Resources/config/validator_debug.php | 1 + 4 files changed, 19 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 5595e14b3..f5111cd10 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -106,6 +106,7 @@ use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator; +use Symfony\Component\HttpKernel\Profiler\ProfilerStateChecker; use Symfony\Component\JsonStreamer\Attribute\JsonStreamable; use Symfony\Component\JsonStreamer\JsonStreamWriter; use Symfony\Component\JsonStreamer\StreamReaderInterface; @@ -963,6 +964,11 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ $loader->load('collectors.php'); $loader->load('cache_debug.php'); + if (!class_exists(ProfilerStateChecker::class)) { + $container->removeDefinition('profiler.state_checker'); + $container->removeDefinition('profiler.is_disabled_state_checker'); + } + if ($this->isInitializedConfigEnabled('form')) { $loader->load('form_debug.php'); } diff --git a/Resources/config/debug.php b/Resources/config/debug.php index 5c426653d..842f5b35b 100644 --- a/Resources/config/debug.php +++ b/Resources/config/debug.php @@ -25,6 +25,7 @@ service('debug.stopwatch'), service('logger')->nullOnInvalid(), service('.virtual_request_stack')->nullOnInvalid(), + service('profiler.is_disabled_state_checker')->nullOnInvalid(), ]) ->tag('monolog.logger', ['channel' => 'event']) ->tag('kernel.reset', ['method' => 'reset']) diff --git a/Resources/config/profiling.php b/Resources/config/profiling.php index 4ae34649b..68fb295bb 100644 --- a/Resources/config/profiling.php +++ b/Resources/config/profiling.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpKernel\EventListener\ProfilerListener; use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage; use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\HttpKernel\Profiler\ProfilerStateChecker; return static function (ContainerConfigurator $container) { $container->services() @@ -56,5 +57,15 @@ ->set('.virtual_request_stack', VirtualRequestStack::class) ->args([service('request_stack')]) ->public() + + ->set('profiler.state_checker', ProfilerStateChecker::class) + ->args([ + service_locator(['profiler' => service('profiler')->ignoreOnUninitialized()]), + param('kernel.runtime_mode.web'), + ]) + + ->set('profiler.is_disabled_state_checker', 'Closure') + ->factory(['Closure', 'fromCallable']) + ->args([[service('profiler.state_checker'), 'isProfilerDisabled']]) ; }; diff --git a/Resources/config/validator_debug.php b/Resources/config/validator_debug.php index e9fe44114..b195aea2b 100644 --- a/Resources/config/validator_debug.php +++ b/Resources/config/validator_debug.php @@ -20,6 +20,7 @@ ->decorate('validator', null, 255) ->args([ service('debug.validator.inner'), + service('profiler.is_disabled_state_checker')->nullOnInvalid(), ]) ->tag('kernel.reset', [ 'method' => 'reset', From 0feb2e31e5fc040b15a57823151ede071b3e8212 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 17 Apr 2025 11:41:39 +0200 Subject: [PATCH 252/281] Add PHP config support for routing --- CHANGELOG.md | 1 + Resources/config/routing/errors.php | 20 ++++++++++++++++++++ Resources/config/routing/errors.xml | 6 +----- Resources/config/routing/webhook.php | 19 +++++++++++++++++++ Resources/config/routing/webhook.xml | 5 +---- 5 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 Resources/config/routing/errors.php create mode 100644 Resources/config/routing/webhook.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e70fb98e..40289cf57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.3 --- + * Add `errors.php` and `webhook.php` routing configuration files (use them instead of their XML equivalent) * Add support for the ObjectMapper component * Add support for assets pre-compression * Rename `TranslationUpdateCommand` to `TranslationExtractCommand` diff --git a/Resources/config/routing/errors.php b/Resources/config/routing/errors.php new file mode 100644 index 000000000..11040e29a --- /dev/null +++ b/Resources/config/routing/errors.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + +return function (RoutingConfigurator $routes): void { + $routes->add('_preview_error', '/{code}.{_format}') + ->controller('error_controller::preview') + ->defaults(['_format' => 'html']) + ->requirements(['code' => '\d+']) + ; +}; diff --git a/Resources/config/routing/errors.xml b/Resources/config/routing/errors.xml index 13a9cc407..f890aef1e 100644 --- a/Resources/config/routing/errors.xml +++ b/Resources/config/routing/errors.xml @@ -4,9 +4,5 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd"> - - error_controller::preview - html - \d+ - + diff --git a/Resources/config/routing/webhook.php b/Resources/config/routing/webhook.php new file mode 100644 index 000000000..413fe6c81 --- /dev/null +++ b/Resources/config/routing/webhook.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + +return function (RoutingConfigurator $routes): void { + $routes->add('_webhook_controller', '/{type}') + ->controller('webhook_controller::handle') + ->requirements(['type' => '.+']) + ; +}; diff --git a/Resources/config/routing/webhook.xml b/Resources/config/routing/webhook.xml index dfa95cfac..8cb64ebb7 100644 --- a/Resources/config/routing/webhook.xml +++ b/Resources/config/routing/webhook.xml @@ -4,8 +4,5 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd"> - - webhook.controller::handle - .+ - + From decd9023a9e52695607b7a1bafc8c7b51fc3d900 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 28 Apr 2025 12:59:59 +0200 Subject: [PATCH 253/281] fix the upgrade instructions and trigger deprecations --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ Resources/config/routing/errors.php | 11 +++++++++++ Resources/config/routing/webhook.php | 11 +++++++++++ 3 files changed, 49 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40289cf57..935479f48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,33 @@ CHANGELOG --- * Add `errors.php` and `webhook.php` routing configuration files (use them instead of their XML equivalent) + + Before: + + ```yaml + when@dev: + _errors: + resource: '@FrameworkBundle/Resources/config/routing/errors.xml' + prefix: /_error + + webhook: + resource: '@FrameworkBundle/Resources/config/routing/webhook.xml' + prefix: /webhook + ``` + + After: + + ```yaml + when@dev: + _errors: + resource: '@FrameworkBundle/Resources/config/routing/errors.php' + prefix: /_error + + webhook: + resource: '@FrameworkBundle/Resources/config/routing/webhook.php' + prefix: /webhook + ``` + * Add support for the ObjectMapper component * Add support for assets pre-compression * Rename `TranslationUpdateCommand` to `TranslationExtractCommand` diff --git a/Resources/config/routing/errors.php b/Resources/config/routing/errors.php index 11040e29a..36a46dee4 100644 --- a/Resources/config/routing/errors.php +++ b/Resources/config/routing/errors.php @@ -10,8 +10,19 @@ */ use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; +use Symfony\Component\Routing\Loader\XmlFileLoader; return function (RoutingConfigurator $routes): void { + foreach (debug_backtrace() as $trace) { + if (isset($trace['object']) && $trace['object'] instanceof XmlFileLoader && 'doImport' === $trace['function']) { + if (__DIR__ === dirname(realpath($trace['args'][3]))) { + trigger_deprecation('symfony/routing', '7.3', 'The "errors.xml" routing configuration file is deprecated, import "errors.php" instead.'); + + break; + } + } + } + $routes->add('_preview_error', '/{code}.{_format}') ->controller('error_controller::preview') ->defaults(['_format' => 'html']) diff --git a/Resources/config/routing/webhook.php b/Resources/config/routing/webhook.php index 413fe6c81..ea8031159 100644 --- a/Resources/config/routing/webhook.php +++ b/Resources/config/routing/webhook.php @@ -10,8 +10,19 @@ */ use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; +use Symfony\Component\Routing\Loader\XmlFileLoader; return function (RoutingConfigurator $routes): void { + foreach (debug_backtrace() as $trace) { + if (isset($trace['object']) && $trace['object'] instanceof XmlFileLoader && 'doImport' === $trace['function']) { + if (__DIR__ === dirname(realpath($trace['args'][3]))) { + trigger_deprecation('symfony/routing', '7.3', 'The "webhook.xml" routing configuration file is deprecated, import "webhook.php" instead.'); + + break; + } + } + } + $routes->add('_webhook_controller', '/{type}') ->controller('webhook_controller::handle') ->requirements(['type' => '.+']) From 7ed440308439b894f21630e85caeaf212c74849d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 2 May 2025 07:43:50 +0200 Subject: [PATCH 254/281] drop the limiters option for non-compound rater limiters --- DependencyInjection/FrameworkExtension.php | 2 ++ .../PhpFrameworkExtensionTest.php | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index f5111cd10..2dd6ed95e 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -3255,6 +3255,8 @@ private function registerRateLimiterConfiguration(array $config, ContainerBuilde continue; } + unset($limiterConfig['limiters']); + $limiters[] = $name; // default configuration (when used by other DI extensions) diff --git a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index a7606b683..60a1765f7 100644 --- a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -314,6 +314,19 @@ public function testRateLimiterCompoundPolicy() ]); }); + $this->assertSame([ + 'policy' => 'fixed_window', + 'limit' => 10, + 'interval' => '1 hour', + 'id' => 'first', + ], $container->getDefinition('limiter.first')->getArgument(0)); + $this->assertSame([ + 'policy' => 'sliding_window', + 'limit' => 10, + 'interval' => '1 hour', + 'id' => 'second', + ], $container->getDefinition('limiter.second')->getArgument(0)); + $definition = $container->getDefinition('limiter.compound'); $this->assertSame(CompoundRateLimiterFactory::class, $definition->getClass()); $this->assertEquals( From 0b95b03a5cb65816004d61b6a4d9a38e5cefeebf Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Fri, 2 May 2025 20:47:36 +0200 Subject: [PATCH 255/281] [Mailer][Mime] Update SMIME repository node description in configuration Clarified the documentation for the S/MIME certificate repository configuration. It now specifies that the repository should be a service implementing `SmimeCertificateRepositoryInterface`. --- DependencyInjection/Configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 6b168a2d4..51db38963 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -2350,7 +2350,7 @@ private function addMailerSection(ArrayNodeDefinition $rootNode, callable $enabl ->info('S/MIME encrypter configuration') ->children() ->scalarNode('repository') - ->info('Path to the S/MIME certificate repository. Shall implement the `Symfony\Component\Mailer\EventListener\SmimeCertificateRepositoryInterface`.') + ->info('S/MIME certificate repository service. This service shall implement the `Symfony\Component\Mailer\EventListener\SmimeCertificateRepositoryInterface`.') ->defaultValue('') ->cannotBeEmpty() ->end() From 5b3a4cf82f21e6e96ef231a06e6539249483267a Mon Sep 17 00:00:00 2001 From: soyuka Date: Wed, 2 Apr 2025 09:54:43 +0200 Subject: [PATCH 256/281] [ObjectMapper] Condition to target a specific class --- Tests/Fixtures/ObjectMapper/TransformCallable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Fixtures/ObjectMapper/TransformCallable.php b/Tests/Fixtures/ObjectMapper/TransformCallable.php index da4f26a2d..3321e28d1 100644 --- a/Tests/Fixtures/ObjectMapper/TransformCallable.php +++ b/Tests/Fixtures/ObjectMapper/TransformCallable.php @@ -18,7 +18,7 @@ */ final class TransformCallable implements TransformCallableInterface { - public function __invoke(mixed $value, object $object): mixed + public function __invoke(mixed $value, object $source, ?object $target): mixed { return 'transformed'; } From 017a7a464ee817262bb51df820679b9014f2c799 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 6 May 2025 20:24:47 +0200 Subject: [PATCH 257/281] bump min constraint for the ObjectMapper component --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2ecedbc45..bc312827f 100644 --- a/composer.json +++ b/composer.json @@ -54,7 +54,7 @@ "symfony/messenger": "^6.4|^7.0", "symfony/mime": "^6.4|^7.0", "symfony/notifier": "^6.4|^7.0", - "symfony/object-mapper": "^7.3", + "symfony/object-mapper": "^v7.3.0-beta2", "symfony/process": "^6.4|^7.0", "symfony/rate-limiter": "^6.4|^7.0", "symfony/scheduler": "^6.4.4|^7.0.4", From e14c287b356b6c30a99fa7862c988e3a4a3ba55d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 6 May 2025 22:43:17 +0200 Subject: [PATCH 258/281] ensure that all supported e-mail validation modes can be configured --- DependencyInjection/Configuration.php | 3 +- .../PhpFrameworkExtensionTest.php | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index cb52a0704..4d44c469f 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -45,6 +45,7 @@ use Symfony\Component\Serializer\Serializer; use Symfony\Component\Translation\Translator; use Symfony\Component\Uid\Factory\UuidFactory; +use Symfony\Component\Validator\Constraints\Email; use Symfony\Component\Validator\Validation; use Symfony\Component\Webhook\Controller\WebhookController; use Symfony\Component\WebLink\HttpHeaderSerializer; @@ -1066,7 +1067,7 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e ->validate()->castToArray()->end() ->end() ->scalarNode('translation_domain')->defaultValue('validators')->end() - ->enumNode('email_validation_mode')->values(['html5', 'loose', 'strict'])->end() + ->enumNode('email_validation_mode')->values(Email::VALIDATION_MODES + ['loose'])->end() ->arrayNode('mapping') ->addDefaultsIfNotSet() ->fixXmlConfig('path') diff --git a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index 53268ffd2..eae457361 100644 --- a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\Validator\Constraints\Email; use Symfony\Component\Workflow\Exception\InvalidDefinitionException; class PhpFrameworkExtensionTest extends FrameworkExtensionTestCase @@ -245,4 +246,31 @@ public function testRateLimiterLockFactory() $container->getDefinition('limiter.without_lock')->getArgument(2); } + + /** + * @dataProvider emailValidationModeProvider + */ + public function testValidatorEmailValidationMode(string $mode) + { + $this->expectNotToPerformAssertions(); + + $this->createContainerFromClosure(function (ContainerBuilder $container) use ($mode) { + $container->loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'validation' => [ + 'email_validation_mode' => $mode, + ], + ]); + }); + } + + public function emailValidationModeProvider() + { + foreach (Email::VALIDATION_MODES as $mode) { + yield [$mode]; + } + } } From b238ab0e1ab20793d151def4a4ef1da9d58362aa Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Thu, 8 May 2025 00:10:13 +0900 Subject: [PATCH 259/281] [FrameworkBundle] Ensure `Email` class exists before using it --- DependencyInjection/Configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 4d44c469f..bae8967a8 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1067,7 +1067,7 @@ private function addValidationSection(ArrayNodeDefinition $rootNode, callable $e ->validate()->castToArray()->end() ->end() ->scalarNode('translation_domain')->defaultValue('validators')->end() - ->enumNode('email_validation_mode')->values(Email::VALIDATION_MODES + ['loose'])->end() + ->enumNode('email_validation_mode')->values((class_exists(Email::class) ? Email::VALIDATION_MODES : ['html5-allow-no-tld', 'html5', 'strict']) + ['loose'])->end() ->arrayNode('mapping') ->addDefaultsIfNotSet() ->fixXmlConfig('path') From b63dec25c8bb03e14e84fa878e306b44eb8810c9 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 8 May 2025 15:32:34 +0200 Subject: [PATCH 260/281] make data provider static --- Tests/DependencyInjection/PhpFrameworkExtensionTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index eae457361..e5cc8522a 100644 --- a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -267,7 +267,7 @@ public function testValidatorEmailValidationMode(string $mode) }); } - public function emailValidationModeProvider() + public static function emailValidationModeProvider() { foreach (Email::VALIDATION_MODES as $mode) { yield [$mode]; From e35e3192856f4bdb232af5592c1a63daab3a2998 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 9 May 2025 10:05:11 +0200 Subject: [PATCH 261/281] [DependencyInjection][FrameworkBundle] Fix precedence of App\Kernel alias and ignore container.excluded tag on synthetic services --- DependencyInjection/FrameworkExtension.php | 20 ++++++++++---------- Kernel/MicroKernelTrait.php | 3 ++- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 2dd6ed95e..4b18b3817 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -791,34 +791,34 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu } $container->registerForAutoconfiguration(CompilerPassInterface::class) - ->addTag('container.excluded', ['source' => 'because it\'s a compiler pass'])->setAbstract(true); + ->addTag('container.excluded', ['source' => 'because it\'s a compiler pass']); $container->registerForAutoconfiguration(Constraint::class) - ->addTag('container.excluded', ['source' => 'because it\'s a validation constraint'])->setAbstract(true); + ->addTag('container.excluded', ['source' => 'because it\'s a validation constraint']); $container->registerForAutoconfiguration(TestCase::class) - ->addTag('container.excluded', ['source' => 'because it\'s a test case'])->setAbstract(true); + ->addTag('container.excluded', ['source' => 'because it\'s a test case']); $container->registerForAutoconfiguration(\UnitEnum::class) - ->addTag('container.excluded', ['source' => 'because it\'s an enum'])->setAbstract(true); + ->addTag('container.excluded', ['source' => 'because it\'s an enum']); $container->registerAttributeForAutoconfiguration(AsMessage::class, static function (ChildDefinition $definition) { - $definition->addTag('container.excluded', ['source' => 'because it\'s a messenger message'])->setAbstract(true); + $definition->addTag('container.excluded', ['source' => 'because it\'s a messenger message']); }); $container->registerAttributeForAutoconfiguration(\Attribute::class, static function (ChildDefinition $definition) { - $definition->addTag('container.excluded', ['source' => 'because it\'s an attribute'])->setAbstract(true); + $definition->addTag('container.excluded', ['source' => 'because it\'s a PHP attribute']); }); $container->registerAttributeForAutoconfiguration(Entity::class, static function (ChildDefinition $definition) { - $definition->addTag('container.excluded', ['source' => 'because it\'s a doctrine entity'])->setAbstract(true); + $definition->addTag('container.excluded', ['source' => 'because it\'s a Doctrine entity']); }); $container->registerAttributeForAutoconfiguration(Embeddable::class, static function (ChildDefinition $definition) { - $definition->addTag('container.excluded', ['source' => 'because it\'s a doctrine embeddable'])->setAbstract(true); + $definition->addTag('container.excluded', ['source' => 'because it\'s a Doctrine embeddable']); }); $container->registerAttributeForAutoconfiguration(MappedSuperclass::class, static function (ChildDefinition $definition) { - $definition->addTag('container.excluded', ['source' => 'because it\'s a doctrine mapped superclass'])->setAbstract(true); + $definition->addTag('container.excluded', ['source' => 'because it\'s a Doctrine mapped superclass']); }); $container->registerAttributeForAutoconfiguration(JsonStreamable::class, static function (ChildDefinition $definition, JsonStreamable $attribute) { $definition->addTag('json_streamer.streamable', [ 'object' => $attribute->asObject, 'list' => $attribute->asList, - ])->addTag('container.excluded', ['source' => 'because it\'s a streamable JSON'])->setAbstract(true); + ])->addTag('container.excluded', ['source' => 'because it\'s a streamable JSON']); }); if (!$container->getParameter('kernel.debug')) { diff --git a/Kernel/MicroKernelTrait.php b/Kernel/MicroKernelTrait.php index 28d616c13..f40373a30 100644 --- a/Kernel/MicroKernelTrait.php +++ b/Kernel/MicroKernelTrait.php @@ -165,7 +165,6 @@ public function registerContainerConfiguration(LoaderInterface $loader): void ->setPublic(true) ; } - $container->setAlias($kernelClass, 'kernel')->setPublic(true); $kernelDefinition = $container->getDefinition('kernel'); $kernelDefinition->addTag('routing.route_loader'); @@ -198,6 +197,8 @@ public function registerContainerConfiguration(LoaderInterface $loader): void $kernelLoader->registerAliasesForSinglyImplementedInterfaces(); AbstractConfigurator::$valuePreProcessor = $valuePreProcessor; } + + $container->setAlias($kernelClass, 'kernel')->setPublic(true); }); } From 42cca5c8fb95d851bf3fb709217e8feb62a4376a Mon Sep 17 00:00:00 2001 From: Nowfel2501 Date: Fri, 9 May 2025 21:12:33 +0200 Subject: [PATCH 262/281] Improve readability of disallow_search_engine_index condition --- DependencyInjection/FrameworkExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index f585b5bbb..68386120e 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -735,7 +735,7 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu $container->getDefinition('config_cache_factory')->setArguments([]); } - if (!$config['disallow_search_engine_index'] ?? false) { + if (!$config['disallow_search_engine_index']) { $container->removeDefinition('disallow_search_engine_index_response_listener'); } From 87f0ace63cdbf4314fc223c193a375b86c23d4d1 Mon Sep 17 00:00:00 2001 From: Quentin Devos <4972091+Okhoshi@users.noreply.github.com> Date: Sat, 9 Dec 2023 21:15:58 +0100 Subject: [PATCH 263/281] [FrameworkBundle] Make `ValidatorCacheWarmer` and `SerializeCacheWarmer` use `kernel.build_dir` instead of `kernel.cache_dir` --- CHANGELOG.md | 2 + CacheWarmer/SerializerCacheWarmer.php | 3 + CacheWarmer/ValidatorCacheWarmer.php | 4 + Resources/config/serializer.php | 2 +- Resources/config/validator.php | 2 +- .../CacheWarmer/SerializerCacheWarmerTest.php | 74 ++++++++++++++--- .../CacheWarmer/ValidatorCacheWarmerTest.php | 81 ++++++++++++++++--- 7 files changed, 146 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e70fb98e..ec0d88fce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ CHANGELOG * Set `framework.rate_limiter.limiters.*.lock_factory` to `auto` by default * Deprecate `RateLimiterFactory` autowiring aliases, use `RateLimiterFactoryInterface` instead * Allow configuring compound rate limiters + * Make `ValidatorCacheWarmer` use `kernel.build_dir` instead of `cache_dir` + * Make `SerializeCacheWarmer` use `kernel.build_dir` instead of `cache_dir` 7.2 --- diff --git a/CacheWarmer/SerializerCacheWarmer.php b/CacheWarmer/SerializerCacheWarmer.php index 46da4daaa..fbf7083b7 100644 --- a/CacheWarmer/SerializerCacheWarmer.php +++ b/CacheWarmer/SerializerCacheWarmer.php @@ -41,6 +41,9 @@ public function __construct( protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter, ?string $buildDir = null): bool { + if (!$buildDir) { + return false; + } if (!$this->loaders) { return true; } diff --git a/CacheWarmer/ValidatorCacheWarmer.php b/CacheWarmer/ValidatorCacheWarmer.php index 6ecaa4bd1..9c313f80a 100644 --- a/CacheWarmer/ValidatorCacheWarmer.php +++ b/CacheWarmer/ValidatorCacheWarmer.php @@ -41,6 +41,10 @@ public function __construct( protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter, ?string $buildDir = null): bool { + if (!$buildDir) { + return false; + } + $loaders = $this->validatorBuilder->getLoaders(); $metadataFactory = new LazyLoadingMetadataFactory(new LoaderChain($loaders), $arrayAdapter); diff --git a/Resources/config/serializer.php b/Resources/config/serializer.php index 535b95a39..e0a256bbe 100644 --- a/Resources/config/serializer.php +++ b/Resources/config/serializer.php @@ -56,7 +56,7 @@ return static function (ContainerConfigurator $container) { $container->parameters() - ->set('serializer.mapping.cache.file', '%kernel.cache_dir%/serialization.php') + ->set('serializer.mapping.cache.file', '%kernel.build_dir%/serialization.php') ; $container->services() diff --git a/Resources/config/validator.php b/Resources/config/validator.php index adde2de23..535b42edc 100644 --- a/Resources/config/validator.php +++ b/Resources/config/validator.php @@ -28,7 +28,7 @@ return static function (ContainerConfigurator $container) { $container->parameters() - ->set('validator.mapping.cache.file', param('kernel.cache_dir').'/validation.php'); + ->set('validator.mapping.cache.file', '%kernel.build_dir%/validation.php'); $validatorsDir = \dirname((new \ReflectionClass(EmailValidator::class))->getFileName()); diff --git a/Tests/CacheWarmer/SerializerCacheWarmerTest.php b/Tests/CacheWarmer/SerializerCacheWarmerTest.php index 5feb0c8ec..9b765c36a 100644 --- a/Tests/CacheWarmer/SerializerCacheWarmerTest.php +++ b/Tests/CacheWarmer/SerializerCacheWarmerTest.php @@ -30,9 +30,50 @@ public function testWarmUp(array $loaders) @unlink($file); $warmer = new SerializerCacheWarmer($loaders, $file); - $warmer->warmUp(\dirname($file)); + $warmer->warmUp(\dirname($file), \dirname($file)); + + $this->assertFileExists($file); + + $arrayPool = new PhpArrayAdapter($file, new NullAdapter()); + + $this->assertTrue($arrayPool->getItem('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Person')->isHit()); + $this->assertTrue($arrayPool->getItem('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Author')->isHit()); + } + + /** + * @dataProvider loaderProvider + */ + public function testWarmUpAbsoluteFilePath(array $loaders) + { + $file = sys_get_temp_dir().'/0/cache-serializer.php'; + @unlink($file); + + $cacheDir = sys_get_temp_dir().'/1'; + + $warmer = new SerializerCacheWarmer($loaders, $file); + $warmer->warmUp($cacheDir, $cacheDir); $this->assertFileExists($file); + $this->assertFileDoesNotExist($cacheDir.'/cache-serializer.php'); + + $arrayPool = new PhpArrayAdapter($file, new NullAdapter()); + + $this->assertTrue($arrayPool->getItem('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Person')->isHit()); + $this->assertTrue($arrayPool->getItem('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Author')->isHit()); + } + + /** + * @dataProvider loaderProvider + */ + public function testWarmUpWithoutBuildDir(array $loaders) + { + $file = sys_get_temp_dir().'/cache-serializer.php'; + @unlink($file); + + $warmer = new SerializerCacheWarmer($loaders, $file); + $warmer->warmUp(\dirname($file)); + + $this->assertFileDoesNotExist($file); $arrayPool = new PhpArrayAdapter($file, new NullAdapter()); @@ -66,7 +107,7 @@ public function testWarmUpWithoutLoader() @unlink($file); $warmer = new SerializerCacheWarmer([], $file); - $warmer->warmUp(\dirname($file)); + $warmer->warmUp(\dirname($file), \dirname($file)); $this->assertFileExists($file); } @@ -79,7 +120,10 @@ public function testClassAutoloadException() { $this->assertFalse(class_exists($mappedClass = 'AClassThatDoesNotExist_FWB_CacheWarmer_SerializerCacheWarmerTest', false)); - $warmer = new SerializerCacheWarmer([new YamlFileLoader(__DIR__.'/../Fixtures/Serialization/Resources/does_not_exist.yaml')], tempnam(sys_get_temp_dir(), __FUNCTION__)); + $file = tempnam(sys_get_temp_dir(), __FUNCTION__); + @unlink($file); + + $warmer = new SerializerCacheWarmer([new YamlFileLoader(__DIR__.'/../Fixtures/Serialization/Resources/does_not_exist.yaml')], $file); spl_autoload_register($classLoader = function ($class) use ($mappedClass) { if ($class === $mappedClass) { @@ -87,7 +131,8 @@ public function testClassAutoloadException() } }, true, true); - $warmer->warmUp('foo'); + $warmer->warmUp(\dirname($file), \dirname($file)); + $this->assertFileExists($file); spl_autoload_unregister($classLoader); } @@ -98,12 +143,12 @@ public function testClassAutoloadException() */ public function testClassAutoloadExceptionWithUnrelatedException() { - $this->expectException(\DomainException::class); - $this->expectExceptionMessage('This exception should not be caught by the warmer.'); - $this->assertFalse(class_exists($mappedClass = 'AClassThatDoesNotExist_FWB_CacheWarmer_SerializerCacheWarmerTest', false)); - $warmer = new SerializerCacheWarmer([new YamlFileLoader(__DIR__.'/../Fixtures/Serialization/Resources/does_not_exist.yaml')], tempnam(sys_get_temp_dir(), __FUNCTION__)); + $file = tempnam(sys_get_temp_dir(), __FUNCTION__); + @unlink($file); + + $warmer = new SerializerCacheWarmer([new YamlFileLoader(__DIR__.'/../Fixtures/Serialization/Resources/does_not_exist.yaml')], basename($file)); spl_autoload_register($classLoader = function ($class) use ($mappedClass) { if ($class === $mappedClass) { @@ -112,8 +157,17 @@ public function testClassAutoloadExceptionWithUnrelatedException() } }, true, true); - $warmer->warmUp('foo'); + $this->expectException(\DomainException::class); + $this->expectExceptionMessage('This exception should not be caught by the warmer.'); + + try { + $warmer->warmUp(\dirname($file), \dirname($file)); + } catch (\DomainException $e) { + $this->assertFileDoesNotExist($file); - spl_autoload_unregister($classLoader); + throw $e; + } finally { + spl_autoload_unregister($classLoader); + } } } diff --git a/Tests/CacheWarmer/ValidatorCacheWarmerTest.php b/Tests/CacheWarmer/ValidatorCacheWarmerTest.php index cc471e43f..af0bb1b50 100644 --- a/Tests/CacheWarmer/ValidatorCacheWarmerTest.php +++ b/Tests/CacheWarmer/ValidatorCacheWarmerTest.php @@ -32,7 +32,7 @@ public function testWarmUp() @unlink($file); $warmer = new ValidatorCacheWarmer($validatorBuilder, $file); - $warmer->warmUp(\dirname($file)); + $warmer->warmUp(\dirname($file), \dirname($file)); $this->assertFileExists($file); @@ -42,6 +42,53 @@ public function testWarmUp() $this->assertTrue($arrayPool->getItem('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Author')->isHit()); } + public function testWarmUpAbsoluteFilePath() + { + $validatorBuilder = new ValidatorBuilder(); + $validatorBuilder->addXmlMapping(__DIR__.'/../Fixtures/Validation/Resources/person.xml'); + $validatorBuilder->addYamlMapping(__DIR__.'/../Fixtures/Validation/Resources/author.yml'); + $validatorBuilder->addMethodMapping('loadValidatorMetadata'); + $validatorBuilder->enableAttributeMapping(); + + $file = sys_get_temp_dir().'/0/cache-validator.php'; + @unlink($file); + + $cacheDir = sys_get_temp_dir().'/1'; + + $warmer = new ValidatorCacheWarmer($validatorBuilder, $file); + $warmer->warmUp($cacheDir, $cacheDir); + + $this->assertFileExists($file); + $this->assertFileDoesNotExist($cacheDir.'/cache-validator.php'); + + $arrayPool = new PhpArrayAdapter($file, new NullAdapter()); + + $this->assertTrue($arrayPool->getItem('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Person')->isHit()); + $this->assertTrue($arrayPool->getItem('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Author')->isHit()); + } + + public function testWarmUpWithoutBuilDir() + { + $validatorBuilder = new ValidatorBuilder(); + $validatorBuilder->addXmlMapping(__DIR__.'/../Fixtures/Validation/Resources/person.xml'); + $validatorBuilder->addYamlMapping(__DIR__.'/../Fixtures/Validation/Resources/author.yml'); + $validatorBuilder->addMethodMapping('loadValidatorMetadata'); + $validatorBuilder->enableAttributeMapping(); + + $file = sys_get_temp_dir().'/cache-validator.php'; + @unlink($file); + + $warmer = new ValidatorCacheWarmer($validatorBuilder, $file); + $warmer->warmUp(\dirname($file)); + + $this->assertFileDoesNotExist($file); + + $arrayPool = new PhpArrayAdapter($file, new NullAdapter()); + + $this->assertTrue($arrayPool->getItem('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Person')->isHit()); + $this->assertTrue($arrayPool->getItem('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Author')->isHit()); + } + public function testWarmUpWithAnnotations() { $validatorBuilder = new ValidatorBuilder(); @@ -52,7 +99,7 @@ public function testWarmUpWithAnnotations() @unlink($file); $warmer = new ValidatorCacheWarmer($validatorBuilder, $file); - $warmer->warmUp(\dirname($file)); + $warmer->warmUp(\dirname($file), \dirname($file)); $this->assertFileExists($file); @@ -72,7 +119,7 @@ public function testWarmUpWithoutLoader() @unlink($file); $warmer = new ValidatorCacheWarmer($validatorBuilder, $file); - $warmer->warmUp(\dirname($file)); + $warmer->warmUp(\dirname($file), \dirname($file)); $this->assertFileExists($file); } @@ -85,9 +132,12 @@ public function testClassAutoloadException() { $this->assertFalse(class_exists($mappedClass = 'AClassThatDoesNotExist_FWB_CacheWarmer_ValidatorCacheWarmerTest', false)); + $file = tempnam(sys_get_temp_dir(), __FUNCTION__); + @unlink($file); + $validatorBuilder = new ValidatorBuilder(); $validatorBuilder->addYamlMapping(__DIR__.'/../Fixtures/Validation/Resources/does_not_exist.yaml'); - $warmer = new ValidatorCacheWarmer($validatorBuilder, tempnam(sys_get_temp_dir(), __FUNCTION__)); + $warmer = new ValidatorCacheWarmer($validatorBuilder, $file); spl_autoload_register($classloader = function ($class) use ($mappedClass) { if ($class === $mappedClass) { @@ -95,7 +145,9 @@ public function testClassAutoloadException() } }, true, true); - $warmer->warmUp('foo'); + $warmer->warmUp(\dirname($file), \dirname($file)); + + $this->assertFileExists($file); spl_autoload_unregister($classloader); } @@ -106,14 +158,14 @@ public function testClassAutoloadException() */ public function testClassAutoloadExceptionWithUnrelatedException() { - $this->expectException(\DomainException::class); - $this->expectExceptionMessage('This exception should not be caught by the warmer.'); + $file = tempnam(sys_get_temp_dir(), __FUNCTION__); + @unlink($file); $this->assertFalse(class_exists($mappedClass = 'AClassThatDoesNotExist_FWB_CacheWarmer_ValidatorCacheWarmerTest', false)); $validatorBuilder = new ValidatorBuilder(); $validatorBuilder->addYamlMapping(__DIR__.'/../Fixtures/Validation/Resources/does_not_exist.yaml'); - $warmer = new ValidatorCacheWarmer($validatorBuilder, tempnam(sys_get_temp_dir(), __FUNCTION__)); + $warmer = new ValidatorCacheWarmer($validatorBuilder, basename($file)); spl_autoload_register($classLoader = function ($class) use ($mappedClass) { if ($class === $mappedClass) { @@ -122,8 +174,17 @@ public function testClassAutoloadExceptionWithUnrelatedException() } }, true, true); - $warmer->warmUp('foo'); + $this->expectException(\DomainException::class); + $this->expectExceptionMessage('This exception should not be caught by the warmer.'); + + try { + $warmer->warmUp(\dirname($file), \dirname($file)); + } catch (\DomainException $e) { + $this->assertFileDoesNotExist($file); - spl_autoload_unregister($classLoader); + throw $e; + } finally { + spl_autoload_unregister($classLoader); + } } } From c5c63084286c21045144652b9dfd7d5b4ca5099d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 13 Mar 2024 18:34:52 +0100 Subject: [PATCH 264/281] [Workflow] Add support for executing custom workflow definition validators during the container compilation --- CHANGELOG.md | 1 + DependencyInjection/Configuration.php | 35 ++++++++--- DependencyInjection/FrameworkExtension.php | 35 ++++++----- FrameworkBundle.php | 2 + Resources/config/schema/symfony-1.0.xsd | 1 + .../Validator/DefinitionValidator.php | 16 +++++ .../Fixtures/php/workflows.php | 3 + .../Fixtures/xml/workflows.xml | 1 + .../Fixtures/yml/workflows.yml | 2 + .../FrameworkExtensionTestCase.php | 12 +++- .../PhpFrameworkExtensionTest.php | 61 ++++++++++++++++++- composer.json | 4 +- 12 files changed, 144 insertions(+), 29 deletions(-) create mode 100644 Tests/DependencyInjection/Fixtures/Workflow/Validator/DefinitionValidator.php diff --git a/CHANGELOG.md b/CHANGELOG.md index f7a3766d6..ce62c9cdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ CHANGELOG * Allow configuring compound rate limiters * Make `ValidatorCacheWarmer` use `kernel.build_dir` instead of `cache_dir` * Make `SerializeCacheWarmer` use `kernel.build_dir` instead of `cache_dir` + * Support executing custom workflow validators during container compilation 7.2 --- diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 11dc781ba..4c4045552 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -52,6 +52,7 @@ use Symfony\Component\Validator\Validation; use Symfony\Component\Webhook\Controller\WebhookController; use Symfony\Component\WebLink\HttpHeaderSerializer; +use Symfony\Component\Workflow\Validator\DefinitionValidatorInterface; use Symfony\Component\Workflow\WorkflowEvents; /** @@ -403,6 +404,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->useAttributeAsKey('name') ->prototype('array') ->fixXmlConfig('support') + ->fixXmlConfig('definition_validator') ->fixXmlConfig('place') ->fixXmlConfig('transition') ->fixXmlConfig('event_to_dispatch', 'events_to_dispatch') @@ -432,11 +434,28 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->prototype('scalar') ->cannotBeEmpty() ->validate() - ->ifTrue(fn ($v) => !class_exists($v) && !interface_exists($v, false)) + ->ifTrue(static fn ($v) => !class_exists($v) && !interface_exists($v, false)) ->thenInvalid('The supported class or interface "%s" does not exist.') ->end() ->end() ->end() + ->arrayNode('definition_validators') + ->prototype('scalar') + ->cannotBeEmpty() + ->validate() + ->ifTrue(static fn ($v) => !class_exists($v)) + ->thenInvalid('The validation class %s does not exist.') + ->end() + ->validate() + ->ifTrue(static fn ($v) => !is_a($v, DefinitionValidatorInterface::class, true)) + ->thenInvalid(\sprintf('The validation class %%s is not an instance of "%s".', DefinitionValidatorInterface::class)) + ->end() + ->validate() + ->ifTrue(static fn ($v) => 1 <= (new \ReflectionClass($v))->getConstructor()?->getNumberOfRequiredParameters()) + ->thenInvalid('The %s validation class constructor must not have any arguments.') + ->end() + ->end() + ->end() ->scalarNode('support_strategy') ->cannotBeEmpty() ->end() @@ -448,7 +467,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->variableNode('events_to_dispatch') ->defaultValue(null) ->validate() - ->ifTrue(function ($v) { + ->ifTrue(static function ($v) { if (null === $v) { return false; } @@ -475,14 +494,14 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->arrayNode('places') ->beforeNormalization() ->always() - ->then(function ($places) { + ->then(static function ($places) { if (!\is_array($places)) { throw new InvalidConfigurationException('The "places" option must be an array in workflow configuration.'); } // It's an indexed array of shape ['place1', 'place2'] if (isset($places[0]) && \is_string($places[0])) { - return array_map(function (string $place) { + return array_map(static function (string $place) { return ['name' => $place]; }, $places); } @@ -522,7 +541,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->arrayNode('transitions') ->beforeNormalization() ->always() - ->then(function ($transitions) { + ->then(static function ($transitions) { if (!\is_array($transitions)) { throw new InvalidConfigurationException('The "transitions" option must be an array in workflow configuration.'); } @@ -589,20 +608,20 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void ->end() ->end() ->validate() - ->ifTrue(function ($v) { + ->ifTrue(static function ($v) { return $v['supports'] && isset($v['support_strategy']); }) ->thenInvalid('"supports" and "support_strategy" cannot be used together.') ->end() ->validate() - ->ifTrue(function ($v) { + ->ifTrue(static function ($v) { return !$v['supports'] && !isset($v['support_strategy']); }) ->thenInvalid('"supports" or "support_strategy" should be configured.') ->end() ->beforeNormalization() ->always() - ->then(function ($values) { + ->then(static function ($values) { // Special case to deal with XML when the user wants an empty array if (\array_key_exists('event_to_dispatch', $values) && null === $values['event_to_dispatch']) { $values['events_to_dispatch'] = []; diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 4b18b3817..6df4d21df 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -1123,7 +1123,8 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ } } $metadataStoreDefinition->replaceArgument(2, $transitionsMetadataDefinition); - $container->setDefinition(\sprintf('%s.metadata_store', $workflowId), $metadataStoreDefinition); + $metadataStoreId = \sprintf('%s.metadata_store', $workflowId); + $container->setDefinition($metadataStoreId, $metadataStoreDefinition); // Create places $places = array_column($workflow['places'], 'name'); @@ -1134,7 +1135,8 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $definitionDefinition->addArgument($places); $definitionDefinition->addArgument($transitions); $definitionDefinition->addArgument($initialMarking); - $definitionDefinition->addArgument(new Reference(\sprintf('%s.metadata_store', $workflowId))); + $definitionDefinition->addArgument(new Reference($metadataStoreId)); + $definitionDefinitionId = \sprintf('%s.definition', $workflowId); // Create MarkingStore $markingStoreDefinition = null; @@ -1148,14 +1150,26 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $markingStoreDefinition = new Reference($workflow['marking_store']['service']); } + // Validation + $workflow['definition_validators'][] = match ($workflow['type']) { + 'state_machine' => Workflow\Validator\StateMachineValidator::class, + 'workflow' => Workflow\Validator\WorkflowValidator::class, + default => throw new \LogicException(\sprintf('Invalid workflow type "%s".', $workflow['type'])), + }; + // Create Workflow $workflowDefinition = new ChildDefinition(\sprintf('%s.abstract', $type)); - $workflowDefinition->replaceArgument(0, new Reference(\sprintf('%s.definition', $workflowId))); + $workflowDefinition->replaceArgument(0, new Reference($definitionDefinitionId)); $workflowDefinition->replaceArgument(1, $markingStoreDefinition); $workflowDefinition->replaceArgument(3, $name); $workflowDefinition->replaceArgument(4, $workflow['events_to_dispatch']); - $workflowDefinition->addTag('workflow', ['name' => $name, 'metadata' => $workflow['metadata']]); + $workflowDefinition->addTag('workflow', [ + 'name' => $name, + 'metadata' => $workflow['metadata'], + 'definition_validators' => $workflow['definition_validators'], + 'definition_id' => $definitionDefinitionId, + ]); if ('workflow' === $type) { $workflowDefinition->addTag('workflow.workflow', ['name' => $name]); } elseif ('state_machine' === $type) { @@ -1164,21 +1178,10 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ // Store to container $container->setDefinition($workflowId, $workflowDefinition); - $container->setDefinition(\sprintf('%s.definition', $workflowId), $definitionDefinition); + $container->setDefinition($definitionDefinitionId, $definitionDefinition); $container->registerAliasForArgument($workflowId, WorkflowInterface::class, $name.'.'.$type); $container->registerAliasForArgument($workflowId, WorkflowInterface::class, $name); - // Validate Workflow - if ('state_machine' === $workflow['type']) { - $validator = new Workflow\Validator\StateMachineValidator(); - } else { - $validator = new Workflow\Validator\WorkflowValidator(); - } - - $trs = array_map(fn (Reference $ref): Workflow\Transition => $container->get((string) $ref), $transitions); - $realDefinition = new Workflow\Definition($places, $trs, $initialMarking); - $validator->validate($realDefinition, $name); - // Add workflow to Registry if ($workflow['supports']) { foreach ($workflow['supports'] as $supportedClassName) { diff --git a/FrameworkBundle.php b/FrameworkBundle.php index faf2841f4..7c5ba6e39 100644 --- a/FrameworkBundle.php +++ b/FrameworkBundle.php @@ -77,6 +77,7 @@ use Symfony\Component\VarExporter\Internal\Registry; use Symfony\Component\Workflow\DependencyInjection\WorkflowDebugPass; use Symfony\Component\Workflow\DependencyInjection\WorkflowGuardListenerPass; +use Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass; // Help opcache.preload discover always-needed symbols class_exists(ApcuAdapter::class); @@ -173,6 +174,7 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new CachePoolPrunerPass(), PassConfig::TYPE_AFTER_REMOVING); $this->addCompilerPassIfExists($container, FormPass::class); $this->addCompilerPassIfExists($container, WorkflowGuardListenerPass::class); + $this->addCompilerPassIfExists($container, WorkflowValidatorPass::class); $container->addCompilerPass(new ResettableServicePass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); $container->addCompilerPass(new RegisterLocaleAwareServicesPass()); $container->addCompilerPass(new TestServiceContainerWeakRefPass(), PassConfig::TYPE_BEFORE_REMOVING, -32); diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index c4ee3486d..3a6242b83 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -449,6 +449,7 @@ + diff --git a/Tests/DependencyInjection/Fixtures/Workflow/Validator/DefinitionValidator.php b/Tests/DependencyInjection/Fixtures/Workflow/Validator/DefinitionValidator.php new file mode 100644 index 000000000..7244e927c --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/Workflow/Validator/DefinitionValidator.php @@ -0,0 +1,16 @@ + [ FrameworkExtensionTestCase::class, ], + 'definition_validators' => [ + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator\DefinitionValidator::class, + ], 'initial_marking' => ['draft'], 'metadata' => [ 'title' => 'article workflow', diff --git a/Tests/DependencyInjection/Fixtures/xml/workflows.xml b/Tests/DependencyInjection/Fixtures/xml/workflows.xml index 76b4f07a8..c5dae479d 100644 --- a/Tests/DependencyInjection/Fixtures/xml/workflows.xml +++ b/Tests/DependencyInjection/Fixtures/xml/workflows.xml @@ -13,6 +13,7 @@ draft Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator\DefinitionValidator diff --git a/Tests/DependencyInjection/Fixtures/yml/workflows.yml b/Tests/DependencyInjection/Fixtures/yml/workflows.yml index a9b427d89..cac5f6f23 100644 --- a/Tests/DependencyInjection/Fixtures/yml/workflows.yml +++ b/Tests/DependencyInjection/Fixtures/yml/workflows.yml @@ -9,6 +9,8 @@ framework: type: workflow supports: - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase + definition_validators: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator\DefinitionValidator initial_marking: [draft] metadata: title: article workflow diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index d942c122c..1899d5239 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -15,6 +15,7 @@ use Psr\Log\LoggerAwareInterface; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator\DefinitionValidator; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\DummyMessage; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Bundle\FullStack; @@ -287,7 +288,11 @@ public function testProfilerCollectSerializerDataEnabled() public function testWorkflows() { - $container = $this->createContainerFromFile('workflows'); + DefinitionValidator::$called = false; + + $container = $this->createContainerFromFile('workflows', compile: false); + $container->addCompilerPass(new \Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass()); + $container->compile(); $this->assertTrue($container->hasDefinition('workflow.article'), 'Workflow is registered as a service'); $this->assertSame('workflow.abstract', $container->getDefinition('workflow.article')->getParent()); @@ -310,6 +315,7 @@ public function testWorkflows() ], $tags['workflow'][0]['metadata'] ?? null); $this->assertTrue($container->hasDefinition('workflow.article.definition'), 'Workflow definition is registered as a service'); + $this->assertTrue(DefinitionValidator::$called, 'DefinitionValidator is called'); $workflowDefinition = $container->getDefinition('workflow.article.definition'); @@ -403,7 +409,9 @@ public function testWorkflowAreValidated() { $this->expectException(InvalidDefinitionException::class); $this->expectExceptionMessage('A transition from a place/state must have an unique name. Multiple transitions named "go" from place/state "first" were found on StateMachine "my_workflow".'); - $this->createContainerFromFile('workflow_not_valid'); + $container = $this->createContainerFromFile('workflow_not_valid', compile: false); + $container->addCompilerPass(new \Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass()); + $container->compile(); } public function testWorkflowCannotHaveBothSupportsAndSupportStrategy() diff --git a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index dbadcc468..d2bd2b38e 100644 --- a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -102,7 +102,7 @@ public function testWorkflowValidationStateMachine() { $this->expectException(InvalidDefinitionException::class); $this->expectExceptionMessage('A transition from a place/state must have an unique name. Multiple transitions named "a_to_b" from place/state "a" were found on StateMachine "article".'); - $this->createContainerFromClosure(function ($container) { + $this->createContainerFromClosure(function (ContainerBuilder $container) { $container->loadFromExtension('framework', [ 'annotations' => false, 'http_method_override' => false, @@ -128,9 +128,57 @@ public function testWorkflowValidationStateMachine() ], ], ]); + $container->addCompilerPass(new \Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass()); + }); + } + + /** + * @dataProvider provideWorkflowValidationCustomTests + */ + public function testWorkflowValidationCustomBroken(string $class, string $message) + { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage($message); + $this->createContainerFromClosure(function ($container) use ($class) { + $container->loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'workflows' => [ + 'article' => [ + 'type' => 'state_machine', + 'supports' => [ + __CLASS__, + ], + 'places' => [ + 'a', + 'b', + ], + 'transitions' => [ + 'a_to_b' => [ + 'from' => ['a'], + 'to' => ['b'], + ], + ], + 'definition_validators' => [ + $class, + ], + ], + ], + ]); }); } + public static function provideWorkflowValidationCustomTests() + { + yield ['classDoesNotExist', 'Invalid configuration for path "framework.workflows.workflows.article.definition_validators.0": The validation class "classDoesNotExist" does not exist.']; + + yield [\DateTime::class, 'Invalid configuration for path "framework.workflows.workflows.article.definition_validators.0": The validation class "DateTime" is not an instance of "Symfony\Component\Workflow\Validator\DefinitionValidatorInterface".']; + + yield [WorkflowValidatorWithConstructor::class, 'Invalid configuration for path "framework.workflows.workflows.article.definition_validators.0": The "Symfony\\\\Bundle\\\\FrameworkBundle\\\\Tests\\\\DependencyInjection\\\\WorkflowValidatorWithConstructor" validation class constructor must not have any arguments.']; + } + public function testWorkflowDefaultMarkingStoreDefinition() { $container = $this->createContainerFromClosure(function ($container) { @@ -407,3 +455,14 @@ public static function emailValidationModeProvider() } } } + +class WorkflowValidatorWithConstructor implements \Symfony\Component\Workflow\Validator\DefinitionValidatorInterface +{ + public function __construct(bool $enabled) + { + } + + public function validate(\Symfony\Component\Workflow\Definition $definition, string $name): void + { + } +} diff --git a/composer.json b/composer.json index bc312827f..b3c81b287 100644 --- a/composer.json +++ b/composer.json @@ -67,7 +67,7 @@ "symfony/twig-bundle": "^6.4|^7.0", "symfony/type-info": "^7.1", "symfony/validator": "^6.4|^7.0", - "symfony/workflow": "^6.4|^7.0", + "symfony/workflow": "^7.3", "symfony/yaml": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", "symfony/json-streamer": "7.3.*", @@ -108,7 +108,7 @@ "symfony/validator": "<6.4", "symfony/web-profiler-bundle": "<6.4", "symfony/webhook": "<7.2", - "symfony/workflow": "<6.4" + "symfony/workflow": "<7.3" }, "autoload": { "psr-4": { "Symfony\\Bundle\\FrameworkBundle\\": "" }, From b096abaa51e1939c01300a6defc7cf24b2539fee Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 12 May 2025 09:26:05 +0200 Subject: [PATCH 265/281] fix lowest allowed Workflow component version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b3c81b287..316c595ff 100644 --- a/composer.json +++ b/composer.json @@ -108,7 +108,7 @@ "symfony/validator": "<6.4", "symfony/web-profiler-bundle": "<6.4", "symfony/webhook": "<7.2", - "symfony/workflow": "<7.3" + "symfony/workflow": "<7.3.0-beta2" }, "autoload": { "psr-4": { "Symfony\\Bundle\\FrameworkBundle\\": "" }, From 8ac582e68a0431df291e330e4892bc667518d8a6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 12 May 2025 10:58:22 +0200 Subject: [PATCH 266/281] use use statements instead of FQCNs --- Tests/DependencyInjection/FrameworkExtensionTestCase.php | 5 +++-- Tests/DependencyInjection/PhpFrameworkExtensionTest.php | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 1899d5239..990e1e8c2 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -92,6 +92,7 @@ use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Webhook\Client\RequestParser; use Symfony\Component\Webhook\Controller\WebhookController; +use Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass; use Symfony\Component\Workflow\Exception\InvalidDefinitionException; use Symfony\Component\Workflow\Metadata\InMemoryMetadataStore; use Symfony\Component\Workflow\WorkflowEvents; @@ -291,7 +292,7 @@ public function testWorkflows() DefinitionValidator::$called = false; $container = $this->createContainerFromFile('workflows', compile: false); - $container->addCompilerPass(new \Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass()); + $container->addCompilerPass(new WorkflowValidatorPass()); $container->compile(); $this->assertTrue($container->hasDefinition('workflow.article'), 'Workflow is registered as a service'); @@ -410,7 +411,7 @@ public function testWorkflowAreValidated() $this->expectException(InvalidDefinitionException::class); $this->expectExceptionMessage('A transition from a place/state must have an unique name. Multiple transitions named "go" from place/state "first" were found on StateMachine "my_workflow".'); $container = $this->createContainerFromFile('workflow_not_valid', compile: false); - $container->addCompilerPass(new \Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass()); + $container->addCompilerPass(new WorkflowValidatorPass()); $container->compile(); } diff --git a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index d2bd2b38e..f69a53932 100644 --- a/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -20,7 +20,10 @@ use Symfony\Component\RateLimiter\CompoundRateLimiterFactory; use Symfony\Component\RateLimiter\RateLimiterFactoryInterface; use Symfony\Component\Validator\Constraints\Email; +use Symfony\Component\Workflow\Definition; +use Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass; use Symfony\Component\Workflow\Exception\InvalidDefinitionException; +use Symfony\Component\Workflow\Validator\DefinitionValidatorInterface; class PhpFrameworkExtensionTest extends FrameworkExtensionTestCase { @@ -128,7 +131,7 @@ public function testWorkflowValidationStateMachine() ], ], ]); - $container->addCompilerPass(new \Symfony\Component\Workflow\DependencyInjection\WorkflowValidatorPass()); + $container->addCompilerPass(new WorkflowValidatorPass()); }); } @@ -456,13 +459,13 @@ public static function emailValidationModeProvider() } } -class WorkflowValidatorWithConstructor implements \Symfony\Component\Workflow\Validator\DefinitionValidatorInterface +class WorkflowValidatorWithConstructor implements DefinitionValidatorInterface { public function __construct(bool $enabled) { } - public function validate(\Symfony\Component\Workflow\Definition $definition, string $name): void + public function validate(Definition $definition, string $name): void { } } From b1de19b2083484d0ce945977f6c6484e9e493a2e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 14 May 2025 09:14:36 +0200 Subject: [PATCH 267/281] remove no longer used service definition --- Resources/config/mailer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Resources/config/mailer.php b/Resources/config/mailer.php index 7a3a95739..f1dc560ab 100644 --- a/Resources/config/mailer.php +++ b/Resources/config/mailer.php @@ -45,7 +45,6 @@ tagged_iterator('mailer.transport_factory'), ]) - ->set('mailer.default_transport', TransportInterface::class) ->alias('mailer.default_transport', 'mailer.transports') ->alias(TransportInterface::class, 'mailer.default_transport') From 639c32f8e2314173a535cfeab683643af3a759b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 2 May 2025 14:12:20 +0200 Subject: [PATCH 268/281] [FrameworkBundle] skip messenger deduplication middlerware registration when no "default" lock is configured --- DependencyInjection/FrameworkExtension.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 385a4caf3..fce6ba65c 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -569,9 +569,9 @@ public function load(array $configs, ContainerBuilder $container): void $container->removeDefinition('console.command.scheduler_debug'); } - // messenger depends on validation being registered + // messenger depends on validation, and lock being registered if ($messengerEnabled) { - $this->registerMessengerConfiguration($config['messenger'], $container, $loader, $this->readConfigEnabled('validation', $container, $config['validation']), $this->readConfigEnabled('lock', $container, $config['lock'])); + $this->registerMessengerConfiguration($config['messenger'], $container, $loader, $this->readConfigEnabled('validation', $container, $config['validation']), $this->readConfigEnabled('lock', $container, $config['lock']) && ($config['lock']['resources']['default'] ?? false)); } else { $container->removeDefinition('console.command.messenger_consume_messages'); $container->removeDefinition('console.command.messenger_stats'); From 7627f005de900e19fde7199247fba325690b0407 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 16 May 2025 15:57:08 +0200 Subject: [PATCH 269/281] [FrameworkBundle] Fix declaring fiel-attr tags in xml config files --- DependencyInjection/Configuration.php | 1 + Resources/config/schema/symfony-1.0.xsd | 2 +- .../Fixtures/php/form_csrf_field_attr.php | 22 +++++++++++++++++++ ...ield_name.xml => form_csrf_field_attr.xml} | 8 ++++++- .../form_csrf_under_form_sets_field_name.xml | 15 ------------- .../Fixtures/yml/form_csrf_field_attr.yml | 16 ++++++++++++++ .../FrameworkExtensionTestCase.php | 11 ++++++++++ 7 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 Tests/DependencyInjection/Fixtures/php/form_csrf_field_attr.php rename Tests/DependencyInjection/Fixtures/xml/{form_csrf_sets_field_name.xml => form_csrf_field_attr.xml} (67%) delete mode 100644 Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml create mode 100644 Tests/DependencyInjection/Fixtures/yml/form_csrf_field_attr.yml diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 50c093f28..5154393f2 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -252,6 +252,7 @@ private function addFormSection(ArrayNodeDefinition $rootNode, callable $enableI ->arrayNode('field_attr') ->performNoDeepMerging() ->normalizeKeys(false) + ->useAttributeAsKey('name') ->scalarPrototype()->end() ->defaultValue(['data-controller' => 'csrf-protection']) ->end() diff --git a/Resources/config/schema/symfony-1.0.xsd b/Resources/config/schema/symfony-1.0.xsd index 491cd1e4f..e99022acf 100644 --- a/Resources/config/schema/symfony-1.0.xsd +++ b/Resources/config/schema/symfony-1.0.xsd @@ -79,7 +79,7 @@ - + diff --git a/Tests/DependencyInjection/Fixtures/php/form_csrf_field_attr.php b/Tests/DependencyInjection/Fixtures/php/form_csrf_field_attr.php new file mode 100644 index 000000000..103ee4797 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/php/form_csrf_field_attr.php @@ -0,0 +1,22 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'csrf_protection' => [ + 'enabled' => true, + ], + 'form' => [ + 'csrf_protection' => [ + 'field-attr' => [ + 'data-foo' => 'bar', + 'data-bar' => 'baz', + ], + ], + ], + 'session' => [ + 'storage_factory_id' => 'session.storage.factory.native', + ], +]); diff --git a/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml b/Tests/DependencyInjection/Fixtures/xml/form_csrf_field_attr.xml similarity index 67% rename from Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml rename to Tests/DependencyInjection/Fixtures/xml/form_csrf_field_attr.xml index 4a05e9d33..1889703be 100644 --- a/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml +++ b/Tests/DependencyInjection/Fixtures/xml/form_csrf_field_attr.xml @@ -9,7 +9,13 @@ - + + + + bar + baz + + diff --git a/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml b/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml deleted file mode 100644 index 09ef0ee16..000000000 --- a/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/Tests/DependencyInjection/Fixtures/yml/form_csrf_field_attr.yml b/Tests/DependencyInjection/Fixtures/yml/form_csrf_field_attr.yml new file mode 100644 index 000000000..db5199775 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/form_csrf_field_attr.yml @@ -0,0 +1,16 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + csrf_protection: + enabled: true + form: + csrf_protection: + enabled: true + field_attr: + data-foo: bar + data-bar: baz + session: + storage_factory_id: session.storage.factory.native diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 7bf66512d..655f9180e 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -1413,6 +1413,17 @@ public function testFormsCanBeEnabledWithoutCsrfProtection() $this->assertFalse($container->getParameter('form.type_extension.csrf.enabled')); } + public function testFormCsrfFieldAttr() + { + $container = $this->createContainerFromFile('form_csrf_field_attr'); + + $expected = [ + 'data-foo' => 'bar', + 'data-bar' => 'baz', + ]; + $this->assertSame($expected, $container->getParameter('form.type_extension.csrf.field_attr')); + } + public function testStopwatchEnabledWithDebugModeEnabled() { $container = $this->createContainerFromFile('default_config', [ From f5158cf658e912609fa443c77bfd37a4b0ea67d9 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 20 May 2025 10:48:43 +0200 Subject: [PATCH 270/281] [FrameworkBundle] Fix activation strategy of traceable decorators --- FrameworkBundle.php | 9 +++++++++ Resources/config/profiling.php | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/FrameworkBundle.php b/FrameworkBundle.php index 7c5ba6e39..300fe22fb 100644 --- a/FrameworkBundle.php +++ b/FrameworkBundle.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddDebugLogProcessorPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AssetsContextPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ContainerBuilderDebugDumpPass; @@ -202,6 +203,14 @@ public function build(ContainerBuilder $container): void } } + /** + * @internal + */ + public static function considerProfilerEnabled(): bool + { + return !($GLOBALS['app'] ?? null) instanceof Application || empty($_GET) && \in_array('--profile', $_SERVER['argv'] ?? [], true); + } + private function addCompilerPassIfExists(ContainerBuilder $container, string $class, string $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0): void { $container->addResource(new ClassExistenceResource($class)); diff --git a/Resources/config/profiling.php b/Resources/config/profiling.php index 68fb295bb..a81c53a63 100644 --- a/Resources/config/profiling.php +++ b/Resources/config/profiling.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Bundle\FrameworkBundle\EventListener\ConsoleProfilerListener; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Component\HttpKernel\Debug\VirtualRequestStack; use Symfony\Component\HttpKernel\EventListener\ProfilerListener; use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage; @@ -61,7 +62,7 @@ ->set('profiler.state_checker', ProfilerStateChecker::class) ->args([ service_locator(['profiler' => service('profiler')->ignoreOnUninitialized()]), - param('kernel.runtime_mode.web'), + inline_service('bool')->factory([FrameworkBundle::class, 'considerProfilerEnabled']), ]) ->set('profiler.is_disabled_state_checker', 'Closure') From 95c9a2cdf5d04816ffdff1378ecd53d0f3a139aa Mon Sep 17 00:00:00 2001 From: soyuka Date: Tue, 20 May 2025 13:53:54 +0200 Subject: [PATCH 271/281] [FrameworkBundle] object mapper service definition without form --- DependencyInjection/FrameworkExtension.php | 16 ++++++++-------- .../FrameworkExtensionTestCase.php | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 385a4caf3..912282f49 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -642,6 +642,14 @@ public function load(array $configs, ContainerBuilder $container): void $loader->load('mime_type.php'); } + if (ContainerBuilder::willBeAvailable('symfony/object-mapper', ObjectMapperInterface::class, ['symfony/framework-bundle'])) { + $loader->load('object_mapper.php'); + $container->registerForAutoconfiguration(TransformCallableInterface::class) + ->addTag('object_mapper.transform_callable'); + $container->registerForAutoconfiguration(ConditionCallableInterface::class) + ->addTag('object_mapper.condition_callable'); + } + $container->registerForAutoconfiguration(PackageInterface::class) ->addTag('assets.package'); $container->registerForAutoconfiguration(AssetCompilerInterface::class) @@ -880,14 +888,6 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont if (!ContainerBuilder::willBeAvailable('symfony/translation', Translator::class, ['symfony/framework-bundle', 'symfony/form'])) { $container->removeDefinition('form.type_extension.upload.validator'); } - - if (ContainerBuilder::willBeAvailable('symfony/object-mapper', ObjectMapperInterface::class, ['symfony/framework-bundle'])) { - $loader->load('object_mapper.php'); - $container->registerForAutoconfiguration(TransformCallableInterface::class) - ->addTag('object_mapper.transform_callable'); - $container->registerForAutoconfiguration(ConditionCallableInterface::class) - ->addTag('object_mapper.condition_callable'); - } } private function registerHttpCacheConfiguration(array $config, ContainerBuilder $container, bool $httpMethodOverride): void diff --git a/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 990e1e8c2..8b452d4c8 100644 --- a/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -2600,6 +2600,14 @@ public function testJsonStreamerEnabled() $this->assertTrue($container->has('json_streamer.stream_writer')); } + public function testObjectMapperEnabled() + { + $container = $this->createContainerFromClosure(function (ContainerBuilder $container) { + $container->loadFromExtension('framework', []); + }); + $this->assertTrue($container->has('object_mapper')); + } + protected function createContainer(array $data = []) { return new ContainerBuilder(new EnvPlaceholderParameterBag(array_merge([ From 3614e216d77431bf0bb68e03299fb7303fd2980e Mon Sep 17 00:00:00 2001 From: HypeMC Date: Wed, 21 May 2025 14:18:26 +0200 Subject: [PATCH 272/281] [PropertyInfo] Improve deprecation message --- DependencyInjection/Configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 0e9b20b0c..f4e137f04 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1290,7 +1290,7 @@ private function addPropertyInfoSection(ArrayNodeDefinition $rootNode, callable ->then(function ($v) { $v['property_info']['with_constructor_extractor'] = false; - trigger_deprecation('symfony/framework-bundle', '7.3', 'Not setting the "with_constructor_extractor" option explicitly is deprecated because its default value will change in version 8.0.'); + trigger_deprecation('symfony/framework-bundle', '7.3', 'Not setting the "property_info.with_constructor_extractor" option explicitly is deprecated because its default value will change in version 8.0.'); return $v; }) From 8122caa52c3b45a0a9e30168834757596bffb87d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 24 May 2025 23:27:21 +0200 Subject: [PATCH 273/281] conflict with 7.4 releases of experimental components --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 316c595ff..15a9496d1 100644 --- a/composer.json +++ b/composer.json @@ -94,6 +94,7 @@ "symfony/mailer": "<6.4", "symfony/messenger": "<6.4", "symfony/mime": "<6.4", + "symfony/object-mapper": ">=7.4", "symfony/property-info": "<6.4", "symfony/property-access": "<6.4", "symfony/runtime": "<6.4.13|>=7.0,<7.1.6", From d35386ab398be41822b464b1027488c5b8b39f14 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Sun, 25 May 2025 23:10:07 +0200 Subject: [PATCH 274/281] [Webhook] Fix controller service name --- Resources/config/routing/webhook.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/config/routing/webhook.php b/Resources/config/routing/webhook.php index ea8031159..177606b26 100644 --- a/Resources/config/routing/webhook.php +++ b/Resources/config/routing/webhook.php @@ -24,7 +24,7 @@ } $routes->add('_webhook_controller', '/{type}') - ->controller('webhook_controller::handle') + ->controller('webhook.controller::handle') ->requirements(['type' => '.+']) ; }; From 030646f55fe18501a43edab22a8ad250d8ec42a6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 27 May 2025 10:30:45 +0200 Subject: [PATCH 275/281] disable the Lock integration to not register the deduplicate middleware --- .../messenger_multiple_buses_without_deduplicate_middleware.php | 1 + .../messenger_multiple_buses_without_deduplicate_middleware.xml | 1 + .../messenger_multiple_buses_without_deduplicate_middleware.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_without_deduplicate_middleware.php b/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_without_deduplicate_middleware.php index b8e7530bb..fd4a00834 100644 --- a/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_without_deduplicate_middleware.php +++ b/Tests/DependencyInjection/Fixtures/php/messenger_multiple_buses_without_deduplicate_middleware.php @@ -5,6 +5,7 @@ 'http_method_override' => false, 'handle_all_throwables' => true, 'php_errors' => ['log' => true], + 'lock' => false, 'messenger' => [ 'default_bus' => 'messenger.bus.commands', 'buses' => [ diff --git a/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_without_deduplicate_middleware.xml b/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_without_deduplicate_middleware.xml index dcf402e1a..3f0d96249 100644 --- a/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_without_deduplicate_middleware.xml +++ b/Tests/DependencyInjection/Fixtures/xml/messenger_multiple_buses_without_deduplicate_middleware.xml @@ -8,6 +8,7 @@ + diff --git a/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_without_deduplicate_middleware.yml b/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_without_deduplicate_middleware.yml index f06d534a5..38fca5737 100644 --- a/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_without_deduplicate_middleware.yml +++ b/Tests/DependencyInjection/Fixtures/yml/messenger_multiple_buses_without_deduplicate_middleware.yml @@ -4,6 +4,7 @@ framework: handle_all_throwables: true php_errors: log: true + lock: false messenger: default_bus: messenger.bus.commands buses: From 9df8e51501cce65c7b259f4644663aa0476c9501 Mon Sep 17 00:00:00 2001 From: matlec Date: Fri, 30 May 2025 13:06:20 +0200 Subject: [PATCH 276/281] Fix `ContainerDebugCommandTest::testNoDumpedXML` --- Tests/Functional/ContainerDebugCommandTest.php | 2 +- Tests/Functional/app/ContainerDebug/no_dump.yml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 Tests/Functional/app/ContainerDebug/no_dump.yml diff --git a/Tests/Functional/ContainerDebugCommandTest.php b/Tests/Functional/ContainerDebugCommandTest.php index 24c6faf33..291a67cb8 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 From 9b1edd26071775162c2df241634496fbbe683399 Mon Sep 17 00:00:00 2001 From: Indra Gunawan Date: Mon, 2 Jun 2025 16:16:20 +0800 Subject: [PATCH 277/281] [FrameworkBundle] set NamespacedPoolInterface alias to cache.app --- DependencyInjection/FrameworkExtension.php | 4 ++++ Resources/config/cache.php | 3 +++ 2 files changed, 7 insertions(+) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 347f3ed65..64d27bca9 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -302,6 +302,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); 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') ; }; From 5df58fd260e6f0902f2719803818c689972e175b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 3 Jun 2025 08:30:01 +0200 Subject: [PATCH 278/281] don't register SchedulerTriggerNormalizer without symfony/serializer --- DependencyInjection/FrameworkExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 64d27bca9..05504af2e 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -2297,7 +2297,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'); } } From 35a2a545f553c30c4679d3797456f82f3d77e08c Mon Sep 17 00:00:00 2001 From: Carlos Quintana Date: Tue, 27 May 2025 14:57:57 +0200 Subject: [PATCH 279/281] [FrameworkBundle] ensureKernelShutdown in tearDownAfterClass --- Test/KernelTestCase.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Test/KernelTestCase.php b/Test/KernelTestCase.php index ee67fa7af..e87ac4824 100644 --- a/Test/KernelTestCase.php +++ b/Test/KernelTestCase.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Test; +use PHPUnit\Framework\Attributes\AfterClass; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -120,8 +121,11 @@ protected static function createKernel(array $options = []): KernelInterface /** * Shuts the kernel down if it was used in the test - called by the tearDown method by default. + * + * @afterClass */ - protected static function ensureKernelShutdown() + #[AfterClass] + public static function ensureKernelShutdown() { if (null !== static::$kernel) { static::$kernel->boot(); From fe8367b6b3593e5451e419c00afd7e780cfce647 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 4 Jun 2025 15:57:42 +0200 Subject: [PATCH 280/281] Revert "bug #60564 [FrameworkBundle] ensureKernelShutdown in tearDownAfterClass (cquintana92)" This reverts commit ec76ab4f28454ebfbcf14287b2aac1351f00df79, reversing changes made to bc886008906f022a8fbf9796b943af928b64d86c. --- Test/KernelTestCase.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Test/KernelTestCase.php b/Test/KernelTestCase.php index e87ac4824..ee67fa7af 100644 --- a/Test/KernelTestCase.php +++ b/Test/KernelTestCase.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\Test; -use PHPUnit\Framework\Attributes\AfterClass; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -121,11 +120,8 @@ protected static function createKernel(array $options = []): KernelInterface /** * Shuts the kernel down if it was used in the test - called by the tearDown method by default. - * - * @afterClass */ - #[AfterClass] - public static function ensureKernelShutdown() + protected static function ensureKernelShutdown() { if (null !== static::$kernel) { static::$kernel->boot(); From 26ca2357cab5dfe862f2bbf397bd1459a8901af8 Mon Sep 17 00:00:00 2001 From: Carlos Quintana Date: Tue, 27 May 2025 14:57:57 +0200 Subject: [PATCH 281/281] [FrameworkBundle] ensureKernelShutdown in tearDownAfterClass --- Test/KernelTestCase.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Test/KernelTestCase.php b/Test/KernelTestCase.php index ee67fa7af..1312f6592 100644 --- a/Test/KernelTestCase.php +++ b/Test/KernelTestCase.php @@ -45,6 +45,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