diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 437d5402a..fd57cc95e 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -344,7 +344,7 @@ public function load(array $configs, ContainerBuilder $container): void throw new LogicException('AssetMapper support cannot be enabled as the AssetMapper component is not installed. Try running "composer require symfony/asset-mapper".'); } - $this->registerAssetMapperConfiguration($config['asset_mapper'], $container, $loader, $this->readConfigEnabled('assets', $container, $config['assets'])); + $this->registerAssetMapperConfiguration($config['asset_mapper'], $container, $loader, $this->readConfigEnabled('assets', $container, $config['assets']), $this->readConfigEnabled('http_client', $container, $config['http_client'])); } else { $container->removeDefinition('cache.asset_mapper'); } @@ -1291,12 +1291,14 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co } } - private function registerAssetMapperConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $assetEnabled): void + private function registerAssetMapperConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $assetEnabled, bool $httpClientEnabled): void { $loader->load('asset_mapper.php'); - if (!$assetEnabled) { - $container->removeDefinition('asset_mapper.asset_package'); + if (!$httpClientEnabled) { + $container->register('asset_mapper.http_client', HttpClientInterface::class) + ->addTag('container.error') + ->addError('You cannot use the AssetMapper integration since the HttpClient component is not enabled. Try enabling the "framework.http_client" config option.'); } $paths = $config['paths']; @@ -1905,18 +1907,20 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder } $arguments = $container->getDefinition('serializer.normalizer.object')->getArguments(); - $context = []; + $context = $arguments[6] ?? $defaultContext; if (isset($config['circular_reference_handler']) && $config['circular_reference_handler']) { - $context += ($arguments[6] ?? $defaultContext) + ['circular_reference_handler' => new Reference($config['circular_reference_handler'])]; + $context += ['circular_reference_handler' => new Reference($config['circular_reference_handler'])]; $container->getDefinition('serializer.normalizer.object')->setArgument(5, null); } if ($config['max_depth_handler'] ?? false) { - $context += ($arguments[6] ?? $defaultContext) + ['max_depth_handler' => new Reference($config['max_depth_handler'])]; + $context += ['max_depth_handler' => new Reference($config['max_depth_handler'])]; } $container->getDefinition('serializer.normalizer.object')->setArgument(6, $context); + + $container->getDefinition('serializer.normalizer.property')->setArgument(5, $defaultContext); } private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void @@ -2855,6 +2859,11 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ ->addArgument(new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE)); } + if (ContainerBuilder::willBeAvailable('symfony/bluesky-notifier', NotifierBridge\Bluesky\BlueskyTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier'])) { + $container->getDefinition($classToServices[NotifierBridge\Bluesky\BlueskyTransportFactory::class]) + ->addArgument(new Reference('logger')); + } + if (isset($config['admin_recipients'])) { $notifier = $container->getDefinition('notifier'); foreach ($config['admin_recipients'] as $i => $recipient) { diff --git a/Resources/config/asset_mapper.php b/Resources/config/asset_mapper.php index b7ce65f03..404e7af18 100644 --- a/Resources/config/asset_mapper.php +++ b/Resources/config/asset_mapper.php @@ -54,6 +54,8 @@ ]) ->alias(AssetMapperInterface::class, 'asset_mapper') + ->alias('asset_mapper.http_client', 'http_client') + ->set('asset_mapper.mapped_asset_factory', MappedAssetFactory::class) ->args([ service('asset_mapper.public_assets_path_resolver'), @@ -197,7 +199,7 @@ ]) ->set('asset_mapper.importmap.resolver', JsDelivrEsmResolver::class) - ->args([service('http_client')]) + ->args([service('asset_mapper.http_client')]) ->set('asset_mapper.importmap.renderer', ImportMapRenderer::class) ->args([ @@ -212,12 +214,12 @@ ->set('asset_mapper.importmap.auditor', ImportMapAuditor::class) ->args([ service('asset_mapper.importmap.config_reader'), - service('http_client'), + service('asset_mapper.http_client'), ]) ->set('asset_mapper.importmap.update_checker', ImportMapUpdateChecker::class) ->args([ service('asset_mapper.importmap.config_reader'), - service('http_client'), + service('asset_mapper.http_client'), ]) ->set('asset_mapper.importmap.command.require', ImportMapRequireCommand::class) diff --git a/Resources/config/notifier_transports.php b/Resources/config/notifier_transports.php index df9be94ed..5ddc9ae24 100644 --- a/Resources/config/notifier_transports.php +++ b/Resources/config/notifier_transports.php @@ -27,7 +27,6 @@ $chatterFactories = [ 'bluesky' => Bridge\Bluesky\BlueskyTransportFactory::class, - 'brevo' => Bridge\Brevo\BrevoTransportFactory::class, 'chatwork' => Bridge\Chatwork\ChatworkTransportFactory::class, 'discord' => Bridge\Discord\DiscordTransportFactory::class, 'fake-chat' => Bridge\FakeChat\FakeChatTransportFactory::class, @@ -59,6 +58,7 @@ $texterFactories = [ 'all-my-sms' => Bridge\AllMySms\AllMySmsTransportFactory::class, 'bandwidth' => Bridge\Bandwidth\BandwidthTransportFactory::class, + 'brevo' => Bridge\Brevo\BrevoTransportFactory::class, 'click-send' => Bridge\ClickSend\ClickSendTransportFactory::class, 'clickatell' => Bridge\Clickatell\ClickatellTransportFactory::class, 'contact-everyone' => Bridge\ContactEveryone\ContactEveryoneTransportFactory::class, diff --git a/Resources/config/serializer.php b/Resources/config/serializer.php index 1135d3752..c75776900 100644 --- a/Resources/config/serializer.php +++ b/Resources/config/serializer.php @@ -140,7 +140,6 @@ service('property_info')->ignoreOnInvalid(), service('serializer.mapping.class_discriminator_resolver')->ignoreOnInvalid(), null, - [], ]) ->set('serializer.denormalizer.array', ArrayDenormalizer::class) diff --git a/Routing/Router.php b/Routing/Router.php index 4dfb71e74..69428a1b7 100644 --- a/Routing/Router.php +++ b/Routing/Router.php @@ -69,7 +69,7 @@ public function getRouteCollection(): RouteCollection $this->collection->addResource(new ContainerParametersResource($this->collectedParameters)); try { - $containerFile = ($this->paramFetcher)('kernel.cache_dir').'/'.($this->paramFetcher)('kernel.container_class').'.php'; + $containerFile = ($this->paramFetcher)('kernel.build_dir').'/'.($this->paramFetcher)('kernel.container_class').'.php'; if (file_exists($containerFile)) { $this->collection->addResource(new FileResource($containerFile)); } else { @@ -84,14 +84,12 @@ public function getRouteCollection(): RouteCollection public function warmUp(string $cacheDir, ?string $buildDir = null): array { - if (!$buildDir) { - return []; + if (null === $currentDir = $this->getOption('cache_dir')) { + return []; // skip warmUp when router doesn't use cache } - $currentDir = $this->getOption('cache_dir'); - - // force cache generation in build_dir - $this->setOption('cache_dir', $buildDir); + // force cache generation + $this->setOption('cache_dir', $buildDir ?? $cacheDir); $this->getMatcher(); $this->getGenerator(); diff --git a/Tests/Functional/AbstractWebTestCase.php b/Tests/Functional/AbstractWebTestCase.php index 085cb812e..17ff5ed73 100644 --- a/Tests/Functional/AbstractWebTestCase.php +++ b/Tests/Functional/AbstractWebTestCase.php @@ -53,7 +53,7 @@ protected static function getKernelClass(): string protected static function createKernel(array $options = []): KernelInterface { - $class = self::getKernelClass(); + $class = static::getKernelClass(); if (!isset($options['test_case'])) { throw new \InvalidArgumentException('The option "test_case" must be set.'); diff --git a/Tests/Functional/SerializerTest.php b/Tests/Functional/SerializerTest.php index 2856816d1..9d75c5bf6 100644 --- a/Tests/Functional/SerializerTest.php +++ b/Tests/Functional/SerializerTest.php @@ -12,6 +12,9 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\TranslatableBackedEnum; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\AppKernel; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; /** * @author Kévin Dunglas @@ -35,39 +38,58 @@ public function testDeserializeArrayOfObject() $this->assertEquals($expected, $result); } - /** - * @dataProvider provideNormalizersAndEncodersWithDefaultContextOption - */ - public function testNormalizersAndEncodersUseDefaultContextConfigOption(string $normalizerId) + public function testNormalizersAndEncodersUseDefaultContextConfigOption() { - static::bootKernel(['test_case' => 'Serializer']); + /** @var SerializerKernel $kernel */ + $kernel = static::bootKernel(['test_case' => 'Serializer', 'root_config' => 'default_context.yaml']); + + foreach ($kernel->normalizersAndEncoders as $normalizerOrEncoderId) { + if (!static::getContainer()->has($normalizerOrEncoderId)) { + continue; + } + + $normalizerOrEncoder = static::getContainer()->get($normalizerOrEncoderId); - $normalizer = static::getContainer()->get($normalizerId); + $reflectionObject = new \ReflectionObject($normalizerOrEncoder); + $property = $reflectionObject->getProperty('defaultContext'); - $reflectionObject = new \ReflectionObject($normalizer); - $property = $reflectionObject->getProperty('defaultContext'); + $defaultContext = $property->getValue($normalizerOrEncoder); - $defaultContext = $property->getValue($normalizer); + self::assertArrayHasKey('fake_context_option', $defaultContext); + self::assertEquals('foo', $defaultContext['fake_context_option']); + } + } - self::assertArrayHasKey('fake_context_option', $defaultContext); - self::assertEquals('foo', $defaultContext['fake_context_option']); + protected static function getKernelClass(): string + { + return SerializerKernel::class; } +} + +class SerializerKernel extends AppKernel implements CompilerPassInterface +{ + public $normalizersAndEncoders = [ + 'serializer.normalizer.property.alias', // Special case as this normalizer isn't tagged + ]; - public static function provideNormalizersAndEncodersWithDefaultContextOption(): array + public function process(ContainerBuilder $container): void { - return [ - ['serializer.normalizer.constraint_violation_list.alias'], - ['serializer.normalizer.dateinterval.alias'], - ['serializer.normalizer.datetime.alias'], - ['serializer.normalizer.json_serializable.alias'], - ['serializer.normalizer.problem.alias'], - ['serializer.normalizer.uid.alias'], - ['serializer.normalizer.translatable.alias'], - ['serializer.normalizer.object.alias'], - ['serializer.encoder.xml.alias'], - ['serializer.encoder.yaml.alias'], - ['serializer.encoder.csv.alias'], - ]; + $services = array_merge( + $container->findTaggedServiceIds('serializer.normalizer'), + $container->findTaggedServiceIds('serializer.encoder') + ); + foreach ($services as $serviceId => $attributes) { + $class = $container->getDefinition($serviceId)->getClass(); + if (null === $reflectionConstructor = (new \ReflectionClass($class))->getConstructor()) { + continue; + } + foreach ($reflectionConstructor->getParameters() as $reflectionParam) { + if ('array $defaultContext' === $reflectionParam->getType()->getName().' $'.$reflectionParam->getName()) { + $this->normalizersAndEncoders[] = $serviceId.'.alias'; + break; + } + } + } } public function testSerializeTranslatableBackedEnum() diff --git a/Tests/Functional/app/AppKernel.php b/Tests/Functional/app/AppKernel.php index 06dc2d637..2fdbaea0f 100644 --- a/Tests/Functional/app/AppKernel.php +++ b/Tests/Functional/app/AppKernel.php @@ -49,6 +49,11 @@ public function __construct($varDir, $testCase, $rootConfig, $environment, $debu parent::__construct($environment, $debug); } + protected function getContainerClass(): string + { + return parent::getContainerClass().substr(md5($this->rootConfig), -16); + } + public function registerBundles(): iterable { if (!file_exists($filename = $this->getProjectDir().'/'.$this->testCase.'/bundles.php')) { diff --git a/Tests/Functional/app/Serializer/config.yml b/Tests/Functional/app/Serializer/config.yml index 987cc384c..2f20dab9e 100644 --- a/Tests/Functional/app/Serializer/config.yml +++ b/Tests/Functional/app/Serializer/config.yml @@ -10,7 +10,6 @@ framework: max_depth_handler: Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serializer\MaxDepthHandler default_context: enable_max_depth: true - fake_context_option: foo property_info: { enabled: true } services: @@ -18,54 +17,6 @@ services: alias: serializer public: true - serializer.normalizer.constraint_violation_list.alias: - alias: serializer.normalizer.constraint_violation_list - public: true - - serializer.normalizer.dateinterval.alias: - alias: serializer.normalizer.dateinterval - public: true - - serializer.normalizer.datetime.alias: - alias: serializer.normalizer.datetime - public: true - - serializer.normalizer.json_serializable.alias: - alias: serializer.normalizer.json_serializable - public: true - - serializer.normalizer.problem.alias: - alias: serializer.normalizer.problem - public: true - - serializer.normalizer.uid.alias: - alias: serializer.normalizer.uid - public: true - - serializer.normalizer.translatable.alias: - alias: serializer.normalizer.translatable - public: true - - serializer.normalizer.property.alias: - alias: serializer.normalizer.property - public: true - - serializer.normalizer.object.alias: - alias: serializer.normalizer.object - public: true - - serializer.encoder.xml.alias: - alias: serializer.encoder.xml - public: true - - serializer.encoder.yaml.alias: - alias: serializer.encoder.yaml - public: true - - serializer.encoder.csv.alias: - alias: serializer.encoder.csv - public: true - Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serializer\CircularReferenceHandler: ~ Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serializer\MaxDepthHandler: ~ diff --git a/Tests/Functional/app/Serializer/default_context.yaml b/Tests/Functional/app/Serializer/default_context.yaml new file mode 100644 index 000000000..de6114c5d --- /dev/null +++ b/Tests/Functional/app/Serializer/default_context.yaml @@ -0,0 +1,59 @@ +imports: + - { resource: ../config/default.yml } + +framework: + serializer: + enabled: true + circular_reference_handler: ~ # This must be null + max_depth_handler: ~ # This must be null + default_context: + fake_context_option: foo + +services: + serializer.normalizer.constraint_violation_list.alias: + alias: serializer.normalizer.constraint_violation_list + public: true + + serializer.normalizer.dateinterval.alias: + alias: serializer.normalizer.dateinterval + public: true + + serializer.normalizer.datetime.alias: + alias: serializer.normalizer.datetime + public: true + + serializer.normalizer.json_serializable.alias: + alias: serializer.normalizer.json_serializable + public: true + + serializer.normalizer.object.alias: + alias: serializer.normalizer.object + public: true + + serializer.normalizer.problem.alias: + alias: serializer.normalizer.problem + public: true + + serializer.normalizer.property.alias: + alias: serializer.normalizer.property + public: true + + serializer.normalizer.uid.alias: + alias: serializer.normalizer.uid + public: true + + serializer.encoder.csv.alias: + alias: serializer.encoder.csv + public: true + + serializer.encoder.json.alias: + alias: serializer.encoder.json + public: true + + serializer.encoder.xml.alias: + alias: serializer.encoder.xml + public: true + + serializer.encoder.yaml.alias: + alias: serializer.encoder.yaml + public: true