From 8db28484a473acc4aab94cf349ae139921226391 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Fri, 19 Apr 2024 20:47:43 +0200 Subject: [PATCH 01/84] [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 02/84] [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 59f4fb50d93f4526f49ab30a9b7fde7df7ab9ced Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 22 May 2024 17:24:31 +0200 Subject: [PATCH 03/84] [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 04/84] 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 05/84] [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 3b777e2fcb3ed5946ebb408413edbf8c593d3e2d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 17 Jun 2024 23:56:05 +0200 Subject: [PATCH 06/84] 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 07/84] 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 08/84] 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 09/84] [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 10/84] 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 11/84] 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 12/84] 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 13/84] 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 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 14/84] 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 15/84] [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 16/84] [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 17/84] 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 + Validation Failed + 422 + comment: This value is too short. It should have 10 characters or more. + + comment + This value is too short. It should have 10 characters or more. + + + "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 + Validation Failed + 422 + comment: This value is too short. It should have 10 characters or more. + + comment + This value is too short. It should have 10 characters or more. + + + "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 18/84] [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 19/84] 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 20/84] [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 21/84] 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 22/84] 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 23/84] [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 24/84] 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 25/84] 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 26/84] [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 27/84] 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 28/84] 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 29/84] 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 30/84] [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 6dd1dab4040a0022eb22f44bc07f12a551fd5449 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 6 Aug 2024 09:58:47 +0200 Subject: [PATCH 31/84] [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 32/84] [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 33/84] 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 34/84] 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 35/84] [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 36/84] [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 37/84] [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 38/84] [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 39/84] 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 40/84] 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 41/84] 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 42/84] 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 43/84] [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 44/84] [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 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 45/84] 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 46/84] [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 47/84] [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 48/84] [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 49/84] [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 50/84] 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 51/84] [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 52/84] 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 53/84] 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 54/84] 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 55/84] 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 56/84] [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 57/84] 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 58/84] 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 59/84] [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 60/84] 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 61/84] [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 62/84] 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 63/84] [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 64/84] [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 65/84] [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 66/84] [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 67/84] [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 68/84] [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 69/84] [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 70/84] [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 71/84] [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 72/84] 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 81bf4e0b09ad76c7a01e4d0abe6785354e5f4e57 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Wed, 14 Aug 2024 15:51:51 +0200 Subject: [PATCH 73/84] [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 74/84] 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 75/84] 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 76/84] 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 77/84] [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 78/84] 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 79/84] [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 80/84] 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 81/84] [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 82/84] [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 83/84] 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 84/84] 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 */ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy