Skip to content

Commit 20371cc

Browse files
authored
fix: fix IriConverterInterface DI & deprecation (#5630)
1 parent c5f709d commit 20371cc

File tree

11 files changed

+225
-7
lines changed

11 files changed

+225
-7
lines changed

src/Core/Api/IriConverterInterface.php

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
* Converts item and resources to IRI and vice versa.
2323
*
2424
* @author Kévin Dunglas <[email protected]>
25+
*
26+
* @deprecated Since Api Platform 2.7, use ApiPlatform\Api\IriConverterInterface instead.
2527
*/
2628
interface IriConverterInterface
2729
{

src/Core/Api/LegacyIriConverter.php

+11-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
/**
2222
* This IRI converter calls the legacy IriConverter on legacy resources.
2323
*
24+
* It also replaces the new iri converter when BC flag is ON to allow using the new interface without new metadata system.
25+
*
2426
* @author Antoine Bluchet <[email protected]>
2527
*
2628
* @internal
@@ -30,7 +32,7 @@ final class LegacyIriConverter implements IriConverterInterface
3032
private $legacyIriConverter;
3133
private $iriConverter;
3234

33-
public function __construct(LegacyIriConverterInterface $legacyIriConverter, IriConverterInterface $iriConverter)
35+
public function __construct(LegacyIriConverterInterface $legacyIriConverter, IriConverterInterface $iriConverter = null)
3436
{
3537
$this->legacyIriConverter = $legacyIriConverter;
3638
$this->iriConverter = $iriConverter;
@@ -41,6 +43,10 @@ public function __construct(LegacyIriConverterInterface $legacyIriConverter, Iri
4143
*/
4244
public function getResourceFromIri(string $iri, array $context = [], ?Operation $operation = null)
4345
{
46+
if (null === $this->iriConverter) {
47+
return $this->legacyIriConverter->getItemFromIri($iri, $context);
48+
}
49+
4450
if (!$operation && !($operation = $context['operation'] ?? null)) {
4551
return $this->iriConverter->getResourceFromIri($iri, $context);
4652
}
@@ -57,6 +63,10 @@ public function getResourceFromIri(string $iri, array $context = [], ?Operation
5763
*/
5864
public function getIriFromResource($item, int $referenceType = UrlGeneratorInterface::ABS_PATH, Operation $operation = null, array $context = []): ?string
5965
{
66+
if (null === $this->iriConverter) {
67+
return \is_string($item) ? $this->legacyIriConverter->getIriFromResourceClass($item, $referenceType) : $this->legacyIriConverter->getIriFromItem($item, $referenceType);
68+
}
69+
6070
return $this->iriConverter->getIriFromResource($item, $referenceType, $operation, $context);
6171
}
6272
}

src/Core/Bridge/Symfony/Routing/IriConverter.php

+2-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
use ApiPlatform\Exception\InvalidIdentifierException;
3232
use ApiPlatform\Exception\ItemNotFoundException;
3333
use ApiPlatform\Exception\RuntimeException;
34-
use ApiPlatform\Symfony\Routing\IriConverter as NewIriConverter;
3534
use ApiPlatform\Util\AttributesExtractor;
3635
use ApiPlatform\Util\ResourceClassInfoTrait;
3736
use Symfony\Component\PropertyAccess\PropertyAccess;
@@ -43,6 +42,8 @@
4342
* {@inheritdoc}
4443
*
4544
* @author Kévin Dunglas <[email protected]>
45+
*
46+
* @deprecated Since Api Platform 2.7, use ApiPlatform\Symfony\Routing\IriConverter instead.
4647
*/
4748
final class IriConverter implements IriConverterInterface
4849
{
@@ -63,8 +64,6 @@ public function __construct(PropertyNameCollectionFactoryInterface $propertyName
6364
$this->resourceClassResolver = $resourceClassResolver;
6465
$this->identifiersExtractor = $identifiersExtractor ?: new IdentifiersExtractor($propertyNameCollectionFactory, $propertyMetadataFactory, $propertyAccessor ?? PropertyAccess::createPropertyAccessor());
6566
$this->resourceMetadataFactory = $resourceMetadataFactory;
66-
67-
trigger_deprecation('api-platform/core', '2.7', sprintf('The service "%s" is deprecated, use %s instead.', self::class, NewIriConverter::class));
6867
}
6968

7069
/**

src/Symfony/Bundle/ApiPlatformBundle.php

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\AnnotationFilterPass;
1717
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\AuthenticatorManagerPass;
1818
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\DataProviderPass;
19+
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\DeprecateLegacyIriConverterPass;
1920
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\DeprecateMercurePublisherPass;
2021
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\ElasticsearchClientPass;
2122
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\FilterPass;
@@ -54,6 +55,7 @@ public function build(ContainerBuilder $container)
5455
$container->addCompilerPass(new MetadataAwareNameConverterPass());
5556
$container->addCompilerPass(new TestClientPass());
5657
$container->addCompilerPass(new AuthenticatorManagerPass());
58+
$container->addCompilerPass(new DeprecateLegacyIriConverterPass());
5759
}
5860
}
5961

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler;
15+
16+
use ApiPlatform\Core\Api\IriConverterInterface;
17+
use Symfony\Component\Config\Definition\BaseNode;
18+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
19+
use Symfony\Component\DependencyInjection\ContainerBuilder;
20+
21+
/**
22+
* @internal calls `setDeprecated` method with valid arguments depending on which version of symfony/dependency-injection is used
23+
*/
24+
final class DeprecateLegacyIriConverterPass implements CompilerPassInterface
25+
{
26+
public function process(ContainerBuilder $container)
27+
{
28+
if ($container->hasAlias(IriConverterInterface::class)) {
29+
$container
30+
->getAlias(IriConverterInterface::class)
31+
->setDeprecated(...$this->buildDeprecationArgs('Using "%alias_id%" is deprecated since API Platform 2.7. Use "ApiPlatform\Api\IriConverterInterface" instead.'));
32+
}
33+
34+
if ($container->hasDefinition(IriConverterInterface::class)) {
35+
$container
36+
->getDefinition(IriConverterInterface::class)
37+
->setDeprecated(...$this->buildDeprecationArgs('Using "%service_id%" is deprecated since API Platform 2.7. Use "ApiPlatform\Api\IriConverterInterface" instead.'));
38+
}
39+
40+
if ($container->hasDefinition('api_platform.iri_converter.legacy')) {
41+
$container
42+
->getDefinition('api_platform.iri_converter.legacy')
43+
->setDeprecated(...$this->buildDeprecationArgs('Using "%service_id%" is deprecated since API Platform 2.7. Use "ApiPlatform\Api\IriConverterInterface" instead.'));
44+
}
45+
}
46+
47+
private function buildDeprecationArgs(string $message): array
48+
{
49+
return method_exists(BaseNode::class, 'getDeprecation')
50+
? ['api-platform/core', '2.7', $message]
51+
: [$message];
52+
}
53+
}

src/Symfony/Bundle/Resources/config/api.xml

+1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@
159159
<argument type="service" id="api_platform.resource_class_resolver" />
160160
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
161161
</service>
162+
<service id="ApiPlatform\Core\Api\IriConverterInterface" alias="api_platform.iri_converter.legacy" />
162163

163164
<service id="api_platform.symfony.iri_converter.skolem" class="ApiPlatform\Symfony\Routing\SkolemIriConverter" public="false">
164165
<argument type="service" id="api_platform.router" />

src/Symfony/Bundle/Resources/config/legacy/api.xml

+3-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@
4545
<argument type="service" id="api_platform.subresource_operation_factory" />
4646
</service>
4747

48-
<service id="ApiPlatform\Core\Api\IriConverterInterface" alias="api_platform.iri_converter.legacy" />
48+
<service id="api_platform.symfony.iri_converter" class="ApiPlatform\Core\Api\LegacyIriConverter" public="false">
49+
<argument type="service" id="api_platform.iri_converter.legacy"/>
50+
</service>
4951

5052
<service id="api_platform.listener.request.add_format" class="ApiPlatform\Symfony\EventListener\AddFormatListener">
5153
<argument type="service" id="api_platform.negotiator" />

src/Symfony/Bundle/Resources/config/legacy/backward_compatibility.xml

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@
1313
<service id="api_platform.cache.metadata.property" alias="api_platform.cache.metadata.property.legacy"/>
1414
<service id="api_platform.cache.metadata.resource" alias="api_platform.cache.metadata.resource.legacy"/>
1515
<service id="api_platform.openapi.factory" alias="api_platform.openapi.factory.legacy" />
16-
<service id="ApiPlatform\Api\IriConverterInterface" alias="api_platform.iri_converter" />
16+
<service id="ApiPlatform\Api\IriConverterInterface" alias="api_platform.symfony.iri_converter" />
1717

18-
1918
<service id="ApiPlatform\Core\Serializer\SerializerContextBuilderInterface" alias="ApiPlatform\Serializer\SerializerContextBuilderInterface" />
2019
<service id="ApiPlatform\Core\Api\ResourceClassResolverInterface" alias="ApiPlatform\Api\ResourceClassResolverInterface" />
2120
<service id="ApiPlatform\Core\Api\UrlGeneratorInterface" alias="ApiPlatform\Api\UrlGeneratorInterface" />

tests/Symfony/Bundle/ApiPlatformBundleTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\AnnotationFilterPass;
1919
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\AuthenticatorManagerPass;
2020
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\DataProviderPass;
21+
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\DeprecateLegacyIriConverterPass;
2122
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\DeprecateMercurePublisherPass;
2223
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\ElasticsearchClientPass;
2324
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\FilterPass;
@@ -52,6 +53,7 @@ public function testBuild()
5253
$containerProphecy->addCompilerPass(Argument::type(MetadataAwareNameConverterPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled();
5354
$containerProphecy->addCompilerPass(Argument::type(TestClientPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled();
5455
$containerProphecy->addCompilerPass(Argument::type(AuthenticatorManagerPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled();
56+
$containerProphecy->addCompilerPass(Argument::type(DeprecateLegacyIriConverterPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled();
5557

5658
$bundle = new ApiPlatformBundle();
5759
$bundle->build($containerProphecy->reveal());

tests/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php

+29
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace ApiPlatform\Tests\Symfony\Bundle\DependencyInjection;
1515

1616
use ApiPlatform\Api\FilterInterface;
17+
use ApiPlatform\Api\IriConverterInterface;
1718
use ApiPlatform\Api\UrlGeneratorInterface;
1819
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
1920
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
@@ -347,6 +348,7 @@ public function testCommonConfigurationWithMetadataBackwardCompatibilityLayer()
347348
'api_platform.route_loader.legacy',
348349
'api_platform.operation_path_resolver.router',
349350
'api_platform.iri_converter.legacy',
351+
'api_platform.symfony.iri_converter',
350352
'api_platform.listener.request.add_format',
351353
'api_platform.listener.request.deserialize',
352354
'api_platform.listener.view.serialize',
@@ -386,6 +388,7 @@ public function testCommonConfigurationWithMetadataBackwardCompatibilityLayer()
386388
'api_platform.metadata.resource.name_collection_factory',
387389
'api_platform.route_loader',
388390
'api_platform.iri_converter',
391+
'ApiPlatform\Api\IriConverterInterface',
389392
'api_platform.identifiers_extractor',
390393
'api_platform.pagination',
391394
'api_platform.cache.metadata.property',
@@ -437,11 +440,13 @@ public function testCommonConfigurationWithoutMetadataBackwardCompatibilityLayer
437440
'api_platform.listener.view.write.legacy',
438441
'api_platform.listener.request.read.legacy',
439442
'api_platform.iri_converter',
443+
'api_platform.iri_converter.legacy',
440444
];
441445

442446
$aliases = [
443447
// v3/api.xml
444448
'ApiPlatform\Api\IriConverterInterface',
449+
'ApiPlatform\Core\Api\IriConverterInterface',
445450
'api_platform.identifiers_extractor',
446451
'ApiPlatform\Api\IdentifiersExtractorInterface',
447452

@@ -1819,6 +1824,30 @@ public function testLegacyServicesMetadataBackwardCompatibilityLayer(): void
18191824
$this->assertNotContainerHasService('api_platform.doctrine_mongodb.odm.metadata.property.identifier_metadata_factory');
18201825
}
18211826

1827+
/**
1828+
* @dataProvider provideIriConverterAliases
1829+
*/
1830+
public function testIriConverterIsCompatibleWithAliasedInterface(bool $metadataBCLayer, string $interface): void
1831+
{
1832+
$config = self::DEFAULT_CONFIG;
1833+
$config['api_platform']['metadata_backward_compatibility_layer'] = $metadataBCLayer;
1834+
(new ApiPlatformExtension())->load($config, $this->container);
1835+
1836+
$this->assertContainerHasAlias($interface);
1837+
1838+
$definition = $this->container->findDefinition($interface);
1839+
$this->assertTrue(is_a($definition->getClass(), $interface, true), "Failed asserting that alias '{$interface}' resolves to a service implementing this interface.");
1840+
}
1841+
1842+
public function provideIriConverterAliases(): \Generator
1843+
{
1844+
yield 'BC layer on, new interface' => [true, IriConverterInterface::class];
1845+
yield 'BC layer on, legacy interface' => [true, \ApiPlatform\Core\Api\IriConverterInterface::class];
1846+
1847+
yield 'BC layer off, new interface' => [false, IriConverterInterface::class];
1848+
yield 'BC layer off, legacy interface' => [false, \ApiPlatform\Core\Api\IriConverterInterface::class];
1849+
}
1850+
18221851
public function testRectorConfiguration(): void
18231852
{
18241853
$config = self::DEFAULT_CONFIG;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Symfony\Bundle\DependencyInjection\Compiler;
15+
16+
use ApiPlatform\Core\Api\IriConverterInterface;
17+
use ApiPlatform\Core\Tests\ProphecyTrait;
18+
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\DeprecateLegacyIriConverterPass;
19+
use PHPUnit\Framework\TestCase;
20+
use Symfony\Component\Config\Definition\BaseNode;
21+
use Symfony\Component\DependencyInjection\Alias;
22+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
23+
use Symfony\Component\DependencyInjection\ContainerBuilder;
24+
use Symfony\Component\DependencyInjection\Definition;
25+
26+
final class DeprecateLegacyIriConverterPassTest extends TestCase
27+
{
28+
use ProphecyTrait;
29+
30+
public function testProcess(): void
31+
{
32+
$deprecateIriConverterPass = new DeprecateLegacyIriConverterPass();
33+
34+
$this->assertInstanceOf(CompilerPassInterface::class, $deprecateIriConverterPass);
35+
36+
$containerBuilderProphecy = $this->prophesize(ContainerBuilder::class);
37+
$aliasProphecy = $this->prophesize(Alias::class);
38+
$definitionProphecy = $this->prophesize(Definition::class);
39+
40+
$containerBuilderProphecy
41+
->hasDefinition(IriConverterInterface::class)
42+
->willReturn(true);
43+
44+
$containerBuilderProphecy
45+
->hasAlias(IriConverterInterface::class)
46+
->willReturn(true);
47+
48+
$containerBuilderProphecy
49+
->hasDefinition('api_platform.iri_converter.legacy')
50+
->willReturn(true);
51+
52+
$containerBuilderProphecy
53+
->getDefinition(IriConverterInterface::class)
54+
->willReturn($definitionProphecy->reveal())
55+
->shouldBeCalled();
56+
57+
$containerBuilderProphecy
58+
->getAlias(IriConverterInterface::class)
59+
->willReturn($aliasProphecy->reveal())
60+
->shouldBeCalled();
61+
62+
$containerBuilderProphecy
63+
->getDefinition('api_platform.iri_converter.legacy')
64+
->willReturn($definitionProphecy->reveal())
65+
->shouldBeCalled();
66+
67+
$setDeprecatedAliasArgs = method_exists(BaseNode::class, 'getDeprecation')
68+
? ['api-platform/core', '2.7', 'Using "%alias_id%" is deprecated since API Platform 2.7. Use "ApiPlatform\Api\IriConverterInterface" instead.']
69+
: ['Using "%alias_id%" is deprecated since API Platform 2.7. Use "ApiPlatform\Api\IriConverterInterface" instead.'];
70+
71+
$setDeprecatedDefinitionArgs = method_exists(BaseNode::class, 'getDeprecation')
72+
? ['api-platform/core', '2.7', 'Using "%service_id%" is deprecated since API Platform 2.7. Use "ApiPlatform\Api\IriConverterInterface" instead.']
73+
: ['Using "%service_id%" is deprecated since API Platform 2.7. Use "ApiPlatform\Api\IriConverterInterface" instead.'];
74+
75+
$aliasProphecy
76+
->setDeprecated(...$setDeprecatedAliasArgs)
77+
->willReturn($aliasProphecy->reveal())
78+
->shouldBeCalled();
79+
80+
$definitionProphecy
81+
->setDeprecated(...$setDeprecatedDefinitionArgs)
82+
->willReturn($definitionProphecy->reveal())
83+
->shouldBeCalled();
84+
85+
$deprecateIriConverterPass->process($containerBuilderProphecy->reveal());
86+
}
87+
88+
public function testProcessWithoutDefinition(): void
89+
{
90+
$deprecateIriConverterPass = new DeprecateLegacyIriConverterPass();
91+
$containerBuilderProphecy = $this->prophesize(ContainerBuilder::class);
92+
93+
$containerBuilderProphecy
94+
->hasDefinition(IriConverterInterface::class)
95+
->willReturn(false);
96+
97+
$containerBuilderProphecy
98+
->hasAlias(IriConverterInterface::class)
99+
->willReturn(false);
100+
101+
$containerBuilderProphecy
102+
->hasDefinition('api_platform.iri_converter.legacy')
103+
->willReturn(false);
104+
105+
$containerBuilderProphecy
106+
->getDefinition(IriConverterInterface::class)
107+
->shouldNotBeCalled();
108+
109+
$containerBuilderProphecy
110+
->getAlias(IriConverterInterface::class)
111+
->shouldNotBeCalled();
112+
113+
$containerBuilderProphecy
114+
->getDefinition('api_platform.iri_converter.legacy')
115+
->shouldNotBeCalled();
116+
117+
$deprecateIriConverterPass->process($containerBuilderProphecy->reveal());
118+
}
119+
}

0 commit comments

Comments
 (0)