Skip to content

Commit 39edcdd

Browse files
authored
fix(symfony): skip ErrorResourceAttributeLoaderPass on Symfony 6.4 (#8253)
Fixes #8244
1 parent b5c41af commit 39edcdd

2 files changed

Lines changed: 55 additions & 2 deletions

File tree

src/Symfony/Bundle/DependencyInjection/Compiler/ErrorResourceAttributeLoaderPass.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1919
use Symfony\Component\DependencyInjection\ContainerBuilder;
2020
use Symfony\Component\DependencyInjection\Definition;
21+
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
2122
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
2223

2324
/**
@@ -43,15 +44,32 @@ public function process(ContainerBuilder $container): void
4344
return;
4445
}
4546

47+
// Symfony 6.4 ships an AttributeLoader with a different constructor signature
48+
// (`?Doctrine\Common\Annotations\Reader`), incompatible with the `allowAnyClass`/`mappedClasses`
49+
// arguments below. The `AnnotationLoader` class only exists on the 6.4 branch
50+
// (removed in 7.0), so its presence is a reliable marker for that signature. See #8244.
51+
if (class_exists(AnnotationLoader::class)) {
52+
return;
53+
}
54+
55+
$chainLoader = $container->getDefinition('serializer.mapping.chain_loader');
56+
$loaders = $chainLoader->getArgument(0);
57+
58+
// Skip when Symfony already wired an AttributeLoader (i.e. `enable_attributes: true`).
59+
// Adding another one would duplicate work and re-process every class twice.
60+
foreach ($loaders as $loader) {
61+
if ($loader instanceof Definition && is_a($loader->getClass(), AttributeLoader::class, true)) {
62+
return;
63+
}
64+
}
65+
4666
$mappedClasses = [
4767
Error::class => [Error::class],
4868
ValidationException::class => [ValidationException::class],
4969
];
5070

5171
$loaderDefinition = new Definition(AttributeLoader::class, [true, $mappedClasses]);
5272

53-
$chainLoader = $container->getDefinition('serializer.mapping.chain_loader');
54-
$loaders = $chainLoader->getArgument(0);
5573
$loaders[] = $loaderDefinition;
5674
$chainLoader->replaceArgument(0, $loaders);
5775

tests/Symfony/Bundle/DependencyInjection/Compiler/ErrorResourceAttributeLoaderPassTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,41 @@ public function testDoesNothingWhenChainLoaderIsAbsent(): void
7878
$this->assertFalse($container->hasDefinition('serializer.mapping.chain_loader'));
7979
}
8080

81+
public function testDoesNothingWhenChainAlreadyContainsAnAttributeLoader(): void
82+
{
83+
$container = new ContainerBuilder();
84+
$existing = new Definition(AttributeLoader::class);
85+
$container->setDefinition('serializer.mapping.chain_loader', new Definition(LoaderChain::class, [[$existing]]));
86+
$container->setDefinition('serializer.mapping.cache_warmer', new Definition(\stdClass::class, [[$existing]]));
87+
88+
(new ErrorResourceAttributeLoaderPass())->process($container);
89+
90+
$loaders = $container->getDefinition('serializer.mapping.chain_loader')->getArgument(0);
91+
$this->assertCount(1, $loaders, 'pass must not add another AttributeLoader when one is already wired (enable_attributes: true).');
92+
93+
$warmerLoaders = $container->getDefinition('serializer.mapping.cache_warmer')->getArgument(0);
94+
$this->assertCount(1, $warmerLoaders);
95+
}
96+
97+
/**
98+
* @see https://github.com/api-platform/core/issues/8244
99+
*/
100+
public function testSkipsOnSymfony64SerializerSignature(): void
101+
{
102+
if (!class_exists(\Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader::class)) {
103+
$this->markTestSkipped('Only relevant when running against symfony/serializer 6.4 (AnnotationLoader still present).');
104+
}
105+
106+
$container = new ContainerBuilder();
107+
$container->setDefinition('serializer.mapping.chain_loader', new Definition(LoaderChain::class, [[]]));
108+
$container->setDefinition('serializer.mapping.cache_warmer', new Definition(\stdClass::class, [[]]));
109+
110+
(new ErrorResourceAttributeLoaderPass())->process($container);
111+
112+
$this->assertSame([], $container->getDefinition('serializer.mapping.chain_loader')->getArgument(0));
113+
$this->assertSame([], $container->getDefinition('serializer.mapping.cache_warmer')->getArgument(0));
114+
}
115+
81116
/**
82117
* Mirrors the runtime behavior with `framework.serializer.enable_attributes: false`:
83118
* Symfony builds the `AttributeLoader` with `allowAnyClass = false` and no mapped classes,

0 commit comments

Comments
 (0)