diff --git a/Describer/OpenApiPhpDescriber.php b/Describer/OpenApiPhpDescriber.php index 1e303e7..645c11d 100644 --- a/Describer/OpenApiPhpDescriber.php +++ b/Describer/OpenApiPhpDescriber.php @@ -17,6 +17,7 @@ use Nelmio\ApiDocBundle\Annotation\Security; use Nelmio\ApiDocBundle\OpenApiPhp\Util; use Nelmio\ApiDocBundle\Util\ControllerReflector; use Nelmio\ApiDocBundle\Util\SetsContextTrait; +use OpenApi\Analysers\AttributeAnnotationFactory; use OpenApi\Annotations as OA; use OpenApi\Generator; use Psr\Log\LoggerInterface; @@ -67,14 +68,14 @@ final class OpenApiPhpDescriber $classAnnotations = array_filter($this->annotationReader->getClassAnnotations($declaringClass), function ($v) { return $v instanceof OA\AbstractAnnotation; }); - $classAnnotations = array_merge($classAnnotations, $this->getAttributesAsAnnotation($declaringClass, OA\AbstractAnnotation::class)); + $classAnnotations = array_merge($classAnnotations, $this->getAttributesAsAnnotation($declaringClass, $context)); $classAnnotations[$declaringClass->getName()] = $classAnnotations; } $annotations = array_filter($this->annotationReader->getMethodAnnotations($method), function ($v) { return $v instanceof OA\AbstractAnnotation; }); - $annotations = array_merge($annotations, $this->getAttributesAsAnnotation($method, OA\AbstractAnnotation::class)); + $annotations = array_merge($annotations, $this->getAttributesAsAnnotation($method, $context)); if (0 === count($annotations) && 0 === count($classAnnotations[$declaringClass->getName()])) { continue; @@ -205,17 +206,18 @@ final class OpenApiPhpDescriber * * @return OA\AbstractAnnotation[] */ - private function getAttributesAsAnnotation($reflection, string $className): array + private function getAttributesAsAnnotation($reflection, \OpenApi\Context $context): array { - $annotations = []; - if (\PHP_VERSION_ID < 80100) { - return $annotations; + // BC zircote/swagger-php < 4.0 + if (!class_exists(AttributeAnnotationFactory::class)) { + return []; } - foreach ($reflection->getAttributes($className, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) { - $annotations[] = $attribute->newInstance(); - } + $attributesFactory = new AttributeAnnotationFactory(); + $attributes = $attributesFactory->build($reflection, $context); + // The attributes factory removes the context after executing so we need to set it back... + $this->setContext($context); - return $annotations; + return $attributes; } } diff --git a/Tests/Functional/Controller/ApiController81.php b/Tests/Functional/Controller/ApiController81.php index d4195f5..71f3629 100644 --- a/Tests/Functional/Controller/ApiController81.php +++ b/Tests/Functional/Controller/ApiController81.php @@ -58,4 +58,11 @@ class ApiController81 extends ApiController80 public function securityOverrideActionAttributes() { } + + #[Route('/inline_path_parameters')] + #[OA\Response(response: '200', description: '')] + public function inlinePathParameters( + #[OA\PathParameter] string $product_id + ) { + } } diff --git a/Tests/Functional/FunctionalTest.php b/Tests/Functional/FunctionalTest.php index e70cbb2..159ae77 100644 --- a/Tests/Functional/FunctionalTest.php +++ b/Tests/Functional/FunctionalTest.php @@ -387,6 +387,19 @@ class FunctionalTest extends WebTestCase } } + public function testInlinePHP81Parameters() + { + if (\PHP_VERSION_ID < 80100) { + $this->markTestSkipped('Attributes require PHP 8.1'); + } + + $operation = $this->getOperation('/api/inline_path_parameters', 'get'); + $this->assertCount(1, $operation->parameters); + $this->assertInstanceOf(OA\PathParameter::class, $operation->parameters[0]); + $this->assertSame($operation->parameters[0]->name, 'product_id'); + $this->assertSame($operation->parameters[0]->schema->type, 'string'); + } + public function testClassSecurityAction() { $operation = $this->getOperation('/api/security/class', 'get');