From d15547d6d7ae0a38c7fa089fc6359bbd8a4180bd Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Fri, 27 Aug 2021 10:34:37 +1200 Subject: [PATCH] Fix injecting context into attribute contructor --- src/Analysers/AttributeAnnotationFactory.php | 20 +++++++------ src/Analysers/DocBlockParser.php | 19 ++++--------- src/Annotations/AbstractAnnotation.php | 15 ++++++---- src/Annotations/JsonContent.php | 30 +++++++++++++++++++- src/Annotations/Response.php | 4 ++- src/Generator.php | 7 +++++ tests/Fixtures/Apis/Attributes/basic.php | 2 +- 7 files changed, 67 insertions(+), 30 deletions(-) diff --git a/src/Analysers/AttributeAnnotationFactory.php b/src/Analysers/AttributeAnnotationFactory.php index 10ae8d8b2..30fe5c71c 100644 --- a/src/Analysers/AttributeAnnotationFactory.php +++ b/src/Analysers/AttributeAnnotationFactory.php @@ -8,6 +8,7 @@ use OpenApi\Annotations\AbstractAnnotation; use OpenApi\Context; +use OpenApi\Generator; class AttributeAnnotationFactory implements AnnotationFactoryInterface { @@ -17,18 +18,21 @@ public function build(\Reflector $reflector, Context $context): array $context->annotations = []; } + Generator::$context = $context; $annotations = []; - foreach ($reflector->getAttributes() as $attribute) { - $instance = $attribute->newInstance(); - if ($instance instanceof AbstractAnnotation) { - $instance->_context = $context; + try { + foreach ($reflector->getAttributes() as $attribute) { + $instance = $attribute->newInstance(); + if ($instance instanceof AbstractAnnotation) { + $instance->_context = $context; + } + $annotations[] = $instance; } - $annotations[] = $instance; + } finally { + Generator::$context = null; } - $context->annotations = $annotations; - - return array_filter($annotations, function ($a) { + return $context->annotations = array_filter($annotations, function ($a) { return $a !== null; }); } diff --git a/src/Analysers/DocBlockParser.php b/src/Analysers/DocBlockParser.php index 7c2745cc9..e5758dc48 100644 --- a/src/Analysers/DocBlockParser.php +++ b/src/Analysers/DocBlockParser.php @@ -9,6 +9,7 @@ use Doctrine\Common\Annotations\AnnotationRegistry; use Doctrine\Common\Annotations\DocParser; use OpenApi\Context; +use OpenApi\Generator; if (class_exists(AnnotationRegistry::class, true)) { AnnotationRegistry::registerLoader( @@ -24,7 +25,7 @@ function (string $class): bool { if (!$loaded && $namespace === 'OpenApi\\Annotations\\') { if (in_array(strtolower(substr($class, 20)), ['definition', 'path'])) { // Detected an 2.x annotation? - throw new \Exception('The annotation @SWG\\' . substr($class, 20) . '() is deprecated. Found in ' . DocBlockParser::$context . "\nFor more information read the migration guide: https://github.com/zircote/swagger-php/blob/master/docs/Migrating-to-v3.md"); + throw new \Exception('The annotation @SWG\\' . substr($class, 20) . '() is deprecated. Found in ' . Generator::$context . "\nFor more information read the migration guide: https://github.com/zircote/swagger-php/blob/master/docs/Migrating-to-v3.md"); } } @@ -59,13 +60,6 @@ class DocBlockParser */ public static $defaultImports = ['oa' => 'OpenApi\\Annotations']; - /** - * Allows Annotation classes to know the context of the annotation that is being processed. - * - * @var null|Context - */ - public static $context; - /** * @var DocParser */ @@ -94,16 +88,13 @@ public function fromComment(string $comment, Context $context): array $context->comment = $comment; try { - self::$context = $context; + Generator::$context = $context; if ($context->is('annotations') === false) { $context->annotations = []; } - $annotations = $this->docParser->parse($comment); - self::$context = null; - return $annotations; + return $this->docParser->parse($comment); } catch (\Exception $e) { - self::$context = null; if (preg_match('/^(.+) at position ([0-9]+) in ' . preg_quote((string) $context, '/') . '\.$/', $e->getMessage(), $matches)) { $errorMessage = $matches[1]; $errorPos = (int) $matches[2]; @@ -117,6 +108,8 @@ public function fromComment(string $comment, Context $context): array } return []; + } finally { + Generator::$context = null; } } } diff --git a/src/Annotations/AbstractAnnotation.php b/src/Annotations/AbstractAnnotation.php index cf0342e59..aee388589 100644 --- a/src/Annotations/AbstractAnnotation.php +++ b/src/Annotations/AbstractAnnotation.php @@ -6,7 +6,6 @@ namespace OpenApi\Annotations; -use OpenApi\Analysers\DocBlockParser; use OpenApi\Context; use OpenApi\Generator; use OpenApi\Util; @@ -88,8 +87,8 @@ public function __construct(array $properties) if (isset($properties['_context'])) { $this->_context = $properties['_context']; unset($properties['_context']); - } elseif (DocBlockParser::$context) { - $this->_context = DocBlockParser::$context; + } elseif (Generator::$context) { + $this->_context = Generator::$context; } else { $this->_context = Context::detect(1); } @@ -113,7 +112,7 @@ public function __construct(array $properties) } elseif (is_array($value)) { $annotations = []; foreach ($value as $annotation) { - if (is_object($annotation) && $annotation instanceof AbstractAnnotation) { + if ($annotation instanceof AbstractAnnotation) { $annotations[] = $annotation; } else { $this->_context->logger->warning('Unexpected field in ' . $this->identity() . ' in ' . $this->_context); @@ -123,7 +122,7 @@ public function __construct(array $properties) } elseif (is_object($value)) { $this->merge([$value]); } else { - if (!(\PHP_VERSION_ID >= 80100 && $value === Generator::UNDEFINED)) { + if ($value !== Generator::UNDEFINED) { $this->_context->logger->warning('Unexpected parameter "' . $property . '" in ' . $this->identity()); } } @@ -654,8 +653,12 @@ private function validateArrayType($value): bool * * @return AbstractAnnotation */ - private function nested($annotation, Context $nestedContext) + protected function nested($annotation, Context $nestedContext) { + if (!$annotation) { + return $annotation; + } + if (property_exists($annotation, '_context') && $annotation->_context === $this->_context) { $annotation->_context = $nestedContext; } diff --git a/src/Annotations/JsonContent.php b/src/Annotations/JsonContent.php index 739aa7845..5b00ff235 100644 --- a/src/Annotations/JsonContent.php +++ b/src/Annotations/JsonContent.php @@ -15,7 +15,7 @@ * * @Annotation */ -class JsonContent extends Schema +abstract class JsonContentImpl extends Schema { /** @@ -45,3 +45,31 @@ class JsonContent extends Schema Examples::class => ['examples', 'example'], ]; } + +if (\PHP_VERSION_ID >= 80100) { + /** + * @Annotation + */ + class JsonContent extends JsonContentImpl + { + public function __construct( + array $properties = [], + string $ref = Generator::UNDEFINED + ) { + parent::__construct($properties + [ + 'ref' => $ref, + ]); + } + } +} else { + /** + * @Annotation + */ + class JsonContent extends JsonContentImpl + { + public function __construct(array $properties) + { + parent::__construct($properties); + } + } +} diff --git a/src/Annotations/Response.php b/src/Annotations/Response.php index d7699e84a..5aa699d09 100644 --- a/src/Annotations/Response.php +++ b/src/Annotations/Response.php @@ -114,11 +114,13 @@ class Response extends ResponeImpl public function __construct( array $properties = [], $response = Generator::UNDEFINED, - string $description = Generator::UNDEFINED + string $description = Generator::UNDEFINED, + $content = Generator::UNDEFINED ) { parent::__construct($properties + [ 'response' => $response, 'description' => $description, + 'value' => $content !== Generator::UNDEFINED ? [$content] : Generator::UNDEFINED, ]); } } diff --git a/src/Generator.php b/src/Generator.php index 4dc444a39..7e8e86b17 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -25,6 +25,13 @@ */ class Generator { + /** + * Allows Annotation classes to know the context of the annotation that is being processed. + * + * @var null|Context + */ + public static $context; + /** @var string Magic value to differentiate between null and undefined. */ public const UNDEFINED = '@OA\Generator::UNDEFINED🙈'; diff --git a/tests/Fixtures/Apis/Attributes/basic.php b/tests/Fixtures/Apis/Attributes/basic.php index 70173a931..64d2f65cc 100644 --- a/tests/Fixtures/Apis/Attributes/basic.php +++ b/tests/Fixtures/Apis/Attributes/basic.php @@ -43,7 +43,7 @@ class ProductController path: '/products/{product_id}', tags: ['Products'], responses: [ - new OA\Response(response: 200, description: 'successful operation'), + new OA\Response(response: 200, description: 'successful operation', content: new OA\JsonContent(ref: '#/components/schemas/Product')), new OA\Response(response: 401, description: 'oops'), ] )]