diff --git a/src/Symfony/Bundle/Resources/config/api.php b/src/Symfony/Bundle/Resources/config/api.php index c1685a0c4a..32fa4e16f8 100644 --- a/src/Symfony/Bundle/Resources/config/api.php +++ b/src/Symfony/Bundle/Resources/config/api.php @@ -46,6 +46,14 @@ use ApiPlatform\Symfony\Routing\Router; use ApiPlatform\Symfony\Routing\SkolemIriConverter; use Negotiation\Negotiator; +use phpDocumentor\Reflection\DocBlockFactory; +use PHPStan\PhpDocParser\Parser\PhpDocParser; +use Symfony\Component\PropertyInfo\Extractor\ConstructorExtractor; +use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; +use Symfony\Component\PropertyInfo\Extractor\PhpStanExtractor; +use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; +use Symfony\Component\PropertyInfo\PropertyInfoCacheExtractor; +use Symfony\Component\PropertyInfo\PropertyInfoExtractor; use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; @@ -68,7 +76,44 @@ $services->alias('api_platform.property_accessor', 'property_accessor'); - $services->alias('api_platform.property_info', 'property_info'); + $services->set('api_platform.property_info.reflection_extractor', ReflectionExtractor::class) + ->tag('property_info.list_extractor', ['priority' => -1000]) + ->tag('property_info.type_extractor', ['priority' => -1002]) + ->tag('property_info.access_extractor', ['priority' => -1000]) + ->tag('property_info.initializable_extractor', ['priority' => -1000]); + + if (class_exists(DocBlockFactory::class)) { + $services->set('api_platform.property_info.php_doc_extractor', PhpDocExtractor::class) + ->tag('property_info.description_extractor', ['priority' => -1000]) + ->tag('property_info.type_extractor', ['priority' => -1001]) + ->tag('property_info.constructor_extractor', ['priority' => -1001]); + } + + if (class_exists(PhpDocParser::class)) { + $services->set('api_platform.property_info.phpstan_extractor', PhpStanExtractor::class) + ->tag('property_info.type_extractor', ['priority' => -1000]) + ->tag('property_info.constructor_extractor', ['priority' => -1000]); + } + + $services->set('api_platform.property_info.constructor_extractor', ConstructorExtractor::class) + ->args([tagged_iterator('property_info.constructor_extractor')]) + ->tag('property_info.type_extractor', ['priority' => -999]); + + $services->set('api_platform.property_info', PropertyInfoExtractor::class) + ->args([ + tagged_iterator('property_info.list_extractor'), + tagged_iterator('property_info.type_extractor'), + tagged_iterator('property_info.description_extractor'), + tagged_iterator('property_info.access_extractor'), + tagged_iterator('property_info.initializable_extractor'), + ]); + + $services->set('api_platform.property_info.cache', PropertyInfoCacheExtractor::class) + ->decorate('api_platform.property_info') + ->args([ + service('.inner'), + service('cache.property_info'), + ]); $services->set('api_platform.negotiator', Negotiator::class); diff --git a/tests/Symfony/Bundle/ApiPlatformBundleTest.php b/tests/Symfony/Bundle/ApiPlatformBundleTest.php index 57f844672b..660f12243d 100644 --- a/tests/Symfony/Bundle/ApiPlatformBundleTest.php +++ b/tests/Symfony/Bundle/ApiPlatformBundleTest.php @@ -29,9 +29,6 @@ use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\TestClientPass; use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\TestMercureHubPass; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; -use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -39,28 +36,29 @@ */ class ApiPlatformBundleTest extends TestCase { - use ProphecyTrait; - public function testBuild(): void { - $containerProphecy = $this->prophesize(ContainerBuilder::class); - // TODO: remove in 5.x - $containerProphecy->addCompilerPass(Argument::type(DataProviderPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(AttributeFilterPass::class), PassConfig::TYPE_BEFORE_OPTIMIZATION, 101)->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(AttributeResourcePass::class))->shouldBeCalled()->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(FilterPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(ElasticsearchClientPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(GraphQlTypePass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(GraphQlResolverPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(MetadataAwareNameConverterPass::class), PassConfig::TYPE_BEFORE_OPTIMIZATION, 100)->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(TestClientPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(TestMercureHubPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(AuthenticatorManagerPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(SerializerMappingLoaderPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(MutatorPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled(); - $containerProphecy->addCompilerPass(Argument::type(JsonStreamerTransformerPass::class), PassConfig::TYPE_BEFORE_OPTIMIZATION, -10)->willReturn($containerProphecy->reveal())->shouldBeCalled(); - + $container = new ContainerBuilder(); $bundle = new ApiPlatformBundle(); - $bundle->build($containerProphecy->reveal()); + $bundle->build($container); + + $passes = $container->getCompilerPassConfig()->getBeforeOptimizationPasses(); + $passClasses = array_map(static fn (object $p): string => $p::class, $passes); + + // TODO: remove in 5.x + $this->assertContains(DataProviderPass::class, $passClasses); + $this->assertContains(AttributeFilterPass::class, $passClasses); + $this->assertContains(AttributeResourcePass::class, $passClasses); + $this->assertContains(FilterPass::class, $passClasses); + $this->assertContains(ElasticsearchClientPass::class, $passClasses); + $this->assertContains(GraphQlTypePass::class, $passClasses); + $this->assertContains(GraphQlResolverPass::class, $passClasses); + $this->assertContains(MetadataAwareNameConverterPass::class, $passClasses); + $this->assertContains(TestClientPass::class, $passClasses); + $this->assertContains(TestMercureHubPass::class, $passClasses); + $this->assertContains(AuthenticatorManagerPass::class, $passClasses); + $this->assertContains(SerializerMappingLoaderPass::class, $passClasses); + $this->assertContains(MutatorPass::class, $passClasses); + $this->assertContains(JsonStreamerTransformerPass::class, $passClasses); } }