diff --git a/src/Symfony/Routing/SkolemIriConverter.php b/src/Symfony/Routing/SkolemIriConverter.php index e62d074cad2..4912a424292 100644 --- a/src/Symfony/Routing/SkolemIriConverter.php +++ b/src/Symfony/Routing/SkolemIriConverter.php @@ -18,13 +18,14 @@ use ApiPlatform\Metadata\Operation; use ApiPlatform\Metadata\UrlGeneratorInterface; use Symfony\Component\Routing\RouterInterface; +use Symfony\Contracts\Service\ResetInterface; /** * {@inheritdoc} * * @author Antoine Bluchet */ -final class SkolemIriConverter implements IriConverterInterface +final class SkolemIriConverter implements IriConverterInterface, ResetInterface { public static string $skolemUriTemplate = '/.well-known/genid/{id}'; @@ -70,4 +71,10 @@ public function getIriFromResource(object|string $resource, int $referenceType = return $this->router->generate('api_genid', ['id' => $id], $referenceType); } + + public function reset(): void + { + $this->objectHashMap = new \SplObjectStorage(); + $this->classHashMap = []; + } } diff --git a/tests/Symfony/Routing/SkolemIriConverterTest.php b/tests/Symfony/Routing/SkolemIriConverterTest.php new file mode 100644 index 00000000000..a2cf2bed0f1 --- /dev/null +++ b/tests/Symfony/Routing/SkolemIriConverterTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Symfony\Routing; + +use ApiPlatform\Symfony\Routing\SkolemIriConverter; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Contracts\Service\ResetInterface; + +class SkolemIriConverterTest extends TestCase +{ + public function testImplementsResetInterface(): void + { + $router = $this->createStub(RouterInterface::class); + $converter = new SkolemIriConverter($router); + + $this->assertInstanceOf(ResetInterface::class, $converter); + } + + public function testResetClearsObjectHashMap(): void + { + $generatedIds = []; + $router = $this->createStub(RouterInterface::class); + $router->method('generate')->willReturnCallback(static function (string $name, array $params) use (&$generatedIds) { + $generatedIds[] = $params['id']; + + return '/.well-known/genid/'.$params['id']; + }); + + $converter = new SkolemIriConverter($router); + + $resource = new \stdClass(); + $converter->getIriFromResource($resource); + $firstId = $generatedIds[0]; + + $converter->getIriFromResource($resource); + $this->assertSame($firstId, $generatedIds[1]); + + $converter->reset(); + + $converter->getIriFromResource($resource); + $this->assertNotSame($firstId, $generatedIds[2]); + } + + public function testResetClearsClassHashMap(): void + { + $generatedIds = []; + $router = $this->createStub(RouterInterface::class); + $router->method('generate')->willReturnCallback(static function (string $name, array $params) use (&$generatedIds) { + $generatedIds[] = $params['id']; + + return '/.well-known/genid/'.$params['id']; + }); + + $converter = new SkolemIriConverter($router); + + $converter->getIriFromResource(\stdClass::class); + $firstId = $generatedIds[0]; + + $converter->getIriFromResource(\stdClass::class); + $this->assertSame($firstId, $generatedIds[1]); + + $converter->reset(); + + $converter->getIriFromResource(\stdClass::class); + $this->assertNotSame($firstId, $generatedIds[2]); + } + + public function testResetAllowsConverterToBeReused(): void + { + $generatedIds = []; + $router = $this->createStub(RouterInterface::class); + $router->method('generate')->willReturnCallback(static function (string $name, array $params) use (&$generatedIds) { + $generatedIds[] = $params['id']; + + return '/.well-known/genid/'.$params['id']; + }); + + $converter = new SkolemIriConverter($router); + + // Simulate multiple request cycles + for ($i = 0; $i < 3; ++$i) { + $resource = new \stdClass(); + $converter->getIriFromResource($resource); + $converter->getIriFromResource(\stdClass::class); + $converter->reset(); + } + + // Each cycle should generate 2 new IDs (object + class), total 6 + $this->assertCount(6, $generatedIds); + // All IDs should be unique (no stale cache) + $this->assertCount(6, array_unique($generatedIds)); + } +}