Skip to content

Commit c15274a

Browse files
committed
feature #43788 [DependencyInjection][FrameworkBundle][SecurityBundle][TwigBundle] Require Composer's runtime API to be present (derrabus)
This PR was merged into the 6.0 branch. Discussion ---------- [DependencyInjection][FrameworkBundle][SecurityBundle][TwigBundle] Require Composer's runtime API to be present | Q | A | ------------- | --- | Branch? | 6.0 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | N/A | License | MIT | Doc PR | N/A Recently, I was asked to help debugging a strange behavior of my client's app that surfaced only on some developer machines while others were fine. Turns out, a particular package was installed as a dev dependency (which was fine because we used it for dev tooling only at that time) and the difference between the environments was that the broken ones used Composer 2. With `ContainerBuilder::willBeAvailable()`, we have introduced logic into the very heart of the framework that exposes significantly different behavior for Composer 1 and 2. With this PR, I'd like to propose to make Composer's runtime API a requirement, essentially making the use of Composer 2 a requirement. Composer 2 has been released over a year ago and by now every developer should have been able to upgrade to version 2. I don't think that this constraint would push the ecosystem too hard. Let's make everyone's lives easier by moving on to Composer 2. Commits ------- e08b36258b Require Composer's runtime API to be present
2 parents 84bc4b7 + 5199aef commit c15274a

File tree

3 files changed

+29
-33
lines changed

3 files changed

+29
-33
lines changed

DependencyInjection/Configuration.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public function getConfigTreeBuilder(): TreeBuilder
122122
$parentPackages = (array) $parentPackage;
123123
$parentPackages[] = 'symfony/framework-bundle';
124124

125-
return ContainerBuilder::willBeAvailable($package, $class, $parentPackages, true);
125+
return ContainerBuilder::willBeAvailable($package, $class, $parentPackages);
126126
};
127127

128128
$enableIfStandalone = static function (string $package, string $class) use ($willBeAvailable) {

DependencyInjection/FrameworkExtension.php

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;
1313

14-
use Composer\InstalledVersions;
1514
use Doctrine\Common\Annotations\AnnotationRegistry;
1615
use Doctrine\Common\Annotations\Reader;
1716
use Http\Client\HttpClient;
@@ -234,18 +233,14 @@ class FrameworkExtension extends Extension
234233
*/
235234
public function load(array $configs, ContainerBuilder $container)
236235
{
237-
if (!class_exists(InstalledVersions::class)) {
238-
trigger_deprecation('symfony/framework-bundle', '5.4', 'Configuring Symfony without the Composer Runtime API is deprecated. Consider upgrading to Composer 2.1 or later.');
239-
}
240-
241236
$loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config'));
242237

243238
$loader->load('web.php');
244239
$loader->load('services.php');
245240
$loader->load('fragment_renderer.php');
246241
$loader->load('error_renderer.php');
247242

248-
if (ContainerBuilder::willBeAvailable('psr/event-dispatcher', PsrEventDispatcherInterface::class, ['symfony/framework-bundle'], true)) {
243+
if (ContainerBuilder::willBeAvailable('psr/event-dispatcher', PsrEventDispatcherInterface::class, ['symfony/framework-bundle'])) {
249244
$container->setAlias(PsrEventDispatcherInterface::class, 'event_dispatcher');
250245
}
251246

@@ -292,11 +287,11 @@ public function load(array $configs, ContainerBuilder $container)
292287
$container->getDefinition('response_listener')->replaceArgument(1, $config['set_content_language_from_locale']);
293288

294289
// If the slugger is used but the String component is not available, we should throw an error
295-
if (!ContainerBuilder::willBeAvailable('symfony/string', SluggerInterface::class, ['symfony/framework-bundle'], true)) {
290+
if (!ContainerBuilder::willBeAvailable('symfony/string', SluggerInterface::class, ['symfony/framework-bundle'])) {
296291
$container->register('slugger', 'stdClass')
297292
->addError('You cannot use the "slugger" service since the String component is not installed. Try running "composer require symfony/string".');
298293
} else {
299-
if (!ContainerBuilder::willBeAvailable('symfony/translation', LocaleAwareInterface::class, ['symfony/framework-bundle'], true)) {
294+
if (!ContainerBuilder::willBeAvailable('symfony/translation', LocaleAwareInterface::class, ['symfony/framework-bundle'])) {
300295
$container->register('slugger', 'stdClass')
301296
->addError('You cannot use the "slugger" service since the Translation contracts are not installed. Try running "composer require symfony/translation".');
302297
}
@@ -352,7 +347,7 @@ public function load(array $configs, ContainerBuilder $container)
352347
}
353348

354349
if (null === $config['csrf_protection']['enabled']) {
355-
$config['csrf_protection']['enabled'] = $this->sessionConfigEnabled && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle'], true);
350+
$config['csrf_protection']['enabled'] = $this->sessionConfigEnabled && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']);
356351
}
357352
$this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader);
358353

@@ -364,7 +359,7 @@ public function load(array $configs, ContainerBuilder $container)
364359
$this->formConfigEnabled = true;
365360
$this->registerFormConfiguration($config, $container, $loader);
366361

367-
if (ContainerBuilder::willBeAvailable('symfony/validator', Validation::class, ['symfony/framework-bundle', 'symfony/form'], true)) {
362+
if (ContainerBuilder::willBeAvailable('symfony/validator', Validation::class, ['symfony/framework-bundle', 'symfony/form'])) {
368363
$config['validation']['enabled'] = true;
369364
} else {
370365
$container->setParameter('validator.translation_domain', 'validators');
@@ -494,7 +489,7 @@ public function load(array $configs, ContainerBuilder $container)
494489
'Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController',
495490
]);
496491

497-
if (ContainerBuilder::willBeAvailable('symfony/mime', MimeTypes::class, ['symfony/framework-bundle'], true)) {
492+
if (ContainerBuilder::willBeAvailable('symfony/mime', MimeTypes::class, ['symfony/framework-bundle'])) {
498493
$loader->load('mime_type.php');
499494
}
500495

@@ -647,7 +642,7 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont
647642
$container->setParameter('form.type_extension.csrf.enabled', false);
648643
}
649644

650-
if (!ContainerBuilder::willBeAvailable('symfony/translation', Translator::class, ['symfony/framework-bundle', 'symfony/form'], true)) {
645+
if (!ContainerBuilder::willBeAvailable('symfony/translation', Translator::class, ['symfony/framework-bundle', 'symfony/form'])) {
651646
$container->removeDefinition('form.type_extension.upload.validator');
652647
}
653648
if (!method_exists(CachingFactoryDecorator::class, 'reset')) {
@@ -1030,7 +1025,7 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co
10301025
$container->getDefinition('routing.loader')->replaceArgument(2, ['_locale' => $enabledLocales]);
10311026
}
10321027

1033-
if (!ContainerBuilder::willBeAvailable('symfony/expression-language', ExpressionLanguage::class, ['symfony/framework-bundle', 'symfony/routing'], true)) {
1028+
if (!ContainerBuilder::willBeAvailable('symfony/expression-language', ExpressionLanguage::class, ['symfony/framework-bundle', 'symfony/routing'])) {
10341029
$container->removeDefinition('router.expression_language_provider');
10351030
}
10361031

@@ -1244,17 +1239,17 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
12441239
$dirs = [];
12451240
$transPaths = [];
12461241
$nonExistingDirs = [];
1247-
if (ContainerBuilder::willBeAvailable('symfony/validator', Validation::class, ['symfony/framework-bundle', 'symfony/translation'], true)) {
1242+
if (ContainerBuilder::willBeAvailable('symfony/validator', Validation::class, ['symfony/framework-bundle', 'symfony/translation'])) {
12481243
$r = new \ReflectionClass(Validation::class);
12491244

12501245
$dirs[] = $transPaths[] = \dirname($r->getFileName()).'/Resources/translations';
12511246
}
1252-
if (ContainerBuilder::willBeAvailable('symfony/form', Form::class, ['symfony/framework-bundle', 'symfony/translation'], true)) {
1247+
if (ContainerBuilder::willBeAvailable('symfony/form', Form::class, ['symfony/framework-bundle', 'symfony/translation'])) {
12531248
$r = new \ReflectionClass(Form::class);
12541249

12551250
$dirs[] = $transPaths[] = \dirname($r->getFileName()).'/Resources/translations';
12561251
}
1257-
if (ContainerBuilder::willBeAvailable('symfony/security-core', AuthenticationException::class, ['symfony/framework-bundle', 'symfony/translation'], true)) {
1252+
if (ContainerBuilder::willBeAvailable('symfony/security-core', AuthenticationException::class, ['symfony/framework-bundle', 'symfony/translation'])) {
12581253
$r = new \ReflectionClass(AuthenticationException::class);
12591254

12601255
$dirs[] = $transPaths[] = \dirname($r->getFileName(), 2).'/Resources/translations';
@@ -1359,7 +1354,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
13591354
foreach ($classToServices as $class => $service) {
13601355
$package = substr($service, \strlen('translation.provider_factory.'));
13611356

1362-
if (!$container->hasDefinition('http_client') || !ContainerBuilder::willBeAvailable(sprintf('symfony/%s-translation-provider', $package), $class, $parentPackages, true)) {
1357+
if (!$container->hasDefinition('http_client') || !ContainerBuilder::willBeAvailable(sprintf('symfony/%s-translation-provider', $package), $class, $parentPackages)) {
13631358
$container->removeDefinition($service);
13641359
}
13651360
}
@@ -1474,7 +1469,7 @@ private function registerValidatorMapping(ContainerBuilder $container, array $co
14741469
$files['yaml' === $extension ? 'yml' : $extension][] = $path;
14751470
};
14761471

1477-
if (ContainerBuilder::willBeAvailable('symfony/form', Form::class, ['symfony/framework-bundle', 'symfony/validator'], true)) {
1472+
if (ContainerBuilder::willBeAvailable('symfony/form', Form::class, ['symfony/framework-bundle', 'symfony/validator'])) {
14781473
$reflClass = new \ReflectionClass(Form::class);
14791474
$fileRecorder('xml', \dirname($reflClass->getFileName()).'/Resources/config/validation.xml');
14801475
}
@@ -1640,7 +1635,7 @@ private function registerSecretsConfiguration(array $config, ContainerBuilder $c
16401635
throw new InvalidArgumentException(sprintf('Invalid value "%s" set as "decryption_env_var": only "word" characters are allowed.', $config['decryption_env_var']));
16411636
}
16421637

1643-
if (ContainerBuilder::willBeAvailable('symfony/string', LazyString::class, ['symfony/framework-bundle'], true)) {
1638+
if (ContainerBuilder::willBeAvailable('symfony/string', LazyString::class, ['symfony/framework-bundle'])) {
16441639
$container->getDefinition('secrets.decryption_key')->replaceArgument(1, $config['decryption_env_var']);
16451640
} else {
16461641
$container->getDefinition('secrets.vault')->replaceArgument(1, "%env({$config['decryption_env_var']})%");
@@ -1776,7 +1771,7 @@ private function registerPropertyInfoConfiguration(ContainerBuilder $container,
17761771

17771772
$loader->load('property_info.php');
17781773

1779-
if (ContainerBuilder::willBeAvailable('phpdocumentor/reflection-docblock', DocBlockFactoryInterface::class, ['symfony/framework-bundle', 'symfony/property-info'], true)) {
1774+
if (ContainerBuilder::willBeAvailable('phpdocumentor/reflection-docblock', DocBlockFactoryInterface::class, ['symfony/framework-bundle', 'symfony/property-info'])) {
17801775
$definition = $container->register('property_info.php_doc_extractor', 'Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor');
17811776
$definition->addTag('property_info.description_extractor', ['priority' => -1000]);
17821777
$definition->addTag('property_info.type_extractor', ['priority' => -1001]);
@@ -1847,19 +1842,19 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder
18471842

18481843
$loader->load('messenger.php');
18491844

1850-
if (ContainerBuilder::willBeAvailable('symfony/amqp-messenger', AmqpTransportFactory::class, ['symfony/framework-bundle', 'symfony/messenger'], true)) {
1845+
if (ContainerBuilder::willBeAvailable('symfony/amqp-messenger', AmqpTransportFactory::class, ['symfony/framework-bundle', 'symfony/messenger'])) {
18511846
$container->getDefinition('messenger.transport.amqp.factory')->addTag('messenger.transport_factory');
18521847
}
18531848

1854-
if (ContainerBuilder::willBeAvailable('symfony/redis-messenger', RedisTransportFactory::class, ['symfony/framework-bundle', 'symfony/messenger'], true)) {
1849+
if (ContainerBuilder::willBeAvailable('symfony/redis-messenger', RedisTransportFactory::class, ['symfony/framework-bundle', 'symfony/messenger'])) {
18551850
$container->getDefinition('messenger.transport.redis.factory')->addTag('messenger.transport_factory');
18561851
}
18571852

1858-
if (ContainerBuilder::willBeAvailable('symfony/amazon-sqs-messenger', AmazonSqsTransportFactory::class, ['symfony/framework-bundle', 'symfony/messenger'], true)) {
1853+
if (ContainerBuilder::willBeAvailable('symfony/amazon-sqs-messenger', AmazonSqsTransportFactory::class, ['symfony/framework-bundle', 'symfony/messenger'])) {
18591854
$container->getDefinition('messenger.transport.sqs.factory')->addTag('messenger.transport_factory');
18601855
}
18611856

1862-
if (ContainerBuilder::willBeAvailable('symfony/beanstalkd-messenger', BeanstalkdTransportFactory::class, ['symfony/framework-bundle', 'symfony/messenger'], true)) {
1857+
if (ContainerBuilder::willBeAvailable('symfony/beanstalkd-messenger', BeanstalkdTransportFactory::class, ['symfony/framework-bundle', 'symfony/messenger'])) {
18631858
$container->getDefinition('messenger.transport.beanstalkd.factory')->addTag('messenger.transport_factory');
18641859
}
18651860

@@ -2185,12 +2180,12 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder
21852180
unset($options['retry_failed']);
21862181
$container->getDefinition('http_client')->setArguments([$options, $config['max_host_connections'] ?? 6]);
21872182

2188-
if (!$hasPsr18 = ContainerBuilder::willBeAvailable('psr/http-client', ClientInterface::class, ['symfony/framework-bundle', 'symfony/http-client'], true)) {
2183+
if (!$hasPsr18 = ContainerBuilder::willBeAvailable('psr/http-client', ClientInterface::class, ['symfony/framework-bundle', 'symfony/http-client'])) {
21892184
$container->removeDefinition('psr18.http_client');
21902185
$container->removeAlias(ClientInterface::class);
21912186
}
21922187

2193-
if (!ContainerBuilder::willBeAvailable('php-http/httplug', HttpClient::class, ['symfony/framework-bundle', 'symfony/http-client'], true)) {
2188+
if (!ContainerBuilder::willBeAvailable('php-http/httplug', HttpClient::class, ['symfony/framework-bundle', 'symfony/http-client'])) {
21942189
$container->removeDefinition(HttpClient::class);
21952190
}
21962191

@@ -2320,7 +2315,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co
23202315
foreach ($classToServices as $class => $service) {
23212316
$package = substr($service, \strlen('mailer.transport_factory.'));
23222317

2323-
if (!ContainerBuilder::willBeAvailable(sprintf('symfony/%s-mailer', 'gmail' === $package ? 'google' : $package), $class, ['symfony/framework-bundle', 'symfony/mailer'], true)) {
2318+
if (!ContainerBuilder::willBeAvailable(sprintf('symfony/%s-mailer', 'gmail' === $package ? 'google' : $package), $class, ['symfony/framework-bundle', 'symfony/mailer'])) {
23242319
$container->removeDefinition($service);
23252320
}
23262321
}
@@ -2462,25 +2457,25 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $
24622457
case 'turbosms': $package = 'turbo-sms'; break;
24632458
}
24642459

2465-
if (!ContainerBuilder::willBeAvailable(sprintf('symfony/%s-notifier', $package), $class, $parentPackages, true)) {
2460+
if (!ContainerBuilder::willBeAvailable(sprintf('symfony/%s-notifier', $package), $class, $parentPackages)) {
24662461
$container->removeDefinition($service);
24672462
}
24682463
}
24692464

2470-
if (ContainerBuilder::willBeAvailable('symfony/mercure-notifier', MercureTransportFactory::class, $parentPackages, true) && ContainerBuilder::willBeAvailable('symfony/mercure-bundle', MercureBundle::class, $parentPackages, true)) {
2465+
if (ContainerBuilder::willBeAvailable('symfony/mercure-notifier', MercureTransportFactory::class, $parentPackages) && ContainerBuilder::willBeAvailable('symfony/mercure-bundle', MercureBundle::class, $parentPackages)) {
24712466
$container->getDefinition($classToServices[MercureTransportFactory::class])
24722467
->replaceArgument('$registry', new Reference(HubRegistry::class));
2473-
} elseif (ContainerBuilder::willBeAvailable('symfony/mercure-notifier', MercureTransportFactory::class, $parentPackages, true)) {
2468+
} elseif (ContainerBuilder::willBeAvailable('symfony/mercure-notifier', MercureTransportFactory::class, $parentPackages)) {
24742469
$container->removeDefinition($classToServices[MercureTransportFactory::class]);
24752470
}
24762471

2477-
if (ContainerBuilder::willBeAvailable('symfony/fake-chat-notifier', FakeChatTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier', 'symfony/mailer'], true)) {
2472+
if (ContainerBuilder::willBeAvailable('symfony/fake-chat-notifier', FakeChatTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier', 'symfony/mailer'])) {
24782473
$container->getDefinition($classToServices[FakeChatTransportFactory::class])
24792474
->replaceArgument('$mailer', new Reference('mailer'))
24802475
->replaceArgument('$logger', new Reference('logger'));
24812476
}
24822477

2483-
if (ContainerBuilder::willBeAvailable('symfony/fake-sms-notifier', FakeSmsTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier', 'symfony/mailer'], true)) {
2478+
if (ContainerBuilder::willBeAvailable('symfony/fake-sms-notifier', FakeSmsTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier', 'symfony/mailer'])) {
24842479
$container->getDefinition($classToServices[FakeSmsTransportFactory::class])
24852480
->replaceArgument('$mailer', new Reference('mailer'))
24862481
->replaceArgument('$logger', new Reference('logger'));

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
],
1818
"require": {
1919
"php": ">=8.0.2",
20+
"composer-runtime-api": ">=2.1",
2021
"ext-xml": "*",
2122
"symfony/cache": "^5.4|^6.0",
2223
"symfony/config": "^5.4|^6.0",

0 commit comments

Comments
 (0)