diff --git a/src/JsonSchema/DefinitionNameFactory.php b/src/JsonSchema/DefinitionNameFactory.php index 86076def71c..9cfbb7deb07 100644 --- a/src/JsonSchema/DefinitionNameFactory.php +++ b/src/JsonSchema/DefinitionNameFactory.php @@ -21,6 +21,9 @@ final class DefinitionNameFactory implements DefinitionNameFactoryInterface { use ResourceClassInfoTrait; + private const GLUE = '.'; + private array $prefixCache = []; + public function __construct(private ?array $distinctFormats) { } @@ -32,18 +35,18 @@ public function create(string $className, string $format = 'json', ?string $inpu } if (!isset($prefix)) { - $prefix = (new \ReflectionClass($className))->getShortName(); + $prefix = $this->createPrefixFromClass($className); } if (null !== $inputOrOutputClass && $className !== $inputOrOutputClass) { $parts = explode('\\', $inputOrOutputClass); $shortName = end($parts); - $prefix .= '.'.$shortName; + $prefix .= self::GLUE.$shortName; } if ('json' !== $format && ($this->distinctFormats[$format] ?? false)) { // JSON is the default, and so isn't included in the definition name - $prefix .= '.'.$format; + $prefix .= self::GLUE.$format; } $definitionName = $serializerContext[SchemaFactory::OPENAPI_DEFINITION_NAME] ?? null; @@ -61,4 +64,22 @@ private function encodeDefinitionName(string $name): string { return preg_replace('/[^a-zA-Z0-9.\-_]/', '.', $name); } + + private function createPrefixFromClass(string $fullyQualifiedClassName, int $namespaceParts = 1): string + { + $parts = explode('\\', $fullyQualifiedClassName); + $name = implode(self::GLUE, \array_slice($parts, -$namespaceParts)); + + if (!isset($this->prefixCache[$name])) { + $this->prefixCache[$name] = $fullyQualifiedClassName; + + return $name; + } + + if ($this->prefixCache[$name] !== $fullyQualifiedClassName) { + $name = $this->createPrefixFromClass($fullyQualifiedClassName, ++$namespaceParts); + } + + return $name; + } } diff --git a/tests/JsonSchema/DefinitionNameFactoryTest.php b/tests/JsonSchema/DefinitionNameFactoryTest.php index 10444160fe7..519fcfcad42 100644 --- a/tests/JsonSchema/DefinitionNameFactoryTest.php +++ b/tests/JsonSchema/DefinitionNameFactoryTest.php @@ -69,4 +69,29 @@ public function testCreate(string $expected, string $className, string $format = static::assertSame($expected, $definitionNameFactory->create($className, $format, $inputOrOutputClass, $operation, $serializerContext)); } + + public function testCreateDifferentPrefixesForClassesWithTheSameShortName(): void + { + $definitionNameFactory = new DefinitionNameFactory(['jsonapi' => true, 'jsonhal' => true]); + + self::assertEquals( + 'DummyClass.jsonapi', + $definitionNameFactory->create(\ApiPlatform\Tests\JsonSchema\Dummy\NamespaceA\Module\DummyClass::class, 'jsonapi') + ); + + self::assertEquals( + 'Module.DummyClass.jsonapi', + $definitionNameFactory->create(\ApiPlatform\Tests\JsonSchema\Dummy\NamespaceB\Module\DummyClass::class, 'jsonapi') + ); + + self::assertEquals( + 'NamespaceC.Module.DummyClass.jsonapi', + $definitionNameFactory->create(\ApiPlatform\Tests\JsonSchema\Dummy\NamespaceC\Module\DummyClass::class, 'jsonapi') + ); + + self::assertEquals( + 'DummyClass.jsonhal', + $definitionNameFactory->create(\ApiPlatform\Tests\JsonSchema\Dummy\NamespaceA\Module\DummyClass::class, 'jsonhal') + ); + } } diff --git a/tests/JsonSchema/Dummy/NamespaceA/Module/DummyClass.php b/tests/JsonSchema/Dummy/NamespaceA/Module/DummyClass.php new file mode 100644 index 00000000000..6ea133b25ef --- /dev/null +++ b/tests/JsonSchema/Dummy/NamespaceA/Module/DummyClass.php @@ -0,0 +1,18 @@ + + * + * 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\JsonSchema\Dummy\NamespaceA\Module; + +class DummyClass +{ +} diff --git a/tests/JsonSchema/Dummy/NamespaceB/Module/DummyClass.php b/tests/JsonSchema/Dummy/NamespaceB/Module/DummyClass.php new file mode 100644 index 00000000000..5fbe6f30d10 --- /dev/null +++ b/tests/JsonSchema/Dummy/NamespaceB/Module/DummyClass.php @@ -0,0 +1,18 @@ + + * + * 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\JsonSchema\Dummy\NamespaceB\Module; + +class DummyClass +{ +} diff --git a/tests/JsonSchema/Dummy/NamespaceC/Module/DummyClass.php b/tests/JsonSchema/Dummy/NamespaceC/Module/DummyClass.php new file mode 100644 index 00000000000..f5b4449ac80 --- /dev/null +++ b/tests/JsonSchema/Dummy/NamespaceC/Module/DummyClass.php @@ -0,0 +1,18 @@ + + * + * 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\JsonSchema\Dummy\NamespaceC\Module; + +class DummyClass +{ +}