Repository: zircote/swagger-php (PHP)

Issue:
Wrong docs generation for php8 promoted properties.
Using php 8 promoted properties, I expect the doccomment to be parsed the same as for the basic class properties. But it actually looks like they are overridden by method arguments. As far as I'm concerned, the only reason I want to use elevated properties is the linter hints (and one more, but it's affected by the class extension).
I tried to investigate on my own but was unsuccessful.
Maybe someone also wants to use PP like me and this could be a nice fix.
In my opinion, the problem is overriding because ReflectionClass->getProperties() returns two values with the correct doc comments.

Code:
```php
#[Schema()]
class NameValue
{
    /**
     * Property name.
     *
     * @var string
     */
    #[Property()]
    public string $name = '';

    public function __construct(
        /**
         * Property value.
         *
         * @var string
         */
        #[Property()]
        public string $value = '',
    ) {
    }
}
```

Expected result:
```json
            "NameValue": {
                "properties": {
                    "value": {
                        "description": "Property value.",
                        "type": "string",
                        "nullable": false
                    },
                    "name": {
                        "description": "Property name.",
                        "type": "string"
                    }
                },
                "type": "object"
            },
```

Actual result:
```json
            "NameValue": {
                "properties": {
                    "value": {
                        "type": "string",
                        "nullable": false
                    },
                    "name": {
                        "description": "Property name.",
                        "type": "string"
                    }
                },
                "type": "object"
            },
```


Discussion: Yep, that could be better.

Also the `"nullable": false` is kinda redundant I suppose...



Fix (patch):
diff --git a/src/Analysers/AttributeAnnotationFactory.php b/src/Analysers/AttributeAnnotationFactory.php
index 31b7f5e53..0bbeed5e7 100644
--- a/src/Analysers/AttributeAnnotationFactory.php
+++ b/src/Analysers/AttributeAnnotationFactory.php
@@ -53,6 +53,7 @@ public function build(\Reflector $reflector, Context $context): array
                 foreach ($reflector->getParameters() as $rp) {
                     foreach ([OA\Property::class, OA\Parameter::class, OA\RequestBody::class] as $attributeName) {
                         foreach ($rp->getAttributes($attributeName, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
+                            /** @var OA\Property|OA\Parameter|OA\RequestBody $instance */
                             $instance = $attribute->newInstance();
                             $type = (($rnt = $rp->getType()) && $rnt instanceof \ReflectionNamedType) ? $rnt->getName() : Generator::UNDEFINED;
                             $nullable = $rnt ? $rnt->allowsNull() : true;
@@ -60,11 +61,20 @@ public function build(\Reflector $reflector, Context $context): array
                             if ($instance instanceof OA\RequestBody) {
                                 $instance->required = !$nullable;
                             } elseif ($instance instanceof OA\Property) {
-                                $instance->property = $rp->getName();
+                                if (Generator::isDefault($instance->property)) {
+                                    $instance->property = $rp->getName();
+                                }
                                 if (Generator::isDefault($instance->type)) {
                                     $instance->type = $type;
                                 }
-                                $instance->nullable = $nullable;
+                                $instance->nullable = $nullable ?: Generator::UNDEFINED;
+
+                                if ($rp->isPromoted()) {
+                                    // promoted parameter - docblock is available via class/property
+                                    if ($comment = $rp->getDeclaringClass()->getProperty($rp->getName())->getDocComment()) {
+                                        $instance->_context->comment = $comment;
+                                    }
+                                }
                             } else {
                                 if (!$instance->name || Generator::isDefault($instance->name)) {
                                     $instance->name = $rp->getName();